Linux-进程替换

本文详细介绍了进程程序替换的概念,包括其原理(子进程与父进程的内存映射和写时拷贝)、execl和execlp函数的使用方法以及execle函数的扩展功能,展示了如何通过这些函数让子进程执行全新的代码。
摘要由CSDN通过智能技术生成

进程程序替换目的

首先我们要知道,创建子进程的目的是什么?

  • 想让子进程执行父进程代码的一部分
  • 想让子进程执行一个全新的代码

我们之前所写的程序,子进程都是在执行父进程代码的一部分,而要想让子进程执行全新的代码,就需要进行进程程序替换
在这里插入图片描述


了解程序替换

先来看看进程程序替换是什么
在这里插入图片描述
上面这个父进程中fork了一个子进程,然后使用程序替换接口,替换了子进程的程序,父进程等待子进程结束,回收子进程

我们看一下程序替换的结果
在这里插入图片描述
这里我们看到子进程进行程序替换成了ls进程

此时,使用我们自己的程序同样可以实现ls -al的功能,因为子进程执行的就是ls -a -l程序。因为程序替换成功了,所以返回ls程序的退出码,如果替换失败,就会执行exit(1)。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

int main() {
    pid_t child_pid;

    if ((child_pid = fork()) == 0) 
    { // 子进程
        if (execl("/bin/ls", "ls", "-a", "-l", NULL) == -1) 
        {
            perror("execl");
            exit(1);
        }
    } 
    else if (child_pid > 0) 
    { // 父进程
        int status;
        waitpid(child_pid, &status, 0);
        if (WIFEXITED(status)) 
        {
            printf("Child exited with status: %d\n", WEXITSTATUS(status));
        } 
        else 
        {
            printf("Child process terminated abnormally.\n");
        }
    } 
    else 
    {
        perror("fork");
        exit(1);
    }
    return 0;
}

程序替换的原理

在这里插入图片描述

在子进程刚创建的时候,子进程和父进程通过页表映射到物理内存中空间是同一块空间,父子进程的代码段,数据段,堆,栈等区域都同一个。
在这里插入图片描述
当子进程中执行exec*()函数的时候,会发生写时拷贝,将原本物理内存中的数据段和代码段拷贝一份,放在新的物理内存中。

将磁盘中要替换的可执行程序覆盖到新的物理内存中,并且改变子进程原本的页表映射关系。

仅程序发生了替换(数据段和代码段),子进程的PCB中的task_struct仍然不变。

而且写时拷贝不仅在数据段发生,在代码段也可以发生,写时拷贝的目的同样是为了保证进程的独立性。程序替换之后,子进程执行的代码也不再是原本父进程中的代码,而是全新的代码,比如上诉例子中的ls程序。


程序替换函数

在这里插入图片描述

第一个参数path表示要执行的程序的路径,第二个参数arg表示要执行的程序的名称,后面的参数是一系列字符串类型的参数,用于指定程序的参数。这里需要注意的是,最后一个参数必须是NULL,表示参数列表的结束。

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

int main() 
{
    if (fork() == 0)
     { // 子进程
        execl("/bin/echo", "echo", "Hello", "World!", NULL);
    } 
    else 
    { // 父进程
        wait(NULL);
    }

    return 0;
}

在这里插入图片描述

第一个参数file表示要执行的程序的文件名(不包括路径),第二个参数arg表示要执行的程序的名称,后面的参数是一系列字符串类型的参数,用于指定程序的参数。最后一个参数必须是NULL,表示参数列表的结束。

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

int main() 
{
    if (fork() == 0) { // 子进程
        execlp("echo", "echo", "Hello", "World!", NULL);
    } 
    else 
    { // 父进程
        wait(NULL);
    }

    return 0;
}

注意:
execlp 函数的第一个参数是要执行的可执行程序的路径或名称。具体取决于使用的是相对路径还是绝对路径。

如果可执行程序位于当前工作目录(当前路径)中,你可以直接提供可执行程序的名称作为第一个参数。

如果可执行程序位于其他目录中,可以提供它的绝对路径或相对路径作为第一个参数。

  • 绝对路径:完整的文件系统路径,例如 /home/user/myprogram
  • 相对路径:相对于当前工作目录的路径,例如 ./myprogram../folder/myprogram

在调用 execlp 函数时,操作系统会根据给定的路径或名称去查找可执行程序,并在新的进程中执行它。

需要注意的是,execlp 函数会在系统的 PATH 环境变量定义的路径中查找可执行程序。因此,如果提供的是可执行程序的名称而不是完整路径,操作系统会根据 PATH 环境变量去寻找该程序。


在这里插入图片描述

execle 函数与 execlp 类似,但它需要显式地指定可执行程序的路径,并允许传递环境变量。下面是 execle 函数的参数说明:
path: 可执行程序的路径。可以使用绝对路径或相对路径来指定。例如,/usr/bin/myprogram 或者 ./myprogram。
arg0, arg1, …: 命令行参数,用于传递给可执行程序。常见的约定是将第一个参数作为程序的名称。例如,myprogram。
envp: 带有环境变量的指针数组。环境变量的格式为 name=value。数组最后一个元素必须为 NULL,表示环境变量列表的结束。

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

int main() {
    char* args[] = {"echo", "$MY_VAR", NULL};
    char* env[] = {"MY_VAR=my_value", NULL};

    execle("/bin/echo", "echo", "$MY_VAR", NULL, env); // 运行 echo

    printf("Exec failed\n"); // 如果运行程序失败,这行代码将不会被执行

    return 0;
}

在上述代码中,我们使用 execle 来运行 echo 命令,并将 $MY_VAR 作为参数传递给它。

同时,我们将一个名为 MY_VAR 的环境变量设为 my_value,使用 env 数组将其传递给 execle。此时,当 echo 命令执行时,它将打印 $MY_VAR,而不是实际的值。但是由于我们提供了 MY_VAR=my_value 的环境变量,因此 echo 命令可以获取到 MY_VAR 的实际值,所以输出将是 my_value。

需要注意的是,execle 会替换当前进程,所以在执行成功之后,程序就不会再执行下面的代码。如果 execle 执行失败,则会继续执行下面的代码,这时我们可以根据自己的需求进行错误处理。


在这里插入图片描述

第二个参数的指针数组,和mian命令函数中的char* argv[]一样,argv[0]是程序名,argv[1]等之后的是选项,最后一个是NULL。

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值