《unix高级环境编程》进程控制——exec序列函数

来源:互联网 发布:steam skyline 源码 编辑:程序博客网 时间:2024/05/17 08:24

exec 替换进程映像

        在进程的创建上 UNIX 采用了一个独特的方法,它将进程创建与加载一个新进程映象分离,这样可以方便对两种操作进行管理。当创建了一个进程之后,通常可以用 exec 系列的函数将子进程替换成新的进程映象。当然,exec 系列的函数也可以将当前进程替换掉。

exec 序列函数

        fork 函数创建一个子进程时,几乎复制了父进程的全部内容。exec 序列函数可以在一个在进程中启动另一个程序的执行,它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程 ID 未改变( exec 序列函数不创建新进程),其他全部被新的程序替换了。
        在 Linux 中使用 exec 序列函数主要有两种情况:

  • 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用 exec 序列函数中的任意一个函数让自己重生。
  • 如果一个进程想执行另一个程序,那么它就可以调用 fork 函数新建一个进程,然后调用 exec 序列函数中的任意一个函数,这样看起来就像通过执行应用程序而产生了一个新进程(这种情况非常普遍)。
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* exec 序列函数 */  
  2.   
  3. /* 
  4.  * 函数功能:把当前进程替换为一个新的进程,新进程与原进程ID相同; 
  5.  * 返回值:若出错则返回-1,若成功则不返回; 
  6.  * 函数原型: 
  7.  */  
  8. #include <unistd.h>  
  9. int execl(const char *pathname, const char *arg, ...);  
  10. int execv(const char *pathnam, char *const argv[]);  
  11. int execle(const char *pathname, const char *arg, ... , char *const envp[]);  
  12. int execve(const char *pathnam, char *const argv[], char *const envp[]);  
  13. int execlp(const char *filename, const char *arg, ...);  
  14. int execvp(const char *filename, char *const argv[]);  
        这6个函数在函数名和使用语法的规则上都有细微的区别,下面就从可执行文件查找方式、参数传递方式及环境变量这几个方面进行比较。
  1. 查找方式:前4个函数的查找方式都是完整的文件目录路径 pathname ,而最后两个函数(也就是以p结尾的两个函数)可以只给出文件名 filename,系统就会自动按照环境变量 “$PATH” 所指定的路径进行查找。
  2. 参数传递方式:exec 序列函数的参数传递有两种方式:一种是逐个列举的方式,而另一种则是将所有参数整体构造指针数组传递。在这里是以函数名的第5位字母来区分的,字母为 “l”(list)的表示逐个列举参数的方式,其语法为 const char *arg;字母为 “v”(vertor)的表示将所有参数整体构造指针数组传递,其语法为 char *const argv[]。读者可以观察 execl()、execle()、execlp() 的语法与 execv()、execve()、execvp() 的区别。这里的参数实际上就是用户在使用这个可执行文件时所需的全部命令选项字符串(包括该可执行程序命令本身)。要注意的是,这些参数必须以NULL结束。
  3.  环境变量:exec 序列函数可以默认系统的环境变量,也可以传入指定的环境变量。这里以 “e”(environment)结尾的两个函数 execle() 和 execve() 就可以在 envp[] 中指定当前进程所使用的环境变量。
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1.     表 1 exec 序列函数的总结      
  2. 前4位 统一为:exec      
  3. 第5位 l:参数传递为逐个列举方式    execl、execle、execlp  
  4.      v:参数传递为构造指针数组方式     execv、execve、execvp  
  5. 第6位 e:可传递新进程环境变量     execle、execve  
  6.      p:可执行文件查找方式为文件名     execlp、execvp  
        事实上,这6个函数中真正的系统调用只有 execve(),其他5个都是 C 库函数,它们最终都会调用 execve() 这个系统调用。在使用 exec 序列函数时,一定要加上错误判断语句。exec 很容易执行失败,其中最常见的原因有:
  1. 找不到文件或路径,此时 errno 被设置为 ENOENT。
  2.  数组 argv 和 envp 忘记用NULL结束,此时,errno 被设置为 EFAUL。
  3. 没有对应可执行文件的运行权限,此时 errno 被设置为EACCES。
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 执行exec函数,下面属性是不发生变化的: 
  2. *进程ID和父进程ID(pid, ppid) 
  3. *实际用户ID和实际组ID(ruid, rgid) 
  4. *附加组ID(sgid) 
  5. *会话ID 
  6. *控制终端 
  7. *闹钟余留时间 
  8. *当前工作目录 
  9. *根目录 
  10. *umask 
  11. *文件锁 
  12. *进程信号屏蔽 
  13. *未处理信号 
  14. *资源限制 
  15. *进程时间 
  16. */  
  17. /*而下面属性是发生变化的: 
  18. *文件描述符如果存在close-on-exec标记的话,那么打开的文件描述符会被关闭。 
  19. *如果可执行程序文件存在SUID和SGID位的话,那么有效用户ID和组ID(euid, egid)会发生变化 
  20. */  
         exec 序列函数的关系如下图所示:


