【Linux】进程控制的详细介绍

前言
在此之前,我们学过进程的概念,进程的状态,进程地址空间等一系列进程相关的问题。本章我们继续学习进程,我们要来学习一下进程的控制,关于进程等待,进程替换等问题。

目录

    • 1.再次认识Fork函数
      • 1.1 fork()之后操作系统做了什么
    • 2.进程终止
      • 2. 1 进程终止的常见方式:
      • 2. 2 进程退出常见操作方法:
    • 3 .进程等待
      • 3.1 进程等待的必要性:
      • 3.2 进程等待的方法
      • 3.3 获取子进程status:
      • 3.4 阻塞等待:
      • 3.5 非阻塞等待:
    • 4.makefile的新增知识点:

1.再次认识Fork函数

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程

  • fork之前父进程独立执行,fork之后,父子两个执行流分别执行

  • fork之前只有父进程执行

  • fork之后父子进程代码共享

  • 进程具有独立性,代码和数据必须独立的

  • 因为代码只能读取,所以就不会有人写入,更不会发生写实拷贝,所以这是父子进程代码共享的原因。

  • 通常父子代码共享,父子在不写入时,数据也是共享的

  • 当任意一方试图写入,便以写时拷贝的方式各自一份副本

  • fork之后,是否只有fork之后的代码是被父子进程共享的?

fork之后,父子共享所有的代码!只不过子进程只能从这里(fork)开始执行。
fork之后,父子进程谁先运行并不能给答案,是调度器给答案

1.1 fork()之后操作系统做了什么

  • 进程调用fork,当控制转移到内核中的fork代码后,内核做
  • 分配新的内存块和内核数据(task_ struct)结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度
  • 进程 = 内核的进程数据结构 + 进程的代码和数据

创建子进程的内核数据结构(struct task_struct + struct mm_struct + 页表) + 代码继承父进程,数据以写时拷贝的方式,来进行共享或者独立。

fork函数返回值

  • 子进程返回0,
  • 父进程返回的是子进程的pid

写时拷贝

  • 写时拷贝是一种延迟拷贝的策略~
  • 写时拷贝本身就是有OS的内存管理模块完成的!

2.进程终止

进程终止的时候,操作系统做了什么?

1.进程终止的时候,首先会进入Z状态,父进程会去等待它回收子进程的信息,读取退去时的一些信息,然后将进程设置为X状态,这时才真正的退出,释放内核结构,释放曾静进程加载到内存所对应的到吗和数据。
2. 当然要释放进程申请的相关内核数据结构和对应的数据和代码(本质就是释放系统资源)

2. 1 进程终止的常见方式:

  • 正常退出:
    a.代码跑完,结果正确 b.代码跑完,结果不正确
    main函数的返回值意义是什么?
答:return的意义是返回给上一级进程,用来评判该进程执
行结果用的。(main函数崩溃,返回值就没意义了)
  • 异常退出:
    代码没有跑完,程序崩溃了(信号部分coredump,涉及到一点点)

