• 【Linux】进程程序替换 &&简易mini_shell实现


    替换原理

    目前,我们使用fork创建子进程,为了用if,else让子进程执行父进程代码的一部分。如果想让子进程执行一个全新的程序,进程不变,仅仅替换当前进程的代码和数据,就叫做 进程替换

    在这里插入图片描述

    子进程往往要调用一种exec函数,执行另一个程序,当进程调用一种exec函数时。进程的用户空间代码和数据被新程序完全替换。调用exec函数前后进程的id不变。

    程序替换的本质:把指定的代码和数据,加载到特定进程的上下文中。
    C/C++程序要运行,必须通过 加载器先加载到内存。
    方式:exec*程序替换函数。

    替换函数

    在Linux下使用man 3 exec查看:
    在这里插入图片描述

    在这里插入图片描述

    六种函数都是以exec开头的吗,称为exec函数。

    exec函数的返回值:
    成功时,函数不返回。失败时,函数返回-1。

    -> 所以exec函数只有出错的返回值而没有成功的返回值。只要exec*返回了,就一定时因为调用失败了。

    替换函数的使用

    execl:
    int execl(const char *path, const char *arg, ...);

    path:要执行的目标程序的全路劲,所在路径/文件名形式,来表示你要执行谁
    arg:表示你要执行的目标程序在命令行上怎么执行,这里的参数就怎么一个一个的传递进去,和命令行上的形式一样!
    … :因为时可变参数列表,所以必须用NULL结尾!

    接下来看7个函数原型:

    #include 
    
    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execle(const char *path, const char *arg, ..., char * const envp[]);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[], char *const envp[]);
    int execve(const char* path, char* const argv[], char* const envp[]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    简单的理解方法:

    l:表示arg参数用列表传,const char* arg, ...

    在这里插入图片描述
    v:arg参数用数组传,char* const argv[]

    在这里插入图片描述

    p:有p自动搜索环境变量 PATH,不需要自己写路径。

    在这里插入图片描述

    e:自己维护环境变量(自定义环境变量)

    所有的接口,差别并不大,只是参数不同。设置这么多接口,是为了满足不同的应用场景。

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

    只有execve时真正的系统调用,其他五个函数最终都调用execve,在man手册用 man 2 exec可以查看,其他函数在man 3 exec可以查看。

    execve的demo:

    char *env[] = {
        "MYENV1_xxxxxxxxxxxxxxxxxxxxxxxxxx",
        NULL
      };
    
    execle("./myload", "myload", NULL, env);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    简易shell实现程序

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define NUM 128
    #define CMD_NUM 64
    
    int main()
    {
      char command[NUM];
      for(; ;)
      {
        char *argv[CMD_NUM] = {NULL};
        // 1、打印提示符
        command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
        printf("[zjy@myhostname mydir]# ");
        fflush(stdout);
        // 2、获取命令字符串
        fgets(command, NUM, stdin);
        command[strlen(command)-1] = '\0';
        printf("echo: %s\n", command);
       
        // ls -a -l -d\0
        // 3、解析命令字符串  strtok
        const char *sep = " ";
        argv[0] = strtok(command, sep);
        int i = 1;
        while(argv[i] = strtok(NULL, sep))
        {
          ++i;
        }
    
        // 4、检测命令是否是需要shell本身执行的,内建命令
        if(strcmp(argv[0], "cd") == 0)
        {
          if(argv[1] != NULL)
            chdir(argv[1]);
            continue;
        }
    
        // 5、执行第三方命令
        if(fork() == 0)
        {
          // child
          execvp(argv[0], argv);
          exit(1);
        }
    
        // father
        waitpid(-1, NULL, 0);
        
        
        // for(i=0; argv[i]; ++i)
        // {
        //   printf("%s\n", argv[i]);
        // }
        
    
      }
      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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    注意点:

    fork()执行的命令是第三方命令(独立的),但是cd不是,cd回退只是子进程回退,但是当子进程结束后,父进程还在原来的路径下,所以这时,不能创建子进程,要让父进程自己执行,可以调用 chdir函数。不能用程序替换,如果使用,会导致整个代码发生改变,不能继续循环输入了。

  • 相关阅读:
    Unity 灯光组件Light
    软件测试基础篇
    HTML5网页设计制作基础大二dreamweaver作业、使用HTML+CSS技术制作博客网站(5个页面)
    python 使用requests爬取百度图片并显示
    【随想】每日两题Day.4
    【Github】 Github修改仓库的基本信息
    【通义千问】大模型Qwen GitHub开源工程学习笔记(4)-- 模型的量化与离线部署
    【服务注册框架1】Eureka&nacos 两者的区别
    Redis6笔记02 配置文件,发布和订阅,新数据类型,Jedis操作
    Maven 入门教程
  • 原文地址:https://blog.csdn.net/Joy_Cheung666/article/details/126265707