Linux(编程-进程基础):11:---exec函数

一、什么是exec函数

  • 用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序
  • 当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行
  • 因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了 当前进程的正文、数据、堆和栈段

二、exec函数

  • 有7种不同的exec函数可供使用,它们常常被统称为exec函数,我们可以使用这些中的其中一个
  • 这些exec函数使得UNIX进程控制原语更加完善。用fork可以创建新进程,用exec可以执行新的程序
  • exit函数和两个wait函数处理终止和等待终止。这些是我们需要的基本的进程控制原语。在后面各节中将使用这些原语构造另外一些如popen和system之类的函数
#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[]);

//返回值:出错返回-1;成功不返回
//参数:前四个取路径名作为参数,后两个则取文件名作为参数,最后一个取文件描述符作为参数

当指定filename作为参数时:

  • 如果filename中包含/,则就将其视为路径名
  • 否则就按PATH环境变量,在有关目录中搜寻可执行文件(有很多出于安全性方面的考虑,要求在搜索路径中决不要包括当前目录

这7个exec函数的参数很难记忆。函数名中的字符会给我们一些帮助:

  • 字母p表示该函数取filename作为参数,并且用PATH环境变量寻找可执行文件。
  • 字母l表示该函数取一个参数表,它与字母v互斥。
  • v表示该函数取一个argv[]矢量。
  • 母e表示该函数取envp[]数组,而不使用当前环境

  • 在很多UNIX实现中,这7个函数中只有一个execve是内核的系统调用。另外6个只是库函数,它们最终都要调用系统调用。这7个函数之间的关系如图:

三、不同exec函数的区别

execlp、execvp:

  • 如果execlp和execvp中的任意一个使用路径前缀中的一个找到了一个可执行文件,但是该文件不是由连接编辑程序产生的机器可执行代码文件,则就认为该文件是一个shell脚本,于是试着调用/bin/sh,并以该filename作为shell的输入

第二个区别与参数表的传递有关(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函数实际参数就将出错

最后一个区别与向新程序传递环境表相关:

  • 以 e结尾的3个函数(execle和execve、fexecve) 可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的environ变量为新程序复制现存的环境。(回忆7 . 9节及表7-2中对环境字符串的讨论。其中曾提及如果系统支持setenv和putenv这样的函数,则可更改当前环境和后面生成的子进程的环境,但不能影响父进程的环境)
  • 通常,一个进程允许将其环境传播给其子进程, 但有时也有这种情况,进程想要为子进程指定一个确定的环境。例如,在初始化一个新登录的shell时,login程序创建一个只定义少数几个变量的特殊环境,而在我们登录时, 可以通过shell启动文件,将其他变量加到环境中

在使用ISO C 原型之前, execle的参数是:

char *pathname, char *arg0, ..., char *argn, (char *)0, char *envp[]
  • 从中可见,最后一个参数是指向环境字符串的各字符指针构成的数组的指针。而在ISO C原型中,所有命令行参数,包括空指针,envp指针都用省略号(…)表示

四、参数表和环境表的限制

1.概念

  • 每个系统对参数表和环境表的总长度都有一个限制

2.案例

  • 在表2-7中,这种限制是ARG_MAX。 在POSIX.1系统中,此值至少是4096字节。当使用shell的文件名扩充功能产生一个文件名表时, 可能会受到此值的限制。

例如,命令:

grep getrlimit /usr/share/man/*/*

在某些系统上可能产生下列形式的shell错误:Argument list too long

备注:由于历史原因,System V中此限制是5120字节。早期BSD系统的此限制是20480字节

3.xargs命令

  • 为了摆脱对参数表长度的限制。我们可以使用xargs命令,将长参数表断分为几部分。为了寻找在我们所用系统手册页中的getrlimit,我们可以使用:find /usr/share/man -type f -print | xargs grep getrlimit
  • 如果所用的系统手册是压缩过的,则可使用:find /usr/share/man -type f -print | xargs bzgrep getrlimit

对于find命令,我们使用-type f,以限制输出列表质只包含普通文件。这样的原因是:

  • grep命令不能在目录中进行模式搜索,我们也想避免不必要的出错消息

五、新程序的特点

在执行exec后,进程ID没有改变。但新程序从调用进程继承了的下列属性:

  • 进程ID和父进程ID
  • 实际用户ID和实际组ID
  • 附属组ID
  • 进程组ID
  • 对话ID
  • 控制终端
  • 闹钟尚余留的时间
  • 当前工作目录
  • 根目录。
  • 文件模式创建屏蔽字
  • 文件锁
  • 进程信号屏蔽
  • 未处理信号
  • 资源限制
  • nice值(遵循XSI的系统)
  • tms_utime, tms_stime, tms_cutime以及tms_ustime值

六、执行时关闭(close-on-exec)

  • 对打开文件的处理与每个描述符的执行时关闭(close-on-exec)标志值有关
  • 进程中每个打开描述符都有一个执行时关闭标志。若设置了此标志,则在执行exec时关闭该描述符,否则该描述符仍打开。除非特地用fcntl设置了该标志,否则系统的默认操作是在exec后仍保持这种描述符打开
  • POSIX.1明确要求在exec时关闭打开目录流(见4.21节中所述的opendir函数)。这通常是由opendir函数实现的,它调用fcntl函数为对应于打开目录流的描述符设置exec关闭标志

七、用户ID与实际组ID

  • 注意,在exec前后实际用户ID和实际组ID保持不变
  • 而有效ID是否改变则取决于所执行程序的文件的设置用户ID位和设置组ID位是否设置
  • 如果新程序的设置用户ID位已设置,则有效用户ID变成程序文件所有者的ID,否则有效用户ID不变。对组ID的处理方式与此相同

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/89006116
今日推荐