• Linux-进程替换


    进程程序替换目的

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

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

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


    了解程序替换

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

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

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

    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    程序替换的原理

    在这里插入图片描述

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

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

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

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


    程序替换函数

    在这里插入图片描述

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

    #include 
    #include 
    
    int main() 
    {
        if (fork() == 0)
         { // 子进程
            execl("/bin/echo", "echo", "Hello", "World!", NULL);
        } 
        else 
        { // 父进程
            wait(NULL);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

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

    #include 
    #include 
    
    int main() 
    {
        if (fork() == 0) { // 子进程
            execlp("echo", "echo", "Hello", "World!", NULL);
        } 
        else 
        { // 父进程
            wait(NULL);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意:
    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 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在上述代码中,我们使用 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。

    在这里插入图片描述

    在这里插入图片描述


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

  • 相关阅读:
    Maven项目使用Checkstyle检查代码
    Java 面向对象(上)
    antv/x6 键盘快捷键事件
    nvcc -V和nvidia-smi的关系
    盘点全球 101 款 AI Agent 产品丨2月更新
    【毕业设计】基于java+swing+Eclipse的推箱子游戏设计与实现(毕业论文+程序源码)——推箱子游戏
    Linux CPU线程绑核
    NFT:使用 EIP-2981 开启 NFT 版税之旅
    Vue3中的常用组件通信大总结 包括最Vue3.4defineModel()实现组件双向绑定
    【LeetCode题目详解】第九章 动态规划part09 198.打家劫舍 213.打家劫舍II 337.打家劫舍III(day48补)
  • 原文地址:https://blog.csdn.net/weixin_65660590/article/details/134558614