进程程序替换

来源:互联网 发布:mac 终端连接数据库 编辑:程序博客网 时间:2024/06/11 20:25

引入进程程序替换的概念

一般在fork了一个子进程之后,子进程往往会调用exec函数组来执行其他的程序,因为我们的子进程不可能直接执行很多的功能,这个大家在以后的操作中会慢慢的体会到

程序替换的几个特点

  • 进程在使用exec函数族进程进程替换的时候,并没有创建一个新的进程,所以当前进程的进程ID并没有改变。exec函数族只是用磁盘上面的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。
  • 调用exec函数族后,除非调用失败,否则没有返回值,并且调用失败之后,调用处之后的子进程的程序继续执行,如果调用成功,调用处之后的子进程将不再执行。
  • 当子进程调用exec函数族之后,不管成功与否,父进程依然可以正常执行的。
    下面我们想写一些程序来测试上面的例子
#include<stdio.h>  2 #include<stdlib.h>  3 #include<unistd.h>  4 int main()  5 {  6     pid_t son = fork();  7     if(son == 0)  //子进程  8     {  9         printf("i am son. my id is %d\n",getpid()); 10         execl("/bin/ls","ls","-a","-n","-l",NULL); 11         printf("调用exec函数失败\n"); 12         exit(0); 13     } 14     else //父进程 15     { 16         printf("i am father\n"); 17         sleep(5); 18     } 19  20     return 0; 21 }

这里的结果如下图所示

这里写图片描述
我们来根据上面的结果来分析一下,这里执行完我们的替换函数之后,就没有执行printf(“执行完毕exec函数\n”);说明 我们上文中的第二个结论中“如果调用成功调用处之后的子进程将不再执行”是正确的。
接下来我们把程序改一下,测试一下如果调用失败是一个什么样的结果
我们这里只把exec函数中的”/bin/ls”改成”/bin/lsss”这样它在bin目录下肯定是找不到了,所以程序应该是调用失败的,失败了之后显示的结果是下图

这里写图片描述
这说明当我们exec函数失败后,调用的进程没有被替换,所以后面的程序继续执行
然后我们还需要看一下父进程的 结果是什么样的,从 上面的结果看不管子子进程调用exec函数结果如何,父进程始终正常运行。
接下来我们试了一下vfork的结果是什么样的,测试之后的结果还是什么上面的结论,当子进程调用exec函数族之后,不管成功与否,父进程依然可以正常执行的
大家可以下去尝试一下vfork,vfork的作用 是保证子进程先执行。

简析exec函数族

首先看一下exec函数族的定义

#include <unistd.h> 2 extern char **environ; 3 int execl(const char *path,              const char *arg, ...); 5 int execlp(const char *file,  6            const char *arg, ...); 7 int execle(const char *path, 8            const char *arg, 9            ..., 10            char * const envp[]);11 int execv(const char *path,12           char *const argv[]);13 int execvp(const char *file, 14            char *const argv[]);15 int execve(const char *file,16             char *const argv[],17             char *const envp[]);

浅析进程替换函数
进程替换函数有六种分别是execl、execlp、execle和execv、execvp、execve,我们首先观察这几个函数会发现这些函数都是以exec开头的,由此我们称这些函数是exec函数族。
接下来又分为两类,一类是execl,一类是execv。前面三个是以execl开头,这个开头有一个特点就是在exec后面加上了l,我们发挥想象 可以想象到STL中的list,就是一个链表,这里可以理解为我们在使用这中类型的函数的时候是需要传入的一个可变的参数列表的。
这个可变参数列表实际上就是一些程序的一些可执行选项,比如我们在执行ls命令的时候,实际上就是在shell程序下进行了一次程序替换,在执行ls的时候我们可以给他它加上许多选项,比如ls -a -l等等。
另外就是execv,这个v可以让我们想起STL中的vector,就是线性表,就是我们在使用这个函数的时候需要传入的是一个数组。

再来解释一下带有p的函数,表示不需要加入替换程序的绝对路径或者是相对路径,不带p的就是要加入绝对路径或者是相对路径,另外还有就是带有e的函数,这个时候我们可以传入特定的环境变量。

接下来举例子来具体看看这些函数是如何使用,比如我们在父进程中创建了一个子进程,然后又在这个子进程中的合适位置调用了exec函数,那么这个时候就会发生程序替换

execl
execl("/bin/ls","ls","-a","-l","-n",NULL)

这里因为是一个execl函数,他没有带有p,所以我们第一个参数传递的是替换程序的绝对路径或者是相对路径,这里我门替换的函数是ls函数,它的绝对路径是”/bin/ls”,下面的那些事执行这个程序的一些选项,注意要以NULL结尾

execlp
execlp("ls","ls","-a","-l","-n",NULL)

加上这个p之后我们就不需要加上程序的路径了,execlp会自己搜索,这个时候有人就会问了,为什么还要写两次ls呢,这里第一个ls是我们需要执行的程序,第二个ls结合着后面的一些选项就是我们是要如何执行。

execv
char* _agrv[] = {"ls","-a","-n","-l",NULL};execv("/bin/ls",_agrv);

其实这里很简单,我们无非就是把上面的那个函数的后面的选项加入到一个指针数组里面去了,这个就是execv函数组的意义,其他的两个函数这里就不解释了。

execle

这个函数我们需要传入特定的环境变量,这个留个悬念,大家自己网上搜索吧。
补充说明,上面替换的程序可以是大家自己写的程序哦…

原创粉丝点击