Linux-进程控制(进程创建、进程终止、进程等待)

一、进程创建

1.1 fork函数介绍

        在命令行下我们可以通过 ./ + exe文件 来创建一个进程,通过fork函数,我们可以通过代码的形式从一个进程中创建一个进程,新进程为子进程,原进程为父进程,子进程在创建时,会与父进程共享下面的代码与数据,当数据被修改时,会采用写实拷贝的方式保证进程间的独立性。

//头文件
#include<unistd.h>

//函数
pid_t fork()

//返回值
 #子进程中返回0
 #父进程返回子进程id
 #出错返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

fork函数常规用法:

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请  求,生成子进程来处理请求。
  • 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数

fork函数失败原因:

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制
     
1.2 使用举例
  #include<unistd.h>
  #include<stdio.h>
  #include<stdlib.h>
  int main()
  {
    //父进程
    printf("I am parent process ppid:%d pid:%d\n",getppid(),getpid());
    //创建子进程
    pid_t id=fork();
    //根据id的值分流,使父子进程执行不同的代码
    if(id == -1)
    {
      exit(-1);
    }
    else if(id == 0)
    {
      //子进程
       printf("I am child process ppid:%d pid:%d\n",getppid(),getpid());
    }
    else
    {                                                                                                                                                                                          
      //父进程
       printf("I am parent process ppid:%d pid:%d\n",getppid(),getpid());
    }
    return 0;
  }

二、进程终止

2.1 进程退出场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码没有运行完,发现异常,提前终止

 2.2 错误码与信号码

错误码
  • 在main函数中,我们通常会return 0,return -1等等,那这些数字有什么意义吗?
  //strerror可以查看错误码信息
  #include<unistd.h>
  #include<stdio.h>
  #include<stdlib.h>
  #include<string.h>                                                                                                                                                                           
  int main()
  {
    int i=0;
    for(i=0;i<255;i++)
    {
      printf("%d:%s\n",i,strerror(i));
    }
    return 0;
  }

可以看到,不同的数字拥有不同的含义,0表示成功,非0表示错误,当程序执行完后,操作系统会检测到错误,并将错误信息打印出来,反馈给用户,这样就可以通过查看错误码来确定程序终止的情况,例如:

ps:操作系统中存在一个变量,名字就叫 ,它保存了最近一次进程的错误码,可以通过

echo $? 查看

信号码
  • 模拟野指针异常:
  
   int main()  
  {  
    int* p=NULL;
    *p=1;
    retrun 0;                                                                                                                                                                                  
  }  

  • 通过kill -11 给进程发信号

通过上述两个例子可以看出,异常终止也是因为OS给进程发了信号,让进程终止的

可以通过kill -l 查看所有信号

总结:

确认进程终止情况的方法:

1.先判断是否是异常导致

2.是异常通过查看信号码确定异常信息

3.不是异常直接看错误码确定错误信息即可

三、进程等待

3.1 进程等待的必要性
  • 子进程退出后,若父进程不进行管理,就会造成僵尸进程,会导致内存泄露问题
  • 父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.2 进程等待方法
3.2.1 wait
#include<sys/types.h>
#include<sys/wait.h>

//等待任一个子进程
pid_t wait(int*status);

#返回值:
成功返回被等待进程pid,失败返回-1。

#参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
      {
          printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt    );
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();                                                                       
          printf("child quit ...\n");
          exit(0);
      }
      // fahter
     pid_t rid = wait(NULL);
     if(rid>0)
     {
       printf("wait success pid:%d\n",rid);
     }
     return 0;
  }
3.2.2 waitpid
//头文件: 
#include<sys/type.h>
#include<sys/wait.h>

pid_t waitpid(pid_t pid,int *status,int options)

#返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

#参数:
pid:

  • Pid=-1,等待任一个子进程。与wait等效。
  • Pid>0.等待其进程ID与pid相等的子进程。

status:

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

ps:WIFEXITED 与 WEXITSTATUS是两个宏

options:
WNOHANG(非阻塞等待): 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

  • pid = 0 ,子进程还没有退出,需要下一次再检测
  • pid > 0 ,  等待成功了,子进程退出,并且父进程回收成功
  • pid < 0 ,  等待失败
3.3 获取子进程status

        父进程要获取子进程的退出信息最重要的就是错误码与信号码,而进程PCB中会存在两个整形变量,用于保存错误码与信号码信息,操作系统可以根据wait与waitpid中status ,将子进程的信息传递给父进程

status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):

  • 0-7位表示子进程的信号码(可让 status & 0x7F获取)
  • 8-15位表示子进程的错误码 (可让 (status >>8) & 0xFF获取 )

通过位运算获取错误码:

 
  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
       {
          printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt    );
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();
          printf("child quit ...\n");
          exit(123);
      }
      sleep(7);
       // fahter
      //pid_t rid = wait(NULL);                                                             
      int status = 0;
      pid_t rid = waitpid(id, &status, 0);
      if(rid > 0)
      {
          printf("wait success, rid: %d\n", rid);
      }
      else
      {
  printf("wait failed !\n");
      }
      sleep(3);
      printf("father quit, status: %d, child quit code : %d, child quit signal: %d\n", statu    s, (status>>8)&0xFF, status & 0x7F);
  }

