在进程的创建上 UNIX 采用了一个独特的方法,它将进程创建与加载一个新进程映象分离,这样可以方便对两种操作进行管理。当创建了一个进程之后,通常可以用 exec 系列的函数将子进程替换成新的进程映象。当然,exec 系列的函数也可以将当前进程替换掉。
fork 函数创建一个子进程时,几乎复制了父进程的全部内容。exec 序列函数可以在一个在进程中启动另一个程序的执行,它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程 ID 未改变( exec 序列函数不创建新进程),其他全部被新的程序替换了。
在 Linux 中使用 exec 序列函数主要有两种情况:
/* exec 序列函数 */ /* * 函数功能:把当前进程替换为一个新的进程,新进程与原进程ID相同; * 返回值:若出错则返回-1,若成功则不返回; * 函数原型: */ #include <unistd.h> int execl(const char *pathname, const char *arg, ...); int execv(const char *pathnam, char *const argv[]); int execle(const char *pathname, const char *arg, ... , char *const envp[]); int execve(const char *pathnam, char *const argv[], char *const envp[]); int execlp(const char *filename, const char *arg, ...); int execvp(const char *filename, char *const argv[]);这6个函数在函数名和使用语法的规则上都有细微的区别,下面就从可执行文件查找方式、参数传递方式及环境变量这几个方面进行比较。
表 1 exec 序列函数的总结 前4位 统一为:exec 第5位 l:参数传递为逐个列举方式 execl、execle、execlp v:参数传递为构造指针数组方式 execv、execve、execvp 第6位 e:可传递新进程环境变量 execle、execve p:可执行文件查找方式为文件名 execlp、execvp事实上,这6个函数中真正的系统调用只有 execve(),其他5个都是 C 库函数,它们最终都会调用 execve() 这个系统调用。在使用 exec 序列函数时,一定要加上错误判断语句。exec 很容易执行失败,其中最常见的原因有:
/* 执行exec函数,下面属性是不发生变化的: *进程ID和父进程ID(pid, ppid) *实际用户ID和实际组ID(ruid, rgid) *附加组ID(sgid) *会话ID *控制终端 *闹钟余留时间 *当前工作目录 *根目录 *umask *文件锁 *进程信号屏蔽 *未处理信号 *资源限制 *进程时间 */ /*而下面属性是发生变化的: *文件描述符如果存在close-on-exec标记的话,那么打开的文件描述符会被关闭。 *如果可执行程序文件存在SUID和SGID位的话,那么有效用户ID和组ID(euid, egid)会发生变化 */exec 序列函数的关系如下图所示:
#include "apue.h" #include <unistd.h> #include <sys/wait.h> char *argv[] = {"echo", "STATUS=testing", "\tHellow linux","\tgood bye",NULL}; char *argve[] = {"env",NULL}; char *env[] = {"USER=unknown", "PATH=/tmp",NULL}; int main() { pid_t pid; if((pid = fork()) < 0) /* fork error */ err_sys("fork error."); else if(0 == pid) /* in child process */ { if(execl("/bin/ls","ls","-a", "-l", (char *)0) == -1) err_sys("execl error.\n"); // exit(0); } if(waitpid(pid, NULL, 0) < 0) err_sys("wait error."); if((pid = fork()) < 0) /* fork error */ err_sys("fork error."); else if(0 == pid) /* in child process */ { if(execlp("ls","ls","-a", "-l", (char *)0) == -1) err_sys("execlp error.\n"); // exit(0); } if(waitpid(pid, NULL, 0) < 0) err_sys("wait error."); if((pid = fork()) < 0) /* fork error */ err_sys("fork error."); else if(0 == pid) /* in child process */ { if(execvp("echo", argv) == -1) err_sys("execvp error.\n"); } if(waitpid(pid, NULL, 0) < 0) err_sys("wait error."); if((pid = fork()) < 0) /* fork error */ err_sys("fork error."); else if(0 == pid) /* in child process */ { if(execve("/usr/bin/env", argve, env) == -1) err_sys("execve error.\n"); } exit(0); }
total 24 drwxrwxr-x 2 nifengweijifen nifengweijifen 4096 Nov 7 15:07 . drwxrwxr-x 3 nifengweijifen nifengweijifen 4096 Nov 7 15:07 .. -rwxrwxr-x 1 nifengweijifen nifengweijifen 12348 Nov 7 15:07 exec -rw-rw-r-- 1 nifengweijifen nifengweijifen 0 Nov 7 14:43 exec.c -rw-rw-r-- 1 nifengweijifen nifengweijifen 0 Nov 7 14:43 exectest.c total 24 drwxrwxr-x 2 nifengweijifen nifengweijifen 4096 Nov 7 15:07 . drwxrwxr-x 3 nifengweijifen nifengweijifen 4096 Nov 7 15:07 .. -rwxrwxr-x 1 nifengweijifen nifengweijifen 12348 Nov 7 15:07 exec -rw-rw-r-- 1 nifengweijifen nifengweijifen 0 Nov 7 14:43 exec.c -rw-rw-r-- 1 nifengweijifen nifengweijifen 0 Nov 7 14:43 exectest.c STATUS=testing Hellow linux good bye USER=unknown PATH=/tmp