进程控制——LINUX

目录

前言

进程创建

进程终止

    一些相关指令

进程等待

     status的构成:​编辑    如何判断是否正常退出

    取出退出码

程序替换


前言

    进程控制包括创建、终止、等待、程序替换这四个内容。

进程创建

    在linux中创建一个子进程可以用fork函数,原型pid_t fork(void),pid_t是一个类型,fork函数包含在头文件<unistd.h>中。

int main(){
 pid_t pid=fork();
 if(pid<0){
 printf("fork failed!");//创建失败则退出
  exit(-1);
 }else if(pid==0){
  printf("This is a Child Process!");
 }else{
  printf("This is a Parent Process!");
 }                                                                                         
}  

     用fork创建的子进程与父进程一开始是在同一块地址空间的,但如果子进程或父进程中有改动,则会修改子进程的地址,值得注意的是创建出来的子进程不一定比父进程先运行。

    另一个函数vfork,vfork创建的子进程和父进程一直都是同一个地址空间,所以为了避免两个程序同时运行造成栈混乱,该函数创建出子进程后会阻塞父进程,让子进程先运行,结束后父进程再运行。

    注意:用fork创建子进程后,子进程会保留父进程的 1、进程上下文 2、进程堆栈 3、内存信息 4、打开的文件描述符 5、信号控制设置 6、进程优先级和进程组号 7、当前工作目录 8、根目录 9、资源限制 10、控制终端等

而子进程独有的:进程号(pid),自己的文件描述符和目录流的拷贝,不继承父进程的进程正文(text),数据和其他锁定内存(memory locks),不继承异步输入和输出
   

进程终止

    程序的退出最主要用的是exit和main函数里的return,注意return只有在main函数里时才是退出进程,且main函数中的返回值就是进程的返回值(返回值的意义在于了解程序运行情况),如果return在其他函数里则只是退出函数,而exit不论在哪个位置都是直接退出进程。

    进程的退出分三种情况:

    ①正常退出且任务完成 

    ②正常退出但任务未完成 

    ③异常退出

    如果系统因为调用失败而终止退出,则可以用void perror (const char *msg)函数来打印上一步系统调用接口失败的原因。

    const char* strerror (int errno) 函数可根据错误编号返回对应的字符串错误原因。

    在pcb中有个全局变量int errno 用于记录每个系统调用接口情况,在执行返回时都会重置errno,返回0则是成功的,返回其他值则异常。所以可以用<string.h>中的extern int errno 来查看这个全局变量。

    说到这里再额外提一下exit和_exit的区别:一个是库函数void exit(int reval),一个是系统调用接口void _exit(int reval)。这两的作用都是让进程退出,不同点在于exit会自动刷新缓冲区,而_exit不会刷新,这就导致在_exit之后的,本来该打印在终端的数据没有被打印。(打印数据时计算机并不是一个一个打印的,而是先将数据放入缓冲区,等到刷新缓冲区时再把里面的数据打印到终端,我们熟悉的\n换行就是刷新缓冲区的一种方式)。

  int main(){
     printf("你好\n");
     printf("Hellow");                                                                           
     _exit(0);
  }

 可以看到“你好”被打印出来了,但Hellow没有,因为_exit不会刷新缓冲区,\n会刷新缓冲区。

    一些相关指令

    void perror(const char *msg)   用于打印上一步系统调用接口失败的原因

    const char* strerror (int errno)   根据错误编号返回对应的字符串错误原因

    extern int errno   在pcb中有个全局变量errno,每个系统调用接口在执行返回时都会重置errno(错误编号),如果返回0则执行是成功的

进程等待

    父进程创建子进程后等待子进程退出,以获取退出码,及时释放资源,避免出现僵尸进程。

    这里先说一下阻塞和非阻塞:

①阻塞接口:为了完成一个功能而发起一个调用,若调用完成的条件不成立则会一直等待

②非阻塞接口:为了完成一个功能而发起一个调用,若调用完成的条件不成立则会报错

  >    pid_t wait (int *status)是一个阻塞接口,其功能是等待任意一个子进程退出,获取其返回值,释放资源,其中status指针用于存放子进程的返回值,wait函数执行成功时返回子进程的pid,失败则返回-1。此外该函数不仅可以等待任意一个子进程退出,也可以指定某个子进程。

    wait(&status)里的status是用于存放子进程返回值的,而wait函数自身的返回值则是成功时返回子进程的pid,失败返回-1;

    >    pid_t waitpid (pid_t pid,int *status,int options) 该接口即可阻塞也可非阻塞,如果是非阻塞则通常配合循环进行操作,当条件不成立时会先去执行其他操作,直到条件成立或循环退出。

    waitpid函数里的pid如果大于0则表示等待指定的子进程退出,pid = -1 则表示等待任意子进程退出。

 int main(){
    pid_t pid=fork();
    int count=0;
    int status=-1;
    if(pid<0){
      printf("fork failed!\n");
      exit(1);
    }else if(pid==0){
      printf("This is a child process!    pid=%d\n",getpid());
      
      for (int i=0;i<5;++i){
        count++;
        sleep(1);                                                                               
        printf("count=%d\n",count);
      }
      exit(1);//非0表示异常退出
    }else{
      printf("This is a parent process!\n");
      wait(&status);//父进程执行到此会阻塞自己
    }
    
    printf("child process' exit status is %d\n",WEXITSTATUS(status));
    return 0;
  }

     status的构成:    如何判断是否正常退出

    需要取出status的低7位,操作: status & 0x7f (0x7f就是16进制的0111 1111)

    取出退出码

    操作:(status >> 8) & 0xff (status>>8 是先将status左移8位,然后再与16进制的1111 1111相与)

   

程序替换

    pcb是描述程序的运行过程,创建子进程是为了完成一个任务,这个任务可以是1️⃣与父进程做相同的任务,分担压力    2️⃣完成另一个任务。

    如果是把另一个任务放在当前代码中,则代码会变得庞大,模块化不好,所以采用程序替换让子进程pcb管理调度这个程序,也就是替换掉一个pcb所描述的要调度管理的程序,把它替换为另一个(修改映射关系)

 

    当程序替换成功后,就不会再运行原程序的剩下部分,而是直接运行新的程序,新程序运行完后直接退出进程。

    相关函数:

    int execve (char* path ,char* argv[],char* env[])   程序替换成功时没有返回值,失败会返回-1。特点:需要手动给路径以及环境变量

    execv (char* path ,char* argv[])    特点:需要给路径,不需要手动给环境变量,函数会使用默认的环境变量。

    execvp (char *file,char* argv[])  特点:不需要给路径,只需要给文件名,函数会在默认的路径下寻找该文件,不需要手动给环境变量,使用默认的环境变量。

    execl (char* path, char* arg)   其中char* arg 可以用...代替,C语言中...表示不定参数,即参数个数不定

    execlp(char* file, ...)    特点:不需要给路径,不需要手动给环境变量,使用默认的环境变量。

    execle(char* path, ..., char *env[])    特点:需要给路径,不需要手动给环境变量,函数会使用默认的环境变量。

 int main(){
     pid_t pid;
     pid=fork();
     if(pid<0){
      printf("fork failed!\n");
      exit(-1);
    }else if(pid==0){
      printf("This is a child process!    pid=%d",getpid());
      execlp("ls","ls","-a","-l",NULL);//将程序替换为文件名为ls的程序                                                       
      printf("程序替换失败!\n");
      exit(0);
    }else{
      printf("This is a parent process!    pid=%d",getpid());
      sleep(3);
    }
    return 0;
  }

 可以看到,以上程序已被替换为linux自带的ls程序了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值