exec函数族


title: exec函数族
date: 2019-06-15 18:32:38
tags:
- Linux
categories:
- Linux

进程的程序替换,exec 函数族。

fork 创建子进程后执行的是和父进程相同的程序,如果需要他执行不同的代码分支,子进程旺旺需要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程的用户空间代码的数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新的进程,所以调用 exec 前后该进程的 id 并未改变。

当前进程的 .text 和 .data 替换为所需要加载的程序的 .text 和 .data,然后让进程从新的 .text 第一条指令开始执行,进程的 id 不变,换核子不换壳子。

exec 函数族的一些参数有些不同,底层实现原理一样。

SYNOPSIS
     #include <unistd.h>
     extern char **environ;
     int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
     int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[] */);
     int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
     int execv(const char *path, char *const argv[]);
     int execvp(const char *file, char *const argv[]);
     int execvP(const char *file, const char *search_path, char *const argv[]);
  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

事实上,只有 execve 是真正的系统调用,其他五个函数最终都调用 execve,所以 execve 在 man 手册第 2 节,其他函数在 man 手册的第 3 节。这些函数之间的关系如下图:

exec函数族,图片来源于网络

execl()

加载一个进程,通过 路径+程序名 来加载。

int execl(const char *path, const char *arg0, ... /*, (char *)0 */);

**返回:**成功:无返回;失败:-1。

**参数:**path:可执行程序的路径,如/bin/ls。后面跟要传给这个程序的参数。

有了这个函数就可以来加载一个自定义的程序了。

栗子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void) {
    pid_t ret = fork();
    if (ret == -1) {
        perror("fork error");
        exit(-1);
    } else if (ret == 0) {
        // 子进程
        execl("/bin/ls", "ls", "-a", NULL);
    } else {
        // 父进程
        sleep(1);
        printf("father process\n");
    }
    return 0;
}

execl_ls.c

execlp()

加载一个进程,借助 PATH 环境变量。

int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);

**返回:**成功:没有返回;失败:-1。

**参数:**file:要加载的程序名。该函数需要配合 PATH 环境变量来使用,当 PATH 中所有目录搜索后没有参数 file 则返回出错。后面跟传给要执行程序的参数。

该函数通常用来调用系统程序。如:ls、date、cp、cat 等。

栗子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void) {
    pid_t ret = fork();
    if (ret == -1) {
        perror("fork error");
    } else if (ret == 0) {
        // 子进程
        execlp("ls", "ls", "-al", NULL); // 注意第二个参数 ls 对应到main函数的参数就是argv[0],传给ls其实他并没有使用这个参数
    } else {
        // 父进程
        sleep(1);
        printf("father process\n");
    }
    return 0;
}

execlp_ls.c

execle()

最后一个参数是一个数组,自己的环境变量数组,这个数组必须以 NULL 结尾。

int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[] */);

execv()

int execv(const char *path, char *const argv[]);

栗子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void) {
    pid_t ret = fork();
    if (ret == -1) {
        perror("fork error");
        exit(-1);
    } else if (ret == 0) {
        // 子进程
        char* argv[] = {"ls", "-a", NULL};
        execv("/bin/ls", argv);
    } else {
        // 父进程
        sleep(1);
        printf("father process\n");
    }
    return 0;
}

execv_ls.c


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章