• 操作系统:进程退出,进程等待,进程替换,简单的shell命令行解释器


    前言:

    • 本文介绍 进程退出,进程等待,进程替换和简单的shell

    • 博主收集的资料New Young,连载中。

    • 博主收录的问题:New Young

    • 转载请标明出处:New Young

    进程退出

    三种场景

    1. 代码运行完毕,结果正确
    2. 代码运行完毕,结果不正确
    3. 代码异常终止 CREASH
    • 进程通过 return或者 exit将进程退出码返还给父进程,同时自动刷新缓冲区
    • return 只有在main函数中具有终止进程的作用,其它都是作为返还值
    • exit()在任何地方都可以终止进程的作用, EXIT_SUCCESS and EXIT_FAILURE
    • _exit()也可以终止进程,但是不会刷新缓冲区
    • 程序一但CREASH–崩溃,其退出码毫无意义,但是可以通过某种操作获得CREASH的原因

    退出码

    类似错误码,不同的值代表不用意义
    在这里插入图片描述

    进程等待

    等待的原因

    1. 通过获取子进程的退出信息,得知子进程的执行结果,以便回收子进程资源
    2. 可以保证:时序问题,子进程先退出,父进程后退出—孤儿问题
    3. 进程退出会先进入“僵尸状态",会造成内存泄漏问题,也不能通过万能的 kill -9杀死进程,因此需要通过父进程waitpid()释放子进程的资源。

    阻塞与非阻塞等待

    • 父进程等待子进程是不做任何事情,为阻塞等待

    • 父进程等待子进程时,通过重复wait轮询,不断获取子进程的执行状态

    hang住

    OS中,进程因为等待某些资源,而卡住的情形即阻塞等待

    而WNOHANG即非阻塞等待

    waitpid

    pid_t waitpid(pid_t pid, int *status, int options);
    
    • 1

    返回值

    • 进程退出,返回id,但是不能保证进程是否正常退出,还是CRESASH
    • 进程没有退出,返回0,----与非阻塞等待有关系
    • 等待进程失败,返回-1

    pid_t pid

    pid=-1,等待任意一个子进程

    pid>0,等待与pid想等的id进程

    int *status

    1. status是一个输出型参数,与子进程退出信息强相关

    2. 因为status指向对象是一个int类型,即有32个比特位,因此通过低16位来反映进程的退出信息,通过第0~6位反映信号,通过 第8~15位反映退出码

    3. 宏:WEXITSTATUS获得退出码,但是退出的3种情形都会返回id,防止退出码失去意义,可以通过 宏:WIFEXITED来判断是否是正常退出,还是异常退出

    >WIFEXITED:正常退出返回 true,否则返回false
    >
    >WEXITSTATUS:若WIFEXITED为真即正常退出,返回退出码
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    ```c++
     int status=0;
       37             pid_t ret= waitpid(-1,&status,0);
       38            if(ret>0) 
       39            {
       40              printf("ret =%d\n",ret);
       41              //正常运行,
       42              if(WIFEXITED(status))
       43              {
       44 
       45                printf("exit code :%d\n",WEXITSTATUS(status));
       46              }
       47              else{
       48               printf("error get a singal :%d\n",status&0x7f);
       49 
       50              }     
       51            }                                                                                                   
       52             // printf("father wait: %d,success. status exit code:%d,status exit signal %d\n",ret,(status>>8)&0x      ff,status&0x7f);
    
    ```
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    options

    options=0:默认是阻塞等待

    options=WNOHANG:非阻塞等待

    int main()    
    {    
              pid_t id =fork();    
              if(id==0)    
                {    
                    int cnt =10;    
                    while(cnt)    
                    {    
        
                      printf("child[%d]is running;cnt =  %d\n",getpid(),cnt);    
                      --cnt;    
                      sleep(1);    
                    }    
                      exit(EXIT_SUCCESS);  
              }
               // sleep(10);
                printf("father begin\n");
                int status=0;
    
                while(1)
                {
                pid_t ret= waitpid(id,&status,WNOHANG);
                if(ret>0) //子进程退出,等待成功
               {
                 //正常运行,
                 if(WIFEXITED(status))
                 {
    
                   printf("exit code :%d\n",WEXITSTATUS(status));
                 }                                                                                                
                 else//异常退出
                 {
                  printf("error get a singal :%d\n",status&0x7f);
    
                 }
        break;
               }else if(ret==0)//子进程未退出
               {
                  printf("do father thing \n "); 
               }
                else
               {
                  printf(" wait failure\n");
                  break;
               }                                                                                                  
                 sleep(1);
                }
     // printf("father wait: %d,success. status exit code:%d,status exit signal %d\n",ret,(status>>8)&0xff,status&0x7f);    
               printf("father end\n");
    
    • 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

    进程替换

    • 子进程与父进程默认共享代码,但如果我们想执行一个“全新的程序”,可以通过进程替换的方式:将新程序的代码和数据加载到子进程的代码和数据区,这样子进程就可以完成这个新的进程。
    • 进程替换发生了“写时拷贝",父子进程不共享同一代码
    • 命令行上执行各种指令,本质上都是bash通过子进程,将指令文件的代码与数据加载到子进程来完成的,这可以很大程度上保护了bash,OS也是如此
    • C/C++的程序运行,本质上也是通过加载器,将代码和数据加载到子进程中即进程替换
    • 进程替换通过 系统调用函数:exe*函数,公用6个

    生成多个可执行文件

    在这里插入图片描述

    替换函数

    替换函数依据不同需求,有多重形式,但是功能是一样的

    • l(list) : 表示参数采用列表
    • v(vector) : 参数用数组
    • p(path) : 有p自动搜索环境变量PATH
    • e(env) : 表示自己维护环境变量

    execl

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

    在这里插入图片描述

    返回值

    因为新的程序会替换原先子进程代码,所以成功时检测返回值是没有意义的,如果替换失败,返回-1,继续执行原先代码

    path

    可执行文件的路径+ 文件名

    “./myexe”

    arg

    • 类似命令行参数,是一个argv指针数组,但是用列表的形式传参,内部会封装对参数的处理细节。
    • 必须要以NULL结尾

    execl(…“ls”,“-al”,NULL)

    execv

     int execv(const char *path, char *const argv[]);
    
    
    • 1
    • 2

    在这里插入图片描述

    返回值

    同execl

    path

    同execl

    argv

    1. 传入一个指针数组,数组内容是命令行参数

    execlp

    int execlp(const char *file, const char *arg, ...);
    
    • 1

    同execl类似,唯一的区别是,因为环境变量的存在,进程会依据PATH自己寻找到相应的文件

    在这里插入图片描述

    execvp

    int execvp(const char *file, char *const argv[]);
    
    • 1

    在这里插入图片描述

    类似execv,唯一的区别是,因为环境变量的存在,进程会依据PATH自己寻找到相应的文件

    execle

    int execle(const char *path, const char *arg, ...,char *const envp[]);
    
    • 1

    在这里插入图片描述

    execve

    这个函数是系统调用函数,其它函数都是通过execve封装的库函数

    在这里插入图片描述

    int execve(const char *path, char *const argv[], char *const envp[])
    
    • 1

    直接打开与程序替换

    直接打开是:建立一个新的进程

    程序替换是:覆盖子进程原有的代码和数据

    简单的shell

    子进程执行的新程序一般是第三方指令,即bin目录下的指令

    而对于内键的指令如:cd,pwd指令,需要父进程自己执行

    在这里插入图片描述

    #include     
      #include     
      #include     
      #include        
      #include        
      #include     
      #include     
      using namespace std;                                                                              
      #define  MAX 100                                                                                                                                         
      int main()                                                                    
      {                                                                                                                                
             string s;                                                                     
             while(1)                                                                                                                                                     
            {                                                                              
                 printf("[New_Young@VM-12-17-centos minshel$]      ");                     
             s.clear();                                                                    
              //获取一行字符                                                               
              getline(cin,s);                                                              
              //分割字符串                                                                 
             const char * sep=" ";                                                         
            // s的c_str()函数返回的是一个常量指针,strok需要一个指针a                      
            //                                                                             
             char *command= new char[s.size()+1];                                          
             strcpy(command,s.c_str());                                                    
             char *argv[MAX]={0};                                                          
              argv[0]= strtok(command,sep);         
                       int i=1;
          while( argv[i]=strtok(NULL,sep) )
              {
                ++i;
              }
              printf("begin:\n");
                   // 检测是否为内键指令
                if(strcmp(argv[0],"cd")==0)
                 {
       
                  if(argv[1]!=NULL)chdir(argv[1]);
                 continue;
                }
             if(fork()==0)
             {
                 execvp(argv[0],argv);
                 printf(" failure\n");
                 exit(1);
             }
           
            int ret=  waitpid(-1,&status,0);          
             if(ret>0)
               {
              if( WIFEXITED(status))
               {
                  printf("exit code: %d \n",WEXITSTATUS(status));                                                                                                      
                 }else{
                  printf("exit signal: %d\n",status&0x7f);
               }
        
               }else{
                  printf(" wait failure\n");
        
              }
    printf("end\n");
            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
  • 相关阅读:
    Darty自养号测评下单支付方式和注册账号手法有哪些要求?
    支持双端享用,无比强大。
    鸿蒙开发语言_ArkTS开发语言体验_TypeScript语言环境搭建_TS声明和数据类型---HarmonyOS4.0+鸿蒙NEXT工作笔记003
    【Element-UI】Mock.js,案例首页导航、左侧菜单
    数据驱动的软件智能化开发| ChinaOSC
    如何提取网页中的日期?
    maven-assembly-plugin
    回忆初学编程的糗事:愚蠢的代码也是宝贵的学习经验
    【Android】Android Studio 调用 类库
    IP地址查询
  • 原文地址:https://blog.csdn.net/qq_55439426/article/details/126130174