exec 函数族提供了六种在进程中启动另一个程序的方法。exec 函数族的作用是根据指定的文件名或目录名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。
在 Windows 平台下,我们可以通过双击运行可执行程序,让这个可执行程序成为一个进程;而在 Linux 平台,我们可以通过 ./ 运行,让一个可执行程序成为一个进程。
但是,如果我们本来就运行着一个程序(进程),我们如何在这个进程内部启动一个外部程序,由内核将这个外部程序读入内存,使其执行起来成为一个进程呢?这里我们通过 exec 函数族实现。
1.exec函数族
所谓exec函数族,就是以exec开头的函数,比如execl函数、execlp函数等,所以称它为exec函数族。一共有 6 个:
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
其中只有 execve() 是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
在exec函数族中,一般是由fork创建子进程后,在子进程中执行,也就是替换子进程中的东西,进程中的空间代码完全被新程序代替,但是调用exec程序不会创建新进程,进程ID也没有发生变化。

2.execl
实例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
if ((pid = fork()) < 0)
{
perror("error to create process:");
exit(1);
}
else if (pid > 0)
{
//父进程
printf("this is a parents process!\n");
wait(NULL);
printf("the child process has quited!\n");
}
else
{
//子进程
printf("this is a child process!\n");
#if 1
//第一个参数只能是绝对路径,可以使用whereis名称查看绝对路径
if(execl("/usr/bin/ls","ls","-l",NULL)==-1){
perror("error to execl:");
exit(1);
}
#endif
printf("hello,world!\n");
#if 0
#endif
}
return 0;
}
测试运行:
[root@iz2zefozq9h39txdb8s7npz shelldemo]# ./a.out
this is a parents process!
this is a child process!
total 80
-rwxr-xr-x 1 root root 8808 Nov 20 22:49 a.out
-rw-r--r-- 1 root root 757 Nov 16 22:42 fork.c
-rw-r--r-- 1 root root 356 Nov 15 11:39 getpid.c
...
the child process has quited!
注意:exec一旦执行无论成功与否成功,子进程exec后面的代码不会执行。因此'hello,world!'不会输出。
3.execlp
execlp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0],argv[1], …, 最后一个参数必须用空指针(NULL)作结束。
实例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
if ((pid = fork()) < 0)
{
perror("error to create process:");
exit(1);
}
else if (pid > 0)
{
//父进程
printf("this is a parents process!\n");
wait(NULL);
printf("the child process has quited!\n");
}
else
{
//子进程
printf("this is a child process!\n");
#if 1
//第一个参数既可以是绝对路径也可以是相对路径
if(execlp("ls","ls","-l",NULL)==-1){
perror("error to execl:");
exit(1);
}
#endif
printf("hello,world!\n");
#if 0
#endif
}
return 0;
}
测试运行: 运行结果与execl相同。
4.execv
实例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
if ((pid = fork()) < 0)
{
perror("error to create process:");
exit(1);
}
else if (pid > 0)
{
//父进程
printf("this is a parents process!\n");
wait(NULL);
printf("the child process has quited!\n");
}
else
{
//子进程
printf("this is a child process!\n");
#if 1
char *str[]={"ls","-l",NULL};
if(execv("/usr/bin/ls",str)==-1){
perror("error to execl:");
exit(1);
}
#endif
printf("hello,world!\n");
#if 0
#endif
}
return 0;
}
运行结果与execl相同。
5.execvp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
if ((pid = fork()) < 0)
{
perror("error to create process:");
exit(1);
}
else if (pid > 0)
{
//父进程
printf("this is a parents process!\n");
wait(NULL);
printf("the child process has quited!\n");
}
else
{
//子进程
printf("this is a child process!\n");
#if 1
char *str[]={"ls","-l",NULL};
if(execvp("ls",str)==-1){
perror("error to execl:");
exit(1);
}
#endif
printf("hello,world!\n");
#if 0
#endif
}
return 0;
}
运行结果与execl相同。
6.exec执行shell脚本
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
if ((pid = fork()) < 0)
{
perror("error to create process:");
exit(1);
}
else if (pid > 0)
{
//父进程
printf("this is a parents process!\n");
wait(NULL);
printf("the child process has quited!\n");
}
else
{
//子进程
printf("this is a child process!\n");
#if 1
//第一个参数既可以是绝对路径也可以是相对路径
if(execlp("./test.sh","./test.sh",NULL)==-1){
perror("error to execl:");
exit(1);
}
#endif
printf("hello,world!\n");
#if 0
#endif
}
return 0;
}