通过宏获取错误码:

  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
       { 
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();
          printf("child quit ...\n");
          exit(123);
      }
      sleep(7);
      // fahter
      //pid_t rid = wait(NULL);
      int status = 0;
      pid_t rid = waitpid(id, &status, 0);
      if(rid > 0)
      {                                                                                                                                                                                        
        //判断子进程是否是因为异常退出
        if(WIFEXITED(status))
        {
          printf("wait success, child exit code:%d\n", WEXITSTATUS(status));//获取子进程退出码:123      
        }
}
        else
        {
          printf("child process quit unnormal!\n");
        }
      }
      else
      {
          printf("wait failed !\n");
      }
      sleep(3);
      printf("father quit, status: %d, child quit code : %d, child quit signal: %d\n", status, (status>>8)&0xFF, status & 0x7F);
  }

 

3.4 非阻塞等待 

        上述例子都是子进程正常退出的情况,属于阻塞等待,若子进程陷入死循环永远不退出,那么父进程就会一直待在waitpid函数体中,等待子进程退出,这段时间父进程什么事情也不能做,若给waitpid函数的options参数传入WNOHANG的话,此时等待就会变成非阻塞等待,父进程在等待子进程退出期间也可以做相关操作

  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void DoanotherThing()
  {
    printf("I am doing anotherthing\n");
  }
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
      {
          printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();
          printf("child quit ...\n");
          exit(123);
      }
      // fahter
      while(1)                                                                                                                                                                                 
      {
        int status = 0;
        pid_t rid = waitpid(id, &status, WNOHANG);
       if(rid==0)
      {
 usleep(10000);
        printf("child is running ,check next time!\n");
        DoanotherThing();
      }
        else if(rid > 0)
      {
        //判断子进程是否是因为异常退出
        if(WIFEXITED(status))
        {
          printf("wait success, child exit code:%d\n", WEXITSTATUS(status));//获取子进程退出码:123      
        }
        else
        {
          printf("child process quit unnormal!\n");
        }
        break;
      }
      else
      {
          printf("wait failed !\n");
          break;
      }
  
     }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/488721.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件项目-详细设计说明书范文参考(46页Word原件)

一、 关于本文档 &#xff08;一&#xff09; 编写目的 &#xff08;二&#xff09; 预期读者 二、 项目概要 &#xff08;一&#xff09; 建设背景 &#xff08;二&#xff09; 建设目标 &#xff08;三&#xff09; 建设内容 三、 总体设计 &#xff08;一&#xff0…

git基础-撤销操作

撤销操作 在任何阶段&#xff0c;我们都可能希望撤消某些操作。在这里&#xff0c;我们将回顾一些基本工具&#xff0c;用于撤消之前所做的更改。操作要谨慎&#xff0c;因为这些撤销可能无法修复。这是 Git 中为数不多的几个领域之一由于操作不当&#xff0c;导致丢失一些工作…

一篇文章,告别Flutter状态管理争论,问题和解决

起因 每隔一段时间&#xff0c;都会出现一个新的状态管理框架&#xff0c;最近在YouTube上也发现了有人在推signals, 一个起源于React的状态管理框架&#xff0c;人们总是乐此不疲的发明各种好用或者为了解决特定问题而产生的方案&#xff0c;比如Bloc, 工具会推陈出新&#x…

JavaParser 手动安装和配置

目录 前言 一、安装 Maven 工具 1.1 Maven 软件的下载 1.2 Maven 软件的安装 1.3 Maven 环境变量配置 1.4 通过命令检查 Maven 版本 二、配置 Maven 仓库 2.1 修改仓库目录 2.2 添加国内镜像 三、从 Github 下载 JavaParser 3.1 下载并解压 JavaParser 3.2 从路径打…

手摸手教你安装使用nvm(简单明了)

1.nvm定义 &#xff08;node.js version management&#xff09; nvm是node版本管理工具&#xff0c;通过nvm可以安装和切换不同版本的node.js 2.卸载之前安装的node 打开系统的控制面板&#xff0c;点击卸载程序&#xff0c;卸载nodejs 提示&#xff1a;如果你没有安装过…

【使用postman发送post请求】

1&#xff09;post http://ip:8090/version?appVersion1.0.0&channelgoogle&platformandroid&deviceId90991c4465e1886a81b00dac855fe098&notice1 这样子选择json格式提交数据&#xff0c;可读性强 好处&#xff1a; 1.最大的好处莫过于我可以记录下来曾经做…

使用easyYapi生成文档

easyYapi生成文档 背景1.安装配置1.1 介绍1.2 安装1.3 配置1.3.1 Export Postman1.3.2 Export Yapi1.3.3 Export Markdown1.3.4 Export Api1.3.6 常见问题补充 2. java注释规范2.1 接口注释规范2.2 出入参注释规范 3. 特定化支持3.1 必填校验3.2 忽略导出3.3 返回不一致3.4 设置…