2. 2 进程退出常见操作方法:

  • 正常退出:(可以通过echo $? 查看进程退出码
    1.从main函数 Return返回
    2.调用exit( n)
    3.调用_exit

注意:在函数内部中,使用Return() 代表返回函数调用。
exit在代码任何地方调用,都表示直接终止进程。
main函数的返回值,是进程退出码(strerror())

  • 异常退出
    1.Ctrl+C :信号终止

程序崩溃的时候,退出码没有意义了(异常退出)
在这里插入图片描述

3 .进程等待

3.1 进程等待的必要性:

1.之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。。
2. 进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。。
3. 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。。
4.父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。。

3.2 进程等待的方法

  • wait方法
 #include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main()
{pid_t id = fork();if(id == 0){//子进程创建成功while(1){printf("我是子进程, 我正在运行...Pid: %d\n", getpid());sleep(1);}}else{//父进程//想看到等待成功之后僵尸进程就不见了printf("我是父进程:pid:%d, 我准备电脑等待子进程啦\n", getpid());sleep(30);pid_t ret = wait(NULL);if(ret < 0){printf("等待失败!\n");}else {printf("等待成功: result: %d\n", ret);}//父进程等20后再退出sleep(20);}
}
  • wait()的方案可以解决回收子进程Z状态,让子进程进入X

  • waitpid()方法

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

返回值:
在这里插入图片描述

  • 返回值大于0:等待子进程成功,返回值就是子进程的进程ID pid
  • 返回值小于0:等待失败
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在

参数:
Pid:

  • pid值大于0:是几就代表等待哪一个子进程, 指定等待
  • pid值等于-1:等待任意进程

status:

  • 是一个输出型参数
  • 通过调用 WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

子进程会将自己的退出信息写入task_ struct~~
子进程一旦死掉,父进程直接把子进程退出码拷贝到自己(通过waitpid传进来的int status参数,父进程就拿到了子进程的退出结果)*

options:

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

3.3 获取子进程status:

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息.
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程.

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

在这里插入图片描述
次低八位得到子进程的退出码,最低七位是终止信号。

3.4 阻塞等待:

如果子进程还在运行,则父进程会被阻塞等待,直到子进程退出或被终止,才能继续执行下去。

阻塞等待,父进程等的时候,子进程压根没退出的,父进程只能阻塞式的等,只有等到子进程退出之后才能正式拿出来这里的int* status。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main()
{pid_t id = fork();if(id == 0){//子进程创建成功while(1){printf("我是子进程, 我正在运行...Pid: %d\n", getpid());sleep(1);int* p = NULL;*p = 100;}}else{//父进程int status = 0;printf("我是父进程:pid:%d, 我准备电脑等待子进程啦\n", getpid());pid_t ret = waitpid(id, &status, 0);if(ret > 0){//status >> 8 并不影响status的值//status >>= 8 才影响status的值// printf("wait success, ret : %d, 我所等待子进程的退出码: %d, 退出信号是: %d\n", ret, (status >> 8) & 0xFF, status & 0x7F);if(WIFEXITED(status)){//子进程是正常退出的printf("子进程执行完毕,字进程的退出码:%d\n",WEXITSTATUS(status));}else{ printf("子进程异常退出:%d\n",WIFEXITED(status));}}}return 0;
}

我们只需要对status进行位操作,就能拿到对应的退出码和退出信号。

我们很显然做了一个对空指针的解引用:

我们能看到退出信号是11号信号,我们来看一下11号信号是什么:
在这里插入图片描述
SIGSEGV的全称是Segmentation Violation,即”段错误”。

3.5 非阻塞等待:

与阻塞等待不同的是,非阻塞等待不是子进程不退出就一直在那里等,而是多次调用非阻塞接口轮询检测!

非阻塞等待时,条件不满足会直接就返回,此时用户不会因为调用了waitpid ()
,让自己阻塞住,一旦条件不满足该函数会立刻返回,父进程或者用户可以继续在返回之后空闲时间段内,做自己的事,做一会之后再去调用waitpid(),这种监测方式叫做非阻塞。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>//非阻塞等待验证
int main()
{pid_t id = fork();if(id == 0){//子进程 //死循环跑不完,但是代码出现异常了,进程收到信号,信号终止了子进程,父进程就要知道while(1){printf("我是子进程,我的PID : %d, 我的PPID : %d\n", getpid(), getppid());sleep(3);}exit(111);}else if(id > 0){//父进程//基于非阻塞的轮询等待方案int status = 0;while(1){pid_t ret = waitpid(-1, &status, WNOHANG);if(ret > 0){   printf("等待成功, %d, exit sig: %d, exit code: %d\n", ret, status & 0x007F, (status & 0xFF00) >> 8);break;}else if(ret == 0){           //等待成功了,但是子进程没有退出 -- 函数调用成功了,只不过是在非阻塞状态printf("子进程好了没,还没,那么父进程就做其他事情...\n");sleep(1);}else {//出错了,暂时不处理}}}else {//do nothing         }return 0;
}
  • 轮训检测:
    在非阻塞等待的时候,子进程还没退出,父进程虽然是在等子进程,但是不是卡住在那里等待,而是可以做其他的事情

补充知识

  • 阻塞的本质是进程阻塞,把进程阻塞是要改进程状态的,R -> S。
  • 把进程的PCB从运行队列放到等待队列,这都是操作系统干的.
  • 把进程的PCB从运行队列放到等待队列,这都是操作系统干的

4.makefile的新增知识点:

在这里插入图片描述
在这里插入图片描述
如果想要生成两个,makefile代码如下:
在这里插入图片描述
尾声
看到这里,相信大家对这个C++有了解了。
如果你感觉这篇博客对你有帮助,不要忘了一键三连哦

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

internet download manager(IDM下载器) 6.42.8.2下载安装使用指南

internet download manager(IDM下载器) 6.42.8.2Z是一款功能强大的下载加速工具&#xff0c;能够显著提升您的下载速度&#xff0c;最高可达500%。它不仅能够加速下载&#xff0c;还能对下载任务进行智能调度&#xff0c;并具备恢复中断下载的能力。根据用户评价&#xff0c;无…

初识C++(命名空间、缺省参数)

初识C 命名空间namespace关键字命名空间的使用 缺省参数 命名空间 namespace关键字 在C中&#xff0c;为了尽可能避免命名冲突&#xff0c;需要对各个变量进行域作用限定&#xff0c;这就需要使用到namespace关键字&#xff0c;namespace可以定义一个命名空间&#xff0c;即命…

LabVIEW红外热波图像缺陷检

开发使用LabVIEW开发的红外热波图像缺陷检测系统。该系统结合红外热像仪、工业相机和高效的数据采集硬件&#xff0c;实现对工件表面缺陷的自动检测和分析。通过LabVIEW的强大功能&#xff0c;系统能够实时采集、处理和显示红外热波图像&#xff0c;有效提高了检测的精度和效率…

vue:标签属性绑定Vue实例【ref,reactive,内置指令v-bind,v-on】,预定义变量、方法【$methods,$computed】

Vue2、3组件通信、双向绑定、插槽slot、内置指令_组件双向绑定-CSDN博客​Vue2&#xff0c;3响应式原理&#xff0c;ref和reactive&#xff0c;toRef和toRefs&#xff0c;shallowRef和shallowRefs_vue2 shallowref-CSDN博客 vue2【Options 选项API、mixin混入】&#xff0c;vu…

WAF基础介绍

WAF 一、WAF是什么&#xff1f;WAF能够做什么 二 waf的部署三、WAF的工作原理 一、WAF是什么&#xff1f; WAF的全称是&#xff08;Web Application Firewall&#xff09;即Web应用防火墙&#xff0c;简称WAF。 国际上公认的一种说法是&#xff1a;Web应用防火墙是通过执行一…

免开steam 脱离steam 进行游戏的小工具

链接&#xff1a;https://pan.baidu.com/s/1k2C8b4jEqKIGLtLZp8YCgA?pwd6666 提取码&#xff1a;6666 我们只需选择游戏根目录 然后输入AppID 点击底部按钮 进行就可以了 关于AppID在&#xff1a;

机器学习——L1 L2 范数 —>L1 L2正则化

1、L1范数和L2范数是机器学习和数据分析中经常使用的两种范数&#xff0c;它们之间存在多个方面的区别。 以下是关于L1范数和L2范数区别的详细解释&#xff1a; 一、定义差异 L1范数&#xff1a;也被称为曼哈顿范数&#xff0c;是向量元素的绝对值之和。对于一个n维向量x&am…

酒店管理系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;酒店管理员管理&#xff0c;房间类型管理&#xff0c;房间信息管理&#xff0c;订单信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;房间信息…

Linux介绍与常用命令详解

目录 一、Linux概述 1.Linux发行版 2.Linux目录结构 二、Linux特点 三、Linux用途 四、Linux常用的命令 1.cd指令&#xff08;跳转位置&#xff09; 2.显示目录文件 3.对文件进行操作 4.rm指令&#xff08;删除文件夹指令&#xff09; 5.mv指令 6.查看文件命令 7.进程命令…

【云岚到家】-day05-6-项目迁移-门户-CMS

【云岚到家】-day05-6-项目迁移-门户-CMS 4 项目迁移-门户4.1 迁移目标4.2 能力基础4.2.1 缓存方案设计与应用能力4.2.2 静态化技术应用能力 4.3 需求分析4.3.1 界面原型 4.4 系统设计4.4.1 表设计4.4.2 接口与方案4.4.2.1 首页信息查询接口4.4.3.1 数据缓存方案4.4.3.2 页面静…

Ubuntu20.04 编译安装FFmpeg,出错分析以及解决方案

最近工程上需要对FFmpeg底层源码进行修改&#xff0c;需要重新编译&#xff0c;遇见不少坑&#xff0c;出篇教程记录一下。 文章目录 1.FFmpeg源码下载地址2.编译环境配置3.编译FFmpeg4.配置FFmpeg运行环境 1.FFmpeg源码下载地址 官方下载地址:Index of /releases (ffmpeg.or…

Java | Leetcode Java题解之第232题用栈实现队列

题目&#xff1a; 题解&#xff1a; class MyQueue {Deque<Integer> inStack;Deque<Integer> outStack;public MyQueue() {inStack new ArrayDeque<Integer>();outStack new ArrayDeque<Integer>();}public void push(int x) {inStack.push(x);}pub…

springboot1——快速构建项目

需求 第一步&#xff1a;创建maven工程(非web项目) 第二步&#xff1a;导入起步依赖 点击&#xff1a; 下拉复制&#xff1a; 粘贴&#xff1a;&#xff01;&#xff01;这是springboot工程需要继承的父工程 下拉复制&#xff1a; 粘贴&#xff1a;&#xff01;&#xf…

Nodejs 第八十章(Kafka高级)

kafka前置知识在前几章章讲过了 不再复述 Kafka集群操作 1.创建多个kafka服务 拷贝一份kafka完整目录改名为kafka2 修改配置文件 kafka2/config/server.properties 这个文件 broker.id1 //唯一broker port9093 //切换端口 listenersPLAINTEXT://:9093 //切换监听源启动zooKe…

常见问题记录(持续更新)

备注&#xff1a; 在7月10日记录之前遇到的问题及解决方法: 一&#xff1a;常见的访问问题&#xff1a; 403 Forbidden&#xff1a;&#xff08;未有请求权限&#xff09; 表示服务器理解请求但是拒绝执行它。这通常是由于服务器上的文件或资源没有正确的读、写或执行权限&…

python接口自动化(二十四)--unittest断言——中(详解)

1.简介 上一篇通过简单的案例给小伙伴们介绍了一下unittest断言&#xff0c;这篇我们将通过结合和围绕实际的工作来进行unittest的断言。这里以获取城市天气预报的接口为例&#xff0c;设计了 2 个用例&#xff0c;一个是查询北京的天气&#xff0c;一个是查询 南京为例&#…

[MySQL][数据类型]详细讲解

目录 1.说明1.数据类型分类2.数值类型1.int类型2.bit类型3.浮点数类型1.float2.decimal 3.字符串类型1.char2.varchar3.char和varchar比较 4.日期和时间类型5.enum和set1.基本语法2.查询数据 1.说明 MySQL表中建立属性列&#xff0c;列名称 类型 num tinyint unsigned;与C/C语…

优化Cocos Creator 包体体积

优化Cocos Creator 包体体积 引言一、优化图片文件体积&#xff1a;二、优化声音文件体积&#xff1a;三、优化引擎代码体积&#xff1a;四、 优化字体字库文件大小&#xff1a; 引言 优化Cocos Creator项目的包体体积是一个常见且重要的任务&#xff0c;尤其是在移动设备和网…

【Flowable | 第二篇】使用Flowable实现请假流程

文章目录 3.使用Flowable实现请假流程3.1部署测试环境3.2工作流/我的任务菜单3.3设计请假流程3.4绑定业务表3.5申请请假3.6节点审批人审批3.6.1审批动作3.6.2流程状态 3.使用Flowable实现请假流程 3.1部署测试环境 我们使用开源的若依框架&#xff0c;来实现Flowable请假流程…

17102 “一条路径图”的最大独立集问题

这个问题可以通过动态规划来解决。我们可以定义一个数组f&#xff0c;其中f[i]表示从最左边的节点沿着一条路径到节点vi为止&#xff0c;具有的独立集最大权值之和。然后我们可以通过比较f[i-1]&#xff0c;f[i-2]w[i]和w[i]的值&#xff0c;来更新f[i]。 以下是解题步骤&…