exec 系列系统调用

系统调用exec 系列:
如果fork() 是唯一可使用的建立新进程的手段,
那么LINUX的性能会受到很大的影响,
因为fork() 只能建立相同的程序副本,
还好,LINUX 还提供了系统调用exec 系列,
exec 系列中的系统调用都完成相同的功能,
它们把一个新程序装入调用进程的内存空间,
来改变调用进程的执行代码,从而形成新的进程,
如果exec 调用成功,调用进程将被覆盖,
然后从新程序的入口开始执行,这样就产生了一个新的进程,
但是它的进程标志符与调用进程相同,
这就是说,exec 并没有建立一个与调用进程并发的进程,
而是用新进程取代了原来的进程,
所以exec 调用成功后,没有任何数据返回。
而且exec 调用后面的代码都不会被执行到,除非exec 调用失败。
exec 系列系统调用在 unistd.h 中声明如下:

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg,...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

因为exec 系列调用的使用方法和功能是差不多的,
现在只以execl 为例子进行讨论。
第一个参数是程序的完整路径,文件本身必须是一个真正的可执行程序,而不能是一个脚本文件。
第二个以及用省略号表示的其它参数一起组成了该程序执行的参数表。
按照LINUX 惯例,参数表的第一项是不带路径的程序文件名。
它们相当于shell 下的命令行参数。
由于参数个数是任意的,所以必须用一个NULL 指针来标志参数表的结尾。
下面是使用execl 的一个简单的例子:

#include<stdio.h>
#include<unistd.h>
int main()
{
    printf("executing ls\n");
    execl("/bin/ls","ls","-l",NULL);
    perror("execl ls error\n");
    return 1;
}


注意,程序在execl 调用后紧跟一个 perror 的无条件调用,
因为如果execl 调用成功的话,原进程肯定已经被覆盖了,
后面的代码是不可能被执行的,
如果后面的代码被执行了,则肯定是execl 调用出错了。

其实execl 中最后一个字母l 表示的是list,即将参数列举出来,
而execv 中最后一个字母v 表示的是vector,即将参数放在一个向量中。
下面就给一个execv 的例子:

#include<stdio.h>
#include<unistd.h>
int main()
{
    char *argv[] = {"ls","-l",NULL};
    printf("executing ls\n");
    execv("/bin/ls",argv);
    perror("execv ls error\n");
    return 1;
}

可以看出只是将参数打包到一个argv 向量中而已,其他的是完全一样的。
下面再来讨论一下execlp 和 execvp,
这两个函数只是比execl,execv 多了个p 而已,
其实p 表示的是path,第一个参数只要提供程序的文件名即可,
而不用提供完整的路径,execlp 可以通过检索shell 的环境变量
PATH 来找到要执行的程序。
对于execlp 来说,只要将execl 的调用修改一下即可
execlp("ls","ls","-l",NULL);

通过上面的讨论可见,exec 系列系统调用的使用方法是非常相似的,
只要我们掌握了其中一个,其它的也就能触类旁通了。

你可能感兴趣的:(exec 系列系统调用)