智慧医疗包括哪些方面?智慧医疗发展前景如何?

近年来&#xff0c;随着云计算、物联网&#xff08;internet of things&#xff0c;IOT&#xff09;、移动互联网、大数据、人工智能&#xff08;artificial intelligence&#xff0c;AI&#xff09;、5G网络、区块链等新一代信息技术的逐步成熟和广泛应用&#xff0c;信息化已…

HTML(二)

一、表格标签 1.1表格的主要作用 表格主要用于显示、展示数据&#xff0c;因为它可以让数据显示的非常的规整&#xff0c;可读性非常好。特别是后台展示数据的时候&#xff0c;能够熟练运用表格就显得很重要。一个清爽简约的表格能够把繁杂的数据表现得很有条理。 1.2 表格的…

外地报医保怎么备案?异地就医备案流程是什么?

外地医疗保险如何办理&#xff1f; 外地医疗保险备案的具体流程可能因地区和医疗保险政策的不同而有所不同&#xff0c;但一般来说&#xff0c;可以通过以下方式进行备案&#xff1a; 1、网上备案&#xff1a;不少地区已经推出网上备案服务&#xff0c;可以通过当地医保局官网…

SQLServer SEQUENCE用法

SEQUENCE&#xff1a;数据库中的序列生成器 在数据库管理中&#xff0c;经常需要生成唯一且递增的数值序列&#xff0c;用于作为主键或其他需要唯一标识的列的值。为了实现这一功能&#xff0c;SQL Server 引入了 SEQUENCE 对象。SEQUENCE 是一个独立的数据库对象&#xff0c;用…

高等数学基础篇(数二)之微分方程(高阶线性微分方程)

高阶线性微分方程&#xff1a; 1.线性微分方程的解的结构 2.常系数齐次线性微分方程 3.常系数非齐次线性微分方程 4.欧拉方程 5.差分方程 目录 1.线性微分方程的解的结构 2.常系数齐次线性微分方程 3.常系数非齐次线性微分方程 4.欧拉方程 5.差分方程 1.线性微分方程…

【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C语言》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、红黑树的概念二、红黑树的模拟实现2.1 结点2.2 成员变量2.3 插入情况一&#xff1a;uncle在左&#xff…

C++ STL- list 的使用以及练习

目录 0.引言 1. list 介绍 2. list 使用 2.1 构造函数 2.2 list iterator 的使用 3 list capacity 4. list element access 5. list modifiers 6. list 迭代器失效 7. list 与vector 对vector 8. OJ 题讲解 删除链表的倒数第 N 个节点&#xff1a; 0.引言 …

tcp/ip是什么意思,tcp/ip协议包含哪几层

TCP/IP是一种网络通信协议&#xff0c;它是互联网所采用的基本协议。TCP/IP协议是由美国国防部高级研究计划局&#xff08;ARPA&#xff09;在上世纪70年代设计开发的&#xff0c;经过多年发展和完善&#xff0c;已成为全球范围内最重要的网络通信协议之一。 首先&#xff0c;让…

python能做什么

python能做什么 Web开发&#xff1a;Python具有许多流行的Web框架&#xff0c;如Django和Flask&#xff0c;使得它成为Web开发的首选语言。它简洁、易于学习、且拥有丰富的生态系统&#xff0c;能够快速构建高性能的Web应用。 数据科学和机器学习&#xff1a;Python在数据科学…

Luminar Neo:重塑图像编辑新纪元,Mac与Win双平台畅享创意之旅

在数字时代的浪潮中&#xff0c;图像编辑软件已成为摄影师和设计师们不可或缺的创作工具。Luminar Neo&#xff0c;作为一款专为Mac与Windows双平台打造的图像编辑软件&#xff0c;正以其卓越的性能和创新的编辑功能&#xff0c;引领着图像编辑的新潮流。 Luminar Neo不仅继承…

Vue3更新Package.json版本号

由于我之前已经更新过了&#xff0c;下面的方法提示我已经是最新的了&#xff0c;记录一下&#xff0c;过段时间在测试一下 npm install -g vue/clivue upgrade

Python算法100例-4.2 列出真分数序列

完整源代码项目地址&#xff0c;关注博主私信源代码后可获取 1.问题描述2.问题分析3.算法设计4.确定程序框架5.完整的程序6.拓展训练 1&#xff0e;问题描述 按递增顺序依次列出所有分母为40、分子小于40的最简分数。 2&#xff0e;问题分析 分子和分母只有公因数1的分数&…

《手把手教你》系列技巧篇(五十四)-java+ selenium自动化测试-上传文件-中篇(详细教程)

1.简介 在实际工作中&#xff0c;我们进行web自动化的时候&#xff0c;文件上传是很常见的操作&#xff0c;例如上传用户头像&#xff0c;上传身份证信息等。所以宏哥打算按上传文件的分类对其进行一下讲解和分享。 2.为什么selenium没有提供API&#xff1f; 想必小伙伴们或者…