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

有6种不同的exec函数可供使用,它们常常被统称为exec函数。这些exec函数使得UNIX进程控制原语更加完善。用fork可以创建新进程,用exec可以执行新程序。exit函数和两个wait函数处理终止和等待终止。这些是我们需要的基本的进程控制原语。

#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[] );

6个函数返回值:若出错则返回-1,若成功则不返回值

这些函数之间的第一个区别是前4个取路径名作为参数,后两个(execlp和execvp,p表示PATH,个人理解)则取文件名作为参数当指定filename作为参数时

  • 如果filename中包含了/,则将其视为路径名。
  • 否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件。

PATH变量包含了一张目录表(称为路径前缀),目录之间用冒号(:)分隔。例如name = value环境字符串:

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

指定在4个目录中进行搜索。最后的路径前缀(.)表示当前目录。(零长前缀也表示当前目录。在value的开始处可用:表示,在行中间则要用::表示,在行尾则以:表示。)

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

第二个区别与参数表的传递有关(l表示list,v表示矢量vector)。函数execl、execlp和execle要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾。对于另外三个函数(execv、execvp和execve),则应先构造一个指向各参数的指针数组,然后将该数组地址作为这三个函数的参数。

在ISO C原型之前,对execl、execle和execlp这三个函数表示命令行参数的一般方法是:

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

应当特别指出的是:在最后一个命令行参数之后跟了一个空指针。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整型参数。如果一个整型数的长度与char *的长度不同,那么exec函数的实际参数就将出错。

最后一个区别与向新程序传递环境表相关。以e结尾的两个函数(execle和execve)可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的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[]数组,而不使用当前环境。

                                          表8-6 6个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的处理方式与此相同。

在很多UNIX实现中,这6个函数中只有execve是内核的系统调用。另外5个只是库函数,它们最终都要调用该系统调用。

程序清单8-8 exec函数实例

[root@localhost apue]# cat prog8-8.c
#include "apue.h"
#include <sys/wait.h> char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL }; int
main(void)
{
pid_t pid; if((pid = fork()) < 0)
{
err_sys("fork error");
}
else if(pid == 0) /*specify pathname, specify environment */
{
if(execle("/home/zhu/apue/echoall", "echoall", "myarg1", "MY ARG2", (char *)0, env_init) < 0)
err_sys("execle error");
} if(waitpid(pid, NULL, 0) < 0)
err_sys("wait error"); if((pid = fork()) < 0)
{
err_sys("fork error");
}
else if(pid == 0) /* specify filename, inherit environment */
{
if(execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)
err_sys("execlp error");
} exit(0);
}

注意,我们将第一个参数(新程序中的argv[0])设置为路径名的文件名分量。某些shell将此参数设置为完整的路径名。这只是一个惯例。我们可将argv[0]设置为任何字符串。

程序清单8-8中要执行的程序echoall示于程序清单8-9中。这是一个很普通的程序,它回送其所有命令行参数及全部环境表。

程序清单8-9 回送所用命令行参数和所有环境字符串

[root@localhost apue]# cat echoall.c
#include "apue.h" int
main(int argc, char *argv[])
{
int i;
char **ptr;
extern char **environ; for(i=0; i<argc; i++) /* echo all command-line args */
printf("argv[%d]: %s\n", i, argv[i]); for(ptr = environ; *ptr != 0; ptr++) /*and all env strings */
printf("%s\n", *ptr); exit(0);
}

执行程序清单8-8得到

[root@localhost apue]# ./prog8-8
argv[0]: echoall
argv[1]: myarg1
argv[2]: MY ARG2
USER=unknown
PATH=/tmp
argv[0]: echoall
argv[1]: only 1 arg
SSH_AGENT_PID=4279
HOSTNAME=localhost.localdomain
DESKTOP_STARTUP_ID=
SHELL=/bin/bash
......

 

最后总结一下使用exec函数时需要考虑哪些参数:首先,要执行的新程序名(带不带路径);其次,命令行参数(列表形式还是指针数组形式);最后,环境表(要不要传递环境表)

 

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

