linux19:程序替换

一:最简单的看看程序替换是什么样的(单个进程版)

1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 int main()
  5 {
  6   printf("Before : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
  7 
  8   execl("/usr/bin/top","top",NULL);                                                                                                                  
  9 
 10   printf("After  : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
 11 
 12   return 0;
 13 }

程序在调用execl之后不能打印"After"信息,因为一旦execl被调用,当前的进程映像将被替换,因此第二个print中的代码将不会被执行。

二:进程替换的原理

用fork创建子进程后执行的是和父进程相同的程序,子进程往往要调用一种exec函数以执行另一个程序。

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
例程开始执行。

调用exec并不创建新进程,所以调用exec前后该进程的id并未改变

三:验证各种程序替换的接口(多进程版)

创建一个子进程,并使用execl函数来替换子进程的映像,执行/usr/bin/ls -l -a命令。

1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8   pid_t id =fork(); //child process return 0 , parent process return child pid
  9 
 10   if(id==0)  //child process
 11   {
 12     printf("Before : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
 13     execl("/usr/bin/ls","-l","-a",NULL);                                                                                                             
 14     printf("After  : I am a process , myPid:%d,myPPid:%d\n",getpid(),getppid());
 15     exit(0);
 16   }
 17 
 18   //parent process
 19   pid_t ret = waitpid(id,NULL,0); //child process pid , status , WNOHANG
 20   if(ret>0)
 21   {
 22     printf("wait sucess,father pid:%d,wait child pid:%d\n",getpid(),ret);
 23   }
 24   return 0;
 25 }
~

execl会替换当前子进程的映像,包括程序的代码和数据

当改子进程程序替换之后,该子进程对应的PCB、进程地址空间以及页表等数据结构都没有发
生改变,对应的execl退出,会继续被父进程等待收回

四:总结

替换函数:

其实有六种以exec开头的函数,统称exec函数

exec参数的上传就如命令行一般,命令行怎么打,参数就怎么加,第一个参数为程序的路径,最后一个参数有再补个NULL即可

函数解释:


这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。

// 尝试执行ls命令
        if (execl("/usr/bin/ls", "ls", "-l", "-a", NULL) == -1) {
            // 如果execl失败,打印错误信息并退出
            perror("execl failed");
            exit(EXIT_FAILURE); // 使用非零值退出,表示程序因错误而终止
        }
        
        // 如果execl成功,if里面的代码不会被执行

命名理解:

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

execl的list链表与execv的vector数组的代码示例:

execl与execlp是否带路径代码示例:

execl函数需要你提供程序的完整路径作为第一个参数

execlp函数使用程序名来搜索程序的路径,不需要提供完整路径。它使用环境变量PATH($PATH)来查找程序,如果PATH环境变量包含的目录中有与程序名匹配的可执行文件,execlp会尝试执行它。

环境变量env:

execle 是需要环境变量的,但即使我们参数中不提供环境变量,execle也能正常运行

环境变量即使没有给子进程也能被子进程拿到,那环境变量是什么时候给进程的呢?

所以即使不通过传参方式,进程也能在程序地址空间内找到环境变量。当程序被替换时,环境变量的信息不会被替换

1:新增环境变量

A:直接在bash中新增环境变量

在bash中新增的环境变量一路被子进程继承,做到添加环境变量

B:父进程地址空间内使用putenv函数

在Linux操作系统中,putenv 是一个用于设置环境变量的函数。可以在程序运行时修改或添加环境变量

putenv 函数会修改程序的全局环境,这个环境变量的设置会继承到该程序启动的任何子进程。

int putenv(char *string);

标准代码示例:

#include <stdlib.h>

int main() {
    char *env_var = "MY_VAR=123";
    if (putenv(env_var) == -1)
    {
        // 处理错误
    }
    // 继续你的程序逻辑...
    return 0;
}

在父进程中使用putenv添加环境变量,出现在子进程的环境变量中:

2:彻底替换环境变量

当我们使用了自己的环境变量参数时,原先的环境变量会被完全替换掉

exec调用举例总结:

exec也可以调用自己写的程序:

五:自定义壳shell

考虑下面这个与shell使用了ls与ps的典型互动

ls  可以查看当前文件夹下的文件

ps 可以显示当前系统中正在运行的进程的状态。

用下图的时间轴来表示事件的发生次序。
shell 由标识为 sh 的方块代表,它随着时间的流逝从左向右移动。
shell 建立一个新的进程,然后在那个进程中运行 ls程序再运行ps程序 并等待这些进程结束。

所以要写一个shell,需要循环以下过程:
1:获取命令行
2:解析命令行
3:建立一个子进程(fork)
4:替换子进程(execvp)

5:父进程等待子进程退出(wait)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值