Linux进程控制——进程程序替换、bash的模拟实现

在学习进程的时候,我们想fork一个子进程,然后就可以给他布置任务了

但是如果我们分成两个人开发,父子进程分别负责不同的任务,等待开发完成之后除了合并项目或者复制粘贴还有更好的办法吗

其实是有的,当子进程被创建后不想执行父进程代码时,就需要用到程序替换

主要是exec系列函数的用法

exec系列函数

在这里插入图片描述

这里一共有六个函数,但是是同一个系列的,也有一定的规律,如果他们替换失败了,返回值都是-1

execl

这是里面最简单的函数,他的函数有两个,第一个是执行的程序路径,之后的是参数包,表示如何执行该程序

可以理解为前一个是环境变量,后一个是程序指令,只不过按单词分开,具体使用是这样的

例如

#include<stdio.h>
#include<unistd.h>
int main()    
{    
    printf("程序替换\n");    
    int n = execl("/usr/bin/ls","ls","-a","-l",NULL);
    if(n==-1)    
    {    
        perror("execl");    
    }    
    printf("程序替换\n");    
    return 0;    
}    

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果是这样的,我们发现执行完execl之后执行了ls命令,而最后的输出命令却没有执行

说明execl之后,当前进程的代码和数据就和之前完全完全替换了,未执行的代码也会直接替换

execl的l是list的意思,其实就是要把执行程序的路径列举出来

execlp和execle

execlp的调用如下

#include<stdio.h>
#include<unistd.h>
int main()    
{    
    printf("程序替换\n");    
    int n = execlp("ls","-l",NULL);  
    if(n==-1)    
    {    
        perror("execlp");    
    }    
    printf("程序替换\n");    
    return 0;    
} 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里我们可以发现execlp函数可以不用加路径,这其实是因为他可以自动检索环境变量,p的含义就是PATH

execle的调用如下

#include<stdio.h>
#include<unistd.h>
int main()    
{    
    const char* _env[]={"MY_ENV=114",NULL};    
    printf("进行程序\n");    
    int n = execle("/usr/bin/ls","ls","-l",NULL,_env); // 定义一个环境变量MY_ENV=114传递给要去执行的程序
    if(n==-1)    
    {    
        perror("execle");    
    }    
    printf("程序替换\n");    
    return 0;    
}  

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

execle可以在执行程序之前传入自己的环境变量,e是env的意思

execv系列函数

execv的v是vector的意思,也就是向量或者数组,可以理解为利用数组来传参

在这里插入图片描述

execv的使用如下

#include<stdio.h>
#include<unistd.h>
int main()    
  {    
	  char* const set[]={"ls","-a","-l",NULL};  
      printf("程序替换\n");    
      int n = execv("/usr/bin/ls",set); 
      if(n==-1)    
      {    
          perror("execv");    
      }    
      printf("程序替换\n");    
      return 0;    
  } 

这里的使用和上面其实差距不大,包括execvp和execvpe都是类似的,需要注意的是需要在最后加一个NULL表示数组结束

bash的模拟实现

一般情况下程序替换并非将自己进行替换,而是将fork出的子进程替换,自己来负责传达命令,回收子进程

实现思路

首先我们可以肯定的是bash是一共while死循环,因为他会不断的给我们打印提示信息

我们要模拟实现就可以自行定义其中的内容

例如

#include<stdio.h>
int main()
{
    while(true)
    {
        // 提示信息
        printf("[leaf @ MyBash]$ ");
        fflush(stdout);
        // ...
    }
    return 0;
}

在模拟实现之前我们需要梳理一下思路

bash的功能是将用户输入的字符串打散之后变成一共字符串数组,将这个字符串数组分配给子进程让他去进行程序替换,最后等待回收资源即可

这里我们可以确定的是需要保存完整的用户输入的命令行字符串,大三之后的字符串数组,之后还有一些细节进行处理

#define NUM 1000 // 一行命令最长字符数
#define SIZE 16 // 一行命令最多单词数
char input_line[NUM]; // 完整输入命令
char* line_argv[SIZE]; // 打散后的字符串数组
if(fgets(input_line, sizeof(input_line), stdin) == NULL) // fget保存到数组
    continue;
input_line[strlen(input_line)-1] = '\0'; // 覆盖换行

将整个字符串打散我们可以使用strtok函数

在这里插入图片描述

line_argv[0] = strtoc(input_line, " "); // 第一个单词
int index = 1;
while(line_argv[index++] = strtok(NULL, " ")); // 之后的每一个单词

这样最麻烦的处理用户输入的部分就解决了,完整代码如下

完整代码

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#define NUM 1000 // 一行命令最长字符数
#define SIZE 16 // 一行命令最多单词数
char input_line[NUM]; // 完整输入命令
char* line_argv[SIZE]; // 打散后的字符串数组

int main()
{
    while(1)
    {
        // 提示信息
        printf("[leaf@MyBash]$ ");
        fflush(stdout);
        
        // 获取输入
        memset(input_line, '\0', sizeof(input_line));
        if(fgets(input_line, sizeof(input_line), stdin) == NULL) // fget保存到数组
    		continue;
		input_line[strlen(input_line)-1] = '\0'; // 覆盖换行
        
        // 处理输入
        line_argv[0] = strtok(input_line, " "); // 第一个单词
		int index = 1;
		while(line_argv[index++] = strtok(NULL, " ")); // 之后的每一个单词
        
        // 子进程
        pid_t id = fork();
        if(id == 0)
        {
            printf("子进程\n");
            execvp(line_argv[0], line_argv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(-1, &status, 0);
        if(ret > 0)
            printf("exit code: %d\n", WEXITSTATUS(status));
    }
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当然如果你愿意的话也可以获取当前的目录也打印在提示信息中

其他问题

我们之前介绍过内建命令,说这种命令只能让父进程调用,例如cd命令,因为写时拷贝,子进程进了某个文件夹,对父进程是没有影响的,也只有在父进程执行才有意义

if(strcmp(line_argv[0], "cd")==0)
{
    if(my_argv[1]!=NULL)
        chdir(my_argv[1]); // 切换当前工作目录
    continue;
}

还有例如export,kill,history都是内建命令

其实在Linux环境下,程序替换不仅可以替换C语言程序,甚至可以替换成Python程序、Java程序去执行他们的内部代码,这就变相实现了不同语言之间的联动

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栖林_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值