进程

简介:

@[TOC]

前言:

  • 本文介绍 进程退出,进程等待,进程替换和简单的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);

返回值

  • 进程退出,返回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为真即正常退出,返回退出码

    在这里插入图片描述

 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);

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");

进程替换

  • 子进程与父进程默认共享代码,但如果我们想执行一个“全新的程序”,可以通过进程替换的方式:将新程序的代码和数据加载到子进程的代码和数据区,这样子进程就可以完成这个新的进程。
  • 进程替换发生了“写时拷贝",父子进程不共享同一代码
  • 命令行上执行各种指令,本质上都是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,继续执行原先代码

path

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

“./myexe”

arg

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

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

execv

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

在这里插入图片描述

返回值

同execl

path

同execl

argv

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

execlp

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

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

在这里插入图片描述

execvp

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

在这里插入图片描述

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

execle

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

在这里插入图片描述

execve

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

在这里插入图片描述

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

直接打开与程序替换

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

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

简单的shell

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

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

在这里插入图片描述

#include <iostream>    
  #include <string.h>    
  #include <string>    
  #include <unistd.h>       
  #include <vector>       
  #include <wait.h>    
  #include <sys/types.h>    
  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;
      }
相关文章
|
网络协议 调度 Python
进程小练习
进程小练习
|
2月前
|
存储 安全 Linux
进程与线程(一)进程相关
进程与线程(一)进程相关
26 1
|
5月前
|
Linux API 调度
进程,任务
进程,任务
30 1
|
5月前
|
存储 Java Unix
什么是进程?
什么是进程?
37 0
|
5月前
|
Java Linux API
进程的认识
进程的认识
|
存储 调度
进程和进程的调度
进程和进程的调度
|
5月前
|
C语言
进程的初步实现
进程的初步实现
73 0
|
存储 缓存 安全
Linux进程理解【进程认识】
Linux进程概念理解与创建操作详细讲解,干货满满!
4360 2
Linux进程理解【进程认识】
|
Unix Linux 调度
Linux之创建进程、查看进程、进程的状态以及进程的优先级
Linux之创建进程、查看进程、进程的状态以及进程的优先级
221 0