exec函数

exec函数

当一个进程调用某个exec函数(execl, execlp, execle, execv, execvp, execve)时,进程会被新程序完全替换,然后新程序开始在它的main函数处执行。PID并不会改变,因为新进程不是被创建出来的;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 []);

All six return: 1 on error, no return on success

前面四个函数使用路径名做参数,而最后两个使用文件名。当使用文件名时要注意:

  • 如果文件名中包含斜线“/”,那它是一个路径名
  • 另外,可执行文件的搜索路径由PATH环境变量指定。

PATH变量包含一个目录列表,被称作做路径前缀,每个目录间用冒号分隔。例如:name=value环境变量字符串

PATH=/bin:/usr/bin:/usr/local/bin/:.

指定了4个目录,最后一个指定当前目录(一个零长度前缀也意味着当前目录,这个值可以由一个冒号在value的最开始、一行中两个冒号或者在value的最后一个冒号指定。)。

如果execlpexecvp在路径前缀的目录中找一个可执行文件,但是这个文件被连接器发现它不是可执行的(isn’t a machine executable that was generated by the link editor),那么函数假定这个文件是一个shell脚本,并尝试使用/bin/sh打开该文件。

另一个不同:是传输的参数列表(l代表list,v代表vector)。execl, execlpexecle函数它们为新程序指定的每一个命令行参数都是分开的参数,命令行参数结尾以null指针结尾。对于execv, execvpexecve传递给新程序的参数都存储在一个指针数组(array of pointer)中。

下面是execl, execlp和execle的参数列表实例:

char *arg0, char *arg1, ..., char *argn, (char *)0

最后的空指针如果要用常量0的话,必须进行显示的强制类型转换,如果不这么做,它会被解释成整型,如果系统中的整型和char*的大小不一样,那么exec函数收到这个参数会出错。

最后一个不同:传送环境变量列表到新程序中。两个结尾代e的函数(execle, execve)允许我们传送一个含有环境字符串的指针数组(array of pointer)给新程序。其它四个函数在调用进程中使用environ变量复制当前环境变量给新程序。(如果系统支持setenvputenv,那我们可以改变当前的环境变量和子进程的环境变量,但是不能改变父进程的)一般一个进程的环境变量会传给他的子进程,但是在一些案例中,进程想为子进程指定某些环境变量。

六个exec函数不方便记忆。通过几个字母来区分它们的用处:

字母p:意味着函数使用filename参数并使用PATH环境变量查找可执行文件。

字母l:意味着使用参数列表,它和字母v是互斥的。

字母v:意味着参数列表使用数组向量。

字母e:意味着使用数组向量代替使用当前环境变量。

Function

pathname

filename

Arg list

argv[]

environ

envp[]

execl

execlp

execle

execv

execvp

execve

(letter in name)

p

l

v

e

不同的系统对参数列表和环境变量列表的长度都有限制。这个限制是通过ARG_MAX定义的。在POSIX.1上,这个值的长度至少是4096字节。

为了绕开参数长度的限制,我们可以使用xargs命令拆开长参数列表。

使用exec函数可以继承到这些属性:

  • PID和父进程的PID
  • 实际的用户ID和实际的组ID
  • 附加组ID
  • 进程组ID
  • 会话ID
  • 控制着的终端
  • Time left until alarm clock
  • 当前的工作目录
  • 根目录
  • 创建文件的掩码
  • 文件锁
  • 进程信号掩码(Process signal mask)
  • 待处理信号(pending signal)
  • 资源限制(resource limits)
  • tms_utime, tms_stime, tms_cutimetms_cstime的值

打开文件的处理依赖于每个文件描述符的close-on-exec标记的值。如果设置了这个标记,经过exec后会关闭文件描述符,否则是一直打开的。默认是一直打开的,除非使用fcntl设置close-on-exec标记。

POSIX.1指定通过exec时要关闭打开的文件夹流。这通常由opendir函数调用fcntl设置close-on-exec标记完成。

注意使用exec时,实际用户ID和实际组ID被保存下来了,但是有效ID的改变要根据被执行程序的SUID和SGID位的状态。如果新程序设置了SUID,那么有效用户ID会变成程序所有者的ID。否则有效ID 不能改变。有效组ID也是同样的方法。(SUID,当运行该文件时,进程的有效用户ID变成文件所有者

在很多UNIX系统实现中,只有execve是系统调用,其它五个程序都仅仅是库函数,它们最终调用这个系统调用。下面图解6个函数的关系:

6个exec函数的关系
6个exec函数的关系

在这个图中库函数execlpexecvp处理PATH环境变量,查找第一个包函filename的可执行文件的目录前缀。

 

Comments are closed.