【Linux进程篇】父子进程fork函数||进程生死轮回状态||僵尸进程与孤儿进程

W...Y的主页 😊

代码仓库分享💕


前言:上篇文章中我们认识了进程,可执行程序在内存中加载运行被称作进程,而操作系统是通过给每一个可执行程序创建一个PCB来管理进程的。并且学习了一些查看进程的指令,认识了进程中的PID与PPID分别代表一个进程的唯一标识ID与他的父进程的ID,今天我们继续了解进程中的属性,探索其中的规则。

目录

通过系统调用创建进程-fork初识

进程状态

进程排队

Linux内核源代码

运行状态 

阻塞状态

挂起状态

各个状态的情况

Z(zombie)-僵尸进程

僵尸进程危害

孤儿进程


通过系统调用创建进程-fork初识

当我们执行一个可执行文件时,就相当于创建了一个进程,我们也可以在一个可执行程序中进行创建一个进程。通过对fork函数的理解,我们就可以实现。

我们可以在Linux下使用man fork来查看其中的内容:

#include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int main()
  5 {
  6    printf("before fork: I am a prcess, pid: %d, ppid: %d\n", getpid(), getppid());
  7    fork();
  8    printf("after fork: I am a prcess, pid: %d, ppid: %d\n", getpid(), getppid());                                                                    
  9    return 0;
 10 }

 我们创建一个可执行程序后使用fork来运行,我们可以看到:

在fork直线printf只打印一遍,而fork之后却打印了两遍。说明在fork之前只有一个执行分支,而fork之后就有两个执行分支,因为创建了子进程。 我们通过打印的结果看来after fork中第三行的ppid是第二行的pid,说明fork创建了自己的子进程。fork之后代码共享!!!

fork有自己的返回值,pid_t:

如果fork成功父进程返回子进程的pid,子进程返回0。我们知道返回值后就可以操作父子进程,各做各的事情了,我们让其死循环打印,每隔一秒打印一次:

int main()
 14 {
 15   pid_t id = fork();
 16   if(id == 0)
 17   {
 18     //子进程
 19     while(1)
 20     {
 21       printf("child fork : I am process, pid: %d, ppid: %d\n", getpid(), getppid());
 22       sleep(1);
 23     }
 24   }
 25   else{
 26     //父进程
 27     while(1)
 28     {
 29       printf("parent fork : I am process, pid: %d, ppid: %d\n", getpid(), getppid());
 30       sleep(1);                                                                                                                                      
 31     }
 32   }
 33   return 0;
 34 }

以前在vs中写代码时,当程序进入死循环后不可能继续出来到下一个死循环中去,而在父子进程下却可以两个死循环同时进行!!! 

操作系统会在创建好的可执行程序后构建一个task_struct,子进程也会创建一个task_struct,但是子进程会指向父进程的代码块与数据区中,共享同一块数据!!!父进程会将自己的task_struct中的大部分属性拷贝给子进程,所以子进程才能和父进程使用同一块数据与代码。

那很多人就会有疑问,为什么fork函数有两个返回值,一般函数不是只有一个吗?那如果一个函数已经到了return的时候,这个函数的整体逻辑已经完成了,所以我们可以理解为在返回之前就已经有父子两个进程了,所以就会有两个返回值。 

父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)这个我们后面会讲。

我们如何创建多个进程呢?

#include<sys/types.h>
 38 const int num = 10;
 39 
 40 void Worker()
 41 {
 42     int cnt = 12;
 43     while(cnt)
 44     {
 45         printf("child %d is rnning, cnt: %d\n", getpid(), cnt);
 46         cnt--;
 47         sleep(1);
 48     }
 49 }             
 50                  
 51 int main()                                                                                              
 52 {
 53     for(int i = 0; i < num ;i++)
 54     {     
 55         pid_t id = fork();
 56         if(id < 0) break;       
 57         if(id == 0){
 58             //子进程      
 59             Worker();    
 60             exit(0); // C用过, 让子进程直接退出
 61         }           
 62         printf("father create child process success, child pid: %d\n", id);
 63         sleep(1);                              
 64     }    
 65                                                                            
 66     //只有父进程会执行到这里!
 67     sleep(15);
 68 
 69     return 0;
 70 }

我们使用循环创建了10个子进程,上述就是代码。

进程状态

