• 【1++的Linux】之进程(五)


    👍作者主页:进击的1++
    🤩 专栏链接:【1++的Linux】

    一,什么是进程替换

    我们创建出来进程是要其做事情的,它可以去调用函数,或者是执行其他的程序,子进程通过exec函数族执行其他的程序就叫做进程替换。也就是在调用进程内部执行一个可执行文件。当进程调用一种exec函数时,该进程的代码和数据完全被新程序替换,新程序从main函数开始执行,由于未创建新进程,所以替换前后进程的id等并不改变。
    在加载新程序之前,父子进程的关系是:代码共享,数据写时拷贝。
    当子进程加载新程序的时候就是一种“写入”,此时代码也就需要进行写时拷贝,进行分离!!!

    二,替换函数

    下面是六种exec开头的函数,统称exec函数

    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 execve(const char *path, char *const argv[], char *const envp[]);


    其中只有execve()是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
    这些函数,如果调用成功则加载新的程序开始执行,不再返回;若调用失败,则返回-1 。
    下面是这些函数的演示:

    #include
    #include
    int main()
    {
        //execl---带路径,参数包传参
        //execl("/usr/bin/ps","ps","-ef",NULL);
       // execlp("ls","-l",NULL);
       
          char* env[]={"PATH=/bin:/usr/bin",NULL};
          char* argv[]={"ls","-l",NULL};
         // execv("/usr/bin/ls",argv);
         // execle("./mike","mike",NULL,env);
           // execvp("ls",argv);
            execve("/usr/bin/ls",argv,env);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    execl运行结果(要写路径,参数格式未列表)
    在这里插入图片描述
    execlp运行结果(带p的:可以使用环境变量PATH,无需写路径)
    在这里插入图片描述
    execle运行结果:

    在这里插入图片描述
    execv运行结果:
    在这里插入图片描述
    execvp运行结果:

    在这里插入图片描述
    execve运行结果:

    在这里插入图片描述
    我们总结以下:

    带p可以使用环境变量PATH,无需写完整路径
    带e,自己组装环境变量。// 改变替换程序的环境变量,正确来说,让替换程序只保留 env 的环境变量
    带l参数格式为列表
    带v参数格式为数组

    事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。
    在这里插入图片描述

    exec*实际上就是一个加载器的底层接口。

    三,实现我们自己的shell

    代码如下:

    #include
    #include
    #include
    #include
    #include
    #include
    #define NUM 32
    char cmd_line[NUM];
    char* _argv[32];
    char myval[32];//这个buffer用来保存我们添加的环境变量,不然保存在cmd_line中会被覆盖。
    int main()
    {
        extern char** environ;//是一个外部的全局变量,储存着系统的全局变量。
        while(1)
        {
            //打印提示信息
            printf("[hyp @myshell]#");
            fflush(stdout);
            memset(cmd_line,'\0',sizeof(cmd_line));
            //用户输入
            if(fgets(cmd_line,sizeof(cmd_line),stdin)==NULL)
            {
                continue;
            }
            cmd_line[strlen(cmd_line)-1]='\0';
            //分割字符
            _argv[0]=strtok(cmd_line," ");
            int i=0;
            if(strcmp(_argv[0],"ls")==0)//加颜色
            {
                _argv[++i]="--color=auto";
            }
            if(strcmp(_argv[0],"ll")==0)
            {
                _argv[0]="ls";
                _argv[++i]="--color=auto";
                _argv[++i]="-l";
            }
            while(_argv[i])//分割
            {
                i++;
                _argv[i]=strtok(NULL," ");
                
            }
    
            if(strcmp(_argv[0],"export")==0 && _argv[1]!=NULL)
            {
                strcpy(myval,_argv[1]);
                int ret=putenv(myval);
                if(ret==0)
                {
                    printf("%s export success\n",myval);
                }
    
                continue;
            }
    	        
    	    if(strcmp(_argv[0],"cd")==0)
    	    {
    	        if(_argv[1]!=NULL)
    	        {
    	        //内置命令,让父进程自己执行的命令,本质就是shell的一个函数调用。
    	            chdir(_argv[1]);//改变当前工作目录
    	        }
    	        continue;
    	
    	    }
    	    int id=fork();
    	    if(id==0)//child
    	    {
    	        printf("child MYVAL:%s\n",getenv("MYVAL"));
    	        printf("PATH:%s\n",getenv("PATH"));
    	        execvp(_argv[0],_argv);
    	        exit(1);
    	    }
    
    	    //father
    	    int status=0;
    	    int ret=waitpid(id,&status,0);
    	    if(ret>0)
    	    {
    	        printf("退出码:%d\n",WEXITSTATUS(status));
    	
    	    }
       }
        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
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
  • 相关阅读:
    Prompt-To-Prompt——仅通过文本进行图像编辑
    一文读懂为什么需要跨链?跨链是什么?跨链实现技术?
    网络问题定位工具记录
    分布式开源存储架构Ceph概述
    【学习笔记】RabbitMQ
    Adams 插件Plugin二次开发教程
    Springmvc笔记
    Unity与C#
    【无标题】
    周亚军 红宝书 案例 3 telnet远程管理协议
  • 原文地址:https://blog.csdn.net/m0_63135219/article/details/133467378