函数exec
简介
fork函数在创建子进程后,往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。
因为调用exec并不创建新进程,所以前后的进程ID并未改变。
exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段。
exec函数
有7种不同的exec函数可供使用,它们常常被统称为exec函数。这些exec函数使得UNIX进程控制原语更加完善。
用fork可以创建新进程;
用exec可以执行新程序;
exit函数和两个wait函数处理终止和等待终止;
这些是我们需要的基本的进程控制原语。
7种不同的exec函数
#include <unistd.h>
int execl(const char *pathname, const char *arg0, ..., (char *)0);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ..., (char *)0, char *const envp[]);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ..., (char *)0);
int execvp(const char *filename, char *const argv[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
// Linux
int execvpe(const char *file, char *const argv[], char *const envp[]);
区别
(1)参数
第一个区别是前4个取路径名作为参数,后两个则取文件名作为参数,最后一个取文件描述符作为参数。
当指定filename作为参数时:
- 如果filename中包含了/,则将其视为路径名。
- 否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件。
PATH变量包含了一张目录表(称为路径前缀),目录之间用冒号(:)分隔。例如name = value环境字符串指定在4个目录中进行搜索。
PATH=/bin:/usr/bin:/usr/local/bin/:.
最后的路径前缀(.)表示当前目录。(零长前缀也表示当前目录。在value的开始处可用:表示,在行中间则要用::表示,在行尾则以:表示。)
如果execlp或execvp使用路径名前缀中的一个找到了一个可执行文件,但是该文件不是由连接编辑器产生的机器可执行文件,则认为该文件是一个shell脚本,于是试着调用/bin/sh,并以该filename作为shell的输入。
(2)参数表的传递
第二个区别与参数表的传递有关(l表示list,v表示矢量vector)。函数execl、execlp和execle要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾。对于另外三个函数(execv、execvp、execve和fexecve),则应先构造一个指向各参数的指针数组,然后将该数组地址作为这三个函数的参数。
在ISO C原型之前,对execl、execle和execlp这三个函数表示命令行参数的一般方法是:
char *arg0, char *arg1, …, char *argn, (char *)0
应当特别指出的是:在最后一个命令行参数之后跟了一个空指针。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整型参数。如果一个整型数的长度与char *的长度不同,那么exec函数的实际参数就将出错。
(3) 向新程序传递环境表
最后一个区别与向新程序传递环境表相关。以e结尾的三个函数(execle、execve和fexecve)可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的environ变量为新程序复制现有的环境。通常,一个进程允许将其环境传播给其子进程,但有时也有这种情况,即进程想要为子进程指定某一个确定的环境。
在使用ISO C原型之前,execle的参数是:
char *pathname, char *arg0, …, char *argn, (char *)0, char *envp[]
从中可见,最后一个参数是指向环境字符串的各字符指针构成的数组的地址。而ISO C原型中,所有命令行参数、空指针和envp指针都用省略号(…)表示。
这6个exec函数名中的字符说明:字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件。字母l表示该函数取一个参数表,它与字母v互斥。字母v表示该函数取一个argv[]矢量。字母e表示该函数取envp[]数组,而不使用当前环境。
7个函数区别表
执行exec后变化
在执行exec后,进程ID没有改变。除此之外,执行新程序的进程还保持了原进程的下列特征:
进程ID和父进程ID。
实际用户ID和实际组ID。
附加组ID。
进程组ID。
会话ID。
控制终端。
闹钟尚余留的时间。
当前工作目录。
根目录。
文件模式创建屏蔽字。
文件锁。
进程信号屏蔽。
未处理信号。
资源限制。
tms_utime、tms_stime、tms_cutime以及tms_cstime值。
对打开文件的处理与每个描述符的执行时关闭(close-on-exec)标志值有关。进程中每个打开描述符都有一个执行时关闭标志。若此标志设置,则在执行exec时关闭该描述符,否则该描述符仍打开。除非特地用fcntl设置了该标志,否则系统默认操作是在执行exec后仍保持这种描述符打开。
==POSIX.1明确要求在执行exec时关闭打开的目录流。==这通常是opendir函数实现的,它调用fcntl函数为对应于打开目录流的描述符设置执行时关闭标志。
注意,在执行exec前后实际用户ID和实际组ID保持不变,而有效ID是否改变则取决于所执行程序文件的设置用户ID位和设置组ID位是否设置。如果新程序的设置用户ID位已设置,则有效用户ID变成程序文件所有者的ID,否则有效用户ID不变。对组ID的处理方式与此相同。
7个exec函数之间的关系
在很多UNIX实现中,这7个函数中只有execve是内核的系统调用。另外6个只是库函数,它们最终都要调用该系统调用。
上一篇: 【第十一周】numpy作业