进程排队

说到进程状态,我们先来说一说进程排队的事情。进程 = task_struct + 可执行程序。我们再Windows中使用软件时,当一个软件崩溃后不会影响其他软件,说明进程是一个个独立的,当我们写一个死循环程序时,别的程序也可以正常运行,证明进程不是一直在运行的,即使在CPU中也一样。因为计算机中有个时间片的概念,比如时间片规定为1ms,当一个程序在CPU上已经运行了1ms后就会被剥离下来,让后面的程序占用CPU,这就是为什么死循环也不会影响其他程序!!!

 

当我们运行这个程序时,在scanf时就需要我们键盘输入,但是我们不输入时就会一直等待。所以通过这个用例我们就可以知道在等待某种资源。

进程的排队其实不是程序排队,而是对应的task_struct在排队。并且task_struct的管理是用数据结构进行管理的,但是最奇特的是它可以插入多种数据结构。

 在task_struct中存放了多个结构体,结构体里面有两个指针,一个指针指向下一个结构体首,一个指针指向上一个结构体。这样只要task_struct中有多个结构体,我们就可以使用多种数据结构进行匹配。

Linux内核源代码

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))。 
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

 我们看源码可知,所谓的状态本质就是一个整形变量,类似与#define定义。而这些状态就是Linux中多个进程的后续动作,在进程排队时我们就需要这些状态来区分进程。

运行状态 

只要这个程序万无一失,准备就绪,然后他们的PCB就开始在cpu进行排队,我们把这种进程叫做R运行状态。

阻塞状态

什么是阻塞状态呢?

当我们需要输入时,就必须要键盘输入。但是这些硬件都是被操作系统管理起来的。这些硬件管理就犹如cpu管理软件时创建的PCB一样。这些软件也有对应的结构体并且将他们连接起来,这样操作系统才可以更好的维护增删查改。当一个程序需要一款硬件时,其就会从运行状态转变为阻塞状态,并且将自己的PCB与硬件的结构体进行排队连接。

总结:当我们的进程等待某种硬件资源时,资源如果没有准备就绪,我们的进程的task_struct只能先将自己设置为阻塞状态,然后将自己的PCB连入等待的硬件资源队列中。状态的变迁引起的是PCB会被操作系统变迁到不同对列中去!!! 

挂起状态

挂起状态必须要有一个大前提,就是计算机的资源已经非常小了,内存不够了,这时我们的某些进程(比如阻塞的进程,因为这些进程当前不会被cpu调用)为了腾出内存就出现了挂起状态。

当资源被挂起就会将进程的代码以及数据拷贝到磁盘的swap分区中,这个过程我们称作唤出。而当cpu要进行调度此进程时,在从磁盘中将代码与数据拷贝到内存中,这个过程叫做唤入。

各个状态的情况

当我们写入一个死循环:

这时这个程序一直在循环执行这个程序,但是我们通过监视可以看出这个进程的状态为S+。证明这个进程为睡眠状态,因为cpu的执行速度非常的快,我们基本捕捉不到这个进程的运行状态,并且这个程序我们是要往显示器上打印数据,cpu的速度远远高于显示器,所以我们看不到。但是当我们在程序中有一个空语句的循环时,我们就可以看到此进程一直是R+状态。

那后面的+是什么呢?+代表的是前端进程,而没有+代表的是后端进程。当有+时我们的显示器只能用来显示此进程,但是当没有+时,我们既可以看到进程显示的内容,而且可以执行我们的Linux命令。但是前端进程使用ctrl+c是可以终止的,但是后端进程只能使用kill -9 + pid才可以终止!!!

Linux默认执行程序时是前端进程,若想成为后端进程 :./可执行程序  + &。

那我们说的S睡眠状态就是阻塞状态也是可中断睡眠状态。还有一个D状态叫做深度睡眠状态,也叫不可中断睡眠,就是操作系统不能将其终止。

我们可以使用kill -l指令查看我们可以对进程发送的信号:

其中我们最常用的指令信号就是kill -9 + PID,杀死当前进程。而kill -19 + PID就可以使进程状态为T状态,也就是暂停状态:

 我们就可以将S状态转变为T状态。要想在恢复到运行的状态:kill -18 + PID即可。

