← 返回首页
Linux高级程序设计(三十四)
发表时间:2021-11-20 23:13:16
进程的替换

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;
}