测试程序:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include "apue.h"  
  2. #include <unistd.h>  
  3. #include <sys/wait.h>  
  4.   
  5. char *argv[] = {"echo""STATUS=testing""\tHellow linux","\tgood bye",NULL};  
  6. char *argve[] = {"env",NULL};  
  7. char *env[] = {"USER=unknown""PATH=/tmp",NULL};  
  8. int main()  
  9. {  
  10.     pid_t pid;  
  11.   
  12.     if((pid = fork()) < 0)  /* fork error */  
  13.         err_sys("fork error.");  
  14.     else if(0 == pid)   /* in child process */  
  15.     {  
  16.         if(execl("/bin/ls","ls","-a""-l", (char *)0) == -1)  
  17.             err_sys("execl error.\n");  
  18.       //  exit(0);  
  19.     }  
  20.   
  21.     if(waitpid(pid, NULL, 0) < 0)  
  22.         err_sys("wait error.");  
  23.     if((pid = fork()) < 0)  /* fork error */  
  24.         err_sys("fork error.");  
  25.     else if(0 == pid)   /* in child process */  
  26.     {  
  27.         if(execlp("ls","ls","-a""-l", (char *)0) == -1)  
  28.             err_sys("execlp error.\n");  
  29.       //  exit(0);  
  30.     }  
  31.   
  32.     if(waitpid(pid, NULL, 0) < 0)  
  33.         err_sys("wait error.");  
  34.     if((pid = fork()) < 0)  /* fork error */  
  35.         err_sys("fork error.");  
  36.     else if(0 == pid)   /* in child process */  
  37.     {  
  38.         if(execvp("echo", argv) == -1)  
  39.             err_sys("execvp error.\n");  
  40.     }  
  41.   
  42.     if(waitpid(pid, NULL, 0) < 0)  
  43.         err_sys("wait error.");  
  44.     if((pid = fork()) < 0)  /* fork error */  
  45.         err_sys("fork error.");  
  46.     else if(0 == pid)   /* in child process */  
  47.     {  
  48.         if(execve("/usr/bin/env", argve, env) == -1)  
  49.             err_sys("execve error.\n");  
  50.     }  
  51.     exit(0);  
  52. }  

输出结果:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. total 24  
  2. drwxrwxr-x 2 nifengweijifen nifengweijifen  4096 Nov  7 15:07 .  
  3. drwxrwxr-x 3 nifengweijifen nifengweijifen  4096 Nov  7 15:07 ..  
  4. -rwxrwxr-x 1 nifengweijifen nifengweijifen 12348 Nov  7 15:07 exec  
  5. -rw-rw-r-- 1 nifengweijifen nifengweijifen     0 Nov  7 14:43 exec.c  
  6. -rw-rw-r-- 1 nifengweijifen nifengweijifen     0 Nov  7 14:43 exectest.c  
  7. total 24  
  8. drwxrwxr-x 2 nifengweijifen nifengweijifen  4096 Nov  7 15:07 .  
  9. drwxrwxr-x 3 nifengweijifen nifengweijifen  4096 Nov  7 15:07 ..  
  10. -rwxrwxr-x 1 nifengweijifen nifengweijifen 12348 Nov  7 15:07 exec  
  11. -rw-rw-r-- 1 nifengweijifen nifengweijifen     0 Nov  7 14:43 exec.c  
  12. -rw-rw-r-- 1 nifengweijifen nifengweijifen     0 Nov  7 14:43 exectest.c  
  13. STATUS=testing  Hellow linux    good bye  
  14. USER=unknown  
  15. PATH=/tmp  
0 0
原创粉丝点击