Z(zombie)-僵尸进程

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(没有使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。

创建一个有僵尸进程的例子:

 我们先创造一个父子进程,然后在五秒后exit(0)直接退出子进程,但是我们使用wait函数来读取子进程的信息,所以子进程就会由S+变为Z+成为僵尸进程。

僵尸进程危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护。
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费。因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!所以内存就会泄露!!!

孤儿进程

什么是孤儿进程呢?顾名思义孤儿进程就是当父进程提前退出而子进程还没退出时,父进程的父亲是bash,所以退出后不会担心变成僵尸进程,但是子进程就没有了父进程,所以就变成了孤儿进程。

那孤儿进程没有了父亲,当进程结束时怎么样回收孤儿进程的各种资源呢?如果没有人回收那么是不是会变成僵尸进程,当孤儿进程太多时,那么僵尸进程就会变多,最终导致资源泄漏系统崩溃呢?我们可以先来看一个程序:

 我们可以看出当父进程提前结束时,子进程会被bash所接管,子进程的父亲变成了bash,所以当子进程结束时会有bash进程帮他回收资源,不会出现僵尸进程!


以上就是本次全部内容,感谢大家观看!!!

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

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

相关文章

Flask 蓝图路由的模块化开发

基于 Flask 蓝图路由的模块化开发 1. 编程目标 为了提高Flask应用的可维护性和可扩展性&#xff0c;我们通过使用Flask的蓝图(Blueprint)功能&#xff0c;可以将不同的功能模块拆分到独立的文件中&#xff0c;方便后续的开发和维护。 2. 项目结构 项目结构树如下&#xff1…

助力企业标准化搭建--图框模板的创建

古有秦皇书同文、车同轨&#xff0c;今各行各业都有国际标准、国家标准&#xff0c;其目的就是为了标准化、统一化&#xff0c;由此可见标准化的重要性&#xff1b;一个企业若是想规范员工的操作&#xff0c;推行标准化也很重要&#xff1b;因此对于需要绘制电气图纸的行业来说…

从0开始学统计-秩和检验

1.什么是秩和检验&#xff1f; 秩和检验&#xff0c;也称为Wilcoxon 秩和检验&#xff0c;是一种非参数统计检验方法&#xff0c;用于比较两个独立样本的中位数是否有显著差异。它不要求数据满足正态分布假设&#xff0c;因此适用于小样本或者数据不满足正态分布假设的情况。 …

2024年怎么下载学浪app视频

想要在2024年紧跟潮流&#xff0c;成为一名优秀的学浪用户吗&#xff1f;今天就让我们一起探索如何下载学浪app视频吧&#xff01; 学浪视频下载工具打包 学浪下载工具打包链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1234 --来自百度网盘超级会员V10的分享…

性能怪兽!香橙派 Kunpeng Pro 开发板深度测评,带你解锁无限可能

性能怪兽&#xff01;香橙派 Kunpeng Pro 开发板深度测评&#xff0c;带你解锁无限可能 文章目录 性能怪兽&#xff01;香橙派 Kunpeng Pro 开发板深度测评&#xff0c;带你解锁无限可能一、背景二、香橙派 Kunpeng Pro 硬件规格概述三、使用准备与系统安装1️⃣、系统安装步骤…

字节面试:百亿级数据存储,怎么设计?只是分库分表吗?

尼恩&#xff1a;百亿级数据存储架构起源 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;经常性的指导小伙伴们改造简历。 经过尼恩的改造之后&#xff0c;很多小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试机会&#xff0c…

cuda 11.6 pytorch安装

在安装之前&#xff0c;需要先配置GPU环境&#xff08;安装CUDA和CudaNN) 命令行输入nvidia-smi&#xff0c;查看驱动信息 nvidia-smi 安装相应的CUDA 和CUDANN 验证&#xff1a;输入nvcc --version 或者nvcc -V 进行检查 nvcc --version nvcc -V 在anaconda里创建环境 con…

彭永东所交“答卷”道尽万般无奈,贝壳找房营收、利润双双锐减

就今年第一季度业绩披露后两日的股价变动来看&#xff0c;贝壳找房&#xff08;下称“贝壳”&#xff09;似乎并未在港股和美股市场取得预期的效果。 港股市场&#xff0c;截至5月24日收盘&#xff0c;贝壳-W&#xff08;HK:02423&#xff09;报收43.9港元/股&#xff0c;当日跌…

海外网红营销新趋势:“快闪式”营销如何迅速提升品牌曝光度

在当今数字化时代&#xff0c;海外网红营销已成为品牌迅速触达全球消费者、提升品牌曝光度和刺激销售的重要手段。其中&#xff0c;“快闪式”营销以其独特的时效性、创意性和互动性&#xff0c;成为品牌与海外网红合作的新趋势。本文Nox聚星将和大家探讨如何利用海外网红的影响…

梭住绿色,植梦WILL来,容声冰箱“节能森林计划”再启航

近日&#xff0c;容声冰箱再度开启了“节能森林计划”绿色公益之旅。 据「TMT星球」了解&#xff0c;此次活动深入到阿拉善荒漠化地带&#xff0c;通过实地考察和亲身体验&#xff0c;见证容声了“节能森林计划”项目的持续落地和实施效果。 2022年&#xff0c;容声冰箱启动了…

5个好用的AI写论文网站推荐

目录 1.AIQuora论文写作 2.passyyds 答辩PPT 3.AIPassgo论文降AIGC 4.文状元 5.passyyds论文写作 毕业论文是每个毕业生的痛&#xff0c;不管你是本科还是硕士要想顺利毕业你就不得不面对论文。然而&#xff0c;面对论文写作时常常感到无从下手&#xff1a;有时缺乏灵感&a…

微信小程序毕业设计-跑腿系统项目开发实战(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

ChAMP加载肺癌数据和分析甲基化数据流程

ChAMP加载肺癌数据和分析甲基化数据流程 1. 加载数据分析 #!/bin/evn R rm(list = ls()) library(rstudioapi) current_script_path <- getActiveDocumentContext()$path# 将路径转换为当前目录 current_directory <- dirname(current_script_path)# 设置当前工作目录 …

【数据结构】红黑树——领略天才的想法

个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 祝福语&#xff1a;愿你拥抱自由的风 目录 二叉搜索树 AVL树 红黑树概述 性质详解 效率对比 旋转操作 元素操作 代码实现 二叉搜索树 【数据结构】二叉搜索树-CSDN博客 AVL树 【数据结构】AVL树——平衡二叉搜索…

摸鱼大数据——Hive表操作——基本操作

Hive表操作 Hive乱码解决 1、乱码现象 create database test1 comment "乱码测试"; use test1; CREATE TABLE orders ( orderId bigint COMMENT 订单id, orderNo string COMMENT 订单编号, shopId bigint COMMENT 门店id ); 2、处理步骤 注意&#…

uniapp页面vue3下拉触底发送获取新数据请求实现分页功能

页面下拉触底获取新数据实现分页功能实现方式有两种&#xff0c;根据自己的业务需求来定&#xff0c;不同的方案适用场景不一样&#xff0c;有的是一整个页面下拉获取新数据&#xff0c;有的是部分盒子内容滚动到底部时候实现获取新数据&#xff0c;下面讨论一下两种方式的区别…

是他将计算机从“一屋子”变成“一柜子”——量子前哨缅怀小型机之父 戈登·贝尔

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;6000字丨15分钟阅读 5 月 21 日&#xff0c; 美国贝尔实验室资深人士 John Mashey 发布消息称&#xff0c;计算机先驱戈登贝尔&#xff08;Gordon…

精通C++ STL(二):string类的模拟实现

目录 string类各函数接口总览 默认成员函数 构造函数 拷贝构造函数 赋值运算符重载函数 析构函数 迭代器相关函数 begin和end 容量和大小相关函数 size和capacity reserve和resize empty 修改字符串相关函数 push_back append operator insert erase clear swap c_str 访…

[数据集][目标检测]森林火灾检测数据集VOC+YOLO格式362张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;362 标注数量(xml文件个数)&#xff1a;362 标注数量(txt文件个数)&#xff1a;362 标注类别…

《开发问题解决》Window下7z解压:cannot create symbolic link : 客户端没有所需的特权

问题描述&#xff1a; 今天使用7z来解压东西的是突然出现这个问题。 问题解决&#xff1a; download直接下载到c盘中&#xff0c;由于所在文件夹有权限限制。无法进行正常解压。 7.zip解压时使用管理员权限进行解压&#xff0c;解压时使用管理员权限。即如图 使用管理员权限重…