打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
exec函数

fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

其实有六种以exec开头的函数,统称exec函数:

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

关于char *const的用法,有如下原文说明:

The  const  char *arg and subsequent ellipses in the execl(), execlp(), and execle() functions can be thought of as arg0, arg1, ..., argn.  Together they describe a list of one or more pointers to  null-terminated  strings  that  represent  the argument list available to the executed program.  The first argument, by convention, should point to the filename associated with the file being  executed.   The list of arguments must be terminated by a NULL pointer, and, since these are variadic functions, this pointer must be cast (char *) NULL.

这些函数原型看起来很容易混,但只要掌握了规律就很好记。不带字母p(表示path)的exec函数第一个参数必须是程序的相对路径或绝对路径,例如"/bin/ls"或"./a.out",而不能是"ls"或"a.out"。对于带字母p的函数:

  • 如果参数中包含/,则将其视为路径名。

  • 否则视为不带路径的程序名,在PATH环境变量的目录列表中搜索这个程序。

带有字母l(表示list)的exec函数要求将新程序的每个命令行参数都当作一个参数传给它,命令行参数的个数是可变的,因此函数原型中有...,中的最后一个可变参数应该是NULL,起sentinel的作用。对于带有字母v(表示vector)的函数,则应该先构造一个指向各参数的指针数组,然后将该数组的首地址当作参数传给它,数组中的最后一个指针也应该是NULL,就像main数的argv参数或者环境变量表一样。

对于以e(表示environment)结尾的exec函数,可以把一份新的环境变量表传给它,其他exec函数仍使用当前的环境变量表执行新程序。

exec调用举例如下:

char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);

事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

图 30.5. exec函数族


一个完整的例子:

#include <unistd.h>#include <stdlib.h>int main(void){execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);perror("exec ps");exit(1);}

执行此程序则得到:

$ ./a.outPID  PPID  PGRP  SESS TPGID COMMAND6614  6608  6614  6614  7199 bash7199  6614  7199  6614  7199 ps

由于exec函数只有错误返回值,只要返回了一定是出错了,所以不需要判断它的返回值,直接在后面调用error即可。注意在调用execlp时传了两个"ps"参数,第一个"ps"是程序名,execlp函数要在DE>PATHDE>环境变量中找到这个程序并执行它,而第二个"ps"是第一个命令行参数,execlp函数并不关心它的值,只是简单地把它传给ps程序,ps程序可以通过main函数的argv[0]取到这个参数。

调用exec后,原来打开的文件描述符仍然是打开的[37]。利用这一点可以实现I/O重定向。先看一个简单的例子,把标准输入转成大写然后打印到标准输出:

例 30.4. upper

/* upper.c */#include <ctype.h>#include <stdio.h>int main(void){int ch;while((ch = getchar()) != EOF) {putchar(toupper(ch));}return 0;}

运行结果如下:

$ ./upperhello THEREHELLO THERE(按Ctrl-D表示EOF)$

使用Shell重定向:

$ cat file.txtthis is the file, file.txt, it is all lower case.$ ./upper < file.txtTHIS IS THE FILE, FILE.TXT, IT IS ALL LOWER CASE.

如果希望把待转换的文件名放在命令行参数中,而不是借助于输入重定向,我们可以利用upper程序的现有功能,再写一个包装程序wrapper。

例 30.5. wrapper

/* wrapper.c */#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <fcntl.h>int main(int argc, char *argv[]){int fd;if (argc != 2) {fputs("usage: wrapper file\n", stderr);exit(1);}fd = open(argv[1], O_RDONLY);if(fd<0) {perror("open");exit(1);}dup2(fd, STDIN_FILENO);close(fd);execl("./upper", "upper", NULL);perror("exec ./upper");exit(1);}

wrapper程序将命令行参数当作文件名打开,将标准输入重定向到这个文件,然后调用exec执行upper程序,这时原来打开的文件描述符仍然是打开的,upper程序只负责从标准输入读入字符转成大写,并不关心标准输入对应的是文件还是终端。运行结果如下:

$ ./wrapper file.txtTHIS IS THE FILE, FILE.TXT, IT IS ALL LOWER CASE.
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
exec函数族 - Linux操作系统学习 - 老汉梦想家园
Linux系统进程控制编程——exec函数族的使用
Variable Length Arguments
Qt程序执行分析
网易博客欢迎您
Linux c编程 exec execl
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服