进程控制之exec函数的更多相关文章

  1. 进程控制之fork函数

    一个现有进程可以调用fork函数创建一个新进程. #include <unistd.h> pid_t fork( void ); 返回值:子进程中返回0,父进程中返回子进程ID,出错返回- ...

  2. linux c编程:进程控制(三)_exec函数

    fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间.堆.栈等资源的副本.注意,子进程持有的是上述存储空间的“副本”,这意味着父子进 ...

  3. 二十五、Linux 进程与信号---exec函数

    25.1 介绍 在用 fork 函数创建子进程后,子进程往往要调用一种 exec 函数以执行另一个程序 当进程调用一种 exec 函数时,该进程完全由新程序代换,替换原有进程的正文,而新程序则从其 m ...

  4. 2.3 进程控制之exec函数族

    学习目标:学习使用exec函数族的重要的几个函数  一.引言 进程通过exec函数根据指定的文件名或目录名执行另一个可执行文件,当进程调用exec函数时,该进程的数据段.代码段和堆栈段完全被新程序替换 ...

  5. Linux系统编程_8_进程控制之fork_wait_waitpid函数

    fork函数: #include <unistd.h>        pid_t fork(void); fork用来创建一个子进程: 特点: fork调用后会返回两次,子进程返回0,父进 ...

  6. 进程控制之system函数

    ISO C定义了system函数,但是其操作对系统的依赖性很强.POSIX.1包括了system接口,它扩展了ISO C定义,以描述system在POSIX.1环境中的运行行为. #include & ...

  7. 进程控制之vfork函数

    vfork函数的调用序列和返回值与fork相同,但两者的语义不同. vfork用于创建一个新进程,而新进程的目的是exec一个新程序.vfork和fork一样都创建一个子进程,但是它并不将父进程的地址 ...

  8. 进程控制之exit函数

    进程有下面5种正常终止方式: (1)在main函数内执行return语句.这等效于调用exit. (2)调用exit函数.此函数有ISO C定义,其操作包括调用各终止处理程序(终止处理程序在调用ate ...

  9. 进程控制之waitid函数

    Single UNIX Specification的XSI扩展包括了另一个取进程终止状态的函数--waitid,此函数类似于waitpid,但提供了更多的灵活性. #include <sys/w ...

随机推荐

  1. 在Mac OS X 10.9上安装 Thrift 0.9.1

    Thrift 0.9.1 官方文档中对于Mac OS X上的安装描述适合 10.8,但不适用于10.9. Homebrew  macport 默认都不能在 10.9上安装Thrift 0.9.1成功 ...

  2. 【boost】使用装饰者模式改造boost::thread_group

    在项目中使用boost::thread_group的时候遇到几个问题: 1.thread_group不提供删除全部thread列表的方法,一直使用create会是其内部列表不断增加. 2.thread ...

  3. dom 按着shift多选

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  4. 由于SSH配置文件的不匹配,导致的Permission denied (publickey)及其解决方法。

    读者如要转载,请标明出处和作者名,谢谢.地址01:http://space.itpub.net/25851087地址02:http://www.cnblogs.com/zjrodger/作者名:zjr ...

  5. Linux 修改hostname 文件

    linux 的机器修改hostname: 修改 /etc/hosts 修改 /etc/sysconfig/network 重启机器reboot

  6. hdu 2444 The Accomodation of Students(最大匹配 + 二分图判断)

    http://acm.hdu.edu.cn/showproblem.php?pid=2444 The Accomodation of Students Time Limit:1000MS     Me ...

  7. LDS,LES,LFS,LGS,LSS指令

    LDS,LES,LFS,LGS,LSS其指令格式都是         LDS reg16,mem32 其意义是同时给一个段寄存器和一个16位通用寄存器同时赋值 具体如下:reg16=mem32的低字, ...

  8. 解决window8 下连接PLSQL 报ora-12154错误

    操作系统版本:window8 64位企业版 数据库:oracle10g2 安装PLSQL,登录PLSQL报ORA-12154错误. 首先:所以需要下载一个32位客户端,我同时也下载了64位客户端,具体 ...

  9. android UI开源库

    . ActionBarSherlock ActionBarSherlock是一个独立的Android设计库,可以让Android 2.x的系统也能使用ActionBar.此 外,ActionBarSh ...

  10. Nginx系列~Nginx服务启动不了

    Nginx服务有时起动不了了,原因是80端口为其它应用程序占用了,这时,我们需要查看是哪个程序占用了它,可能是IIS的某个站点,或者Tomat,Apache等,都有可能,所以,我们需要查看一下电脑80 ...