Linux学习笔记之五(父子进程、孤儿进程、僵尸进程、守护进程)

Linux

  • 1、进程
    • 1.1、进程的六种状态
    • 1.2、创建子进程
    • 1.3、添加子进程任务
    • 1.4、孤儿进程、僵尸进程、守护进程
      • 1.4.1、避免僵尸进程
      • 1.4.2、创建守护进程
      • 1.4.3、杀死守护进程
    • 1.5、综合练习

1、进程

进程可以简单的理解为一个正在执行的程序,它是计算机系统中拥有资源和独立运行的最小单位。多个进程同时运行从宏观看是并行,从微观上看是串行。举个例子,现有一个CPU以及两个同时运行的线程a和b,CPU实际上是用极小的时间碎片来交替执行a和b,以达到肉眼觉得CPU在同时执行两个进程的效果。
在这里插入图片描述
进程有三个状态,分别是就绪态、运行态、阻塞态。顾名思义,就绪态就是进程万事俱备只等CPU来执行它了;运行态便是CPU正在执行该进程;阻塞态是线程还没准备好被CPU执行。当然在这三个状态之上又衍生出许多状态,这里不多做介绍。
在这里插入图片描述
另外,每一个进程都有自己的编号,称之为pid(process identity document)。在进程中可以通过getpid()获得当前进程pid,也可以通过getppid()获得当前进程父进程的pid。

1.1、进程的六种状态

  1. 运行状态(Running: R):进程在运行,或者进程准备好被系统调度。
  2. 睡眠状态(Sleeping: S):此时进程在安静的等待某个事件发生,且此时进程也可以被杀死。
  3. 磁盘休眠状态(Disk sleep: D):不可杀死的睡眠状态。
  4. 停止状态(Stopped: T):该进程被某个信号叫停了,同时它也可以被叫起来继续运行。
  5. 僵尸状态(Zombie: Z):子进程死了,但父进程在忙无法替它收尸,此时子进程进入僵尸状态。
  6. 死亡状态(Dead: X):进程被杀死之后,尸体也成功回收,即资源被回收。

1.2、创建子进程

man 2 fork

在这里插入图片描述
可以看到通过以下代码便可以创建一个子进程。

pid_t pid fork();

返回值:成功则返回子进程的pid,失败则返回负值。

用fork创建的子进程会和父进程执行同一个可执行文件,但子进程会从fork函数之后才开始执行。如图所示:
在这里插入图片描述
这里值得注意的是,程序的编译会经历四个步骤,即预处理、编译、汇编、链接。只有经过这四个步骤之后程序才会变成一个可执行文件,而由于这四个步骤会处理好程序的各种变量、头文件、宏定义等内容,所以不会导致子进程从fork开始执行下去会因为缺少一些变量定义之类的而产生报错。

1.3、添加子进程任务

如果仅仅使用fork让子进程执行父进程的代码,这将使子进程显得毫无意义,而为了给子进程添加新的任务,exec函数族便被发明出来。从说明书可以看到exec有六个函数。

man execl

在这里插入图片描述
比较常用的使execl,通过用execl函数让子进程去执行其他的可执行文件,以达到给子进程添加新任务的目的。其函数原型长这样:

int execl(const char *path, const char *arg, ... /* (char *) NULL*/);

它的参数应当如何设置,我先直接贴一段manual的原文上来。

The  const  char *arg and subsequent ellipses in the execl(), execlp(),and execle() functions can be thought of  as  arg0,  arg1,  ...,  argn.
Together  they  describe  a list of one or more pointers to null-terminated strings that represent the argument list available  to  the  executed  program.  
The first argument, by convention, should point to the filename associated with the file being executed.  
The  list  of  arguments  must be terminated by a null pointer, and, since these are variadic functions, this pointer must be cast (char *) NULL.

这段话大概的意思是,execl可以有无数个参数,具体取决了即将调用的可执行文件的需要。但除了char *path之外的第一个参数是可执行文件的名字,最后一个参数是NULL。

char *path:可执行文件所在的目录(包含可执行文件的名字)。
char *arg1:可执行文件的名字。

char *argn:NULL

比如我们想在子进程中执行ls。execl可以这个写:

execl(/bin/ls”,“ls”,NULL);		//仅列出当前目录可见文件
execl(/bin/ls”,“ls”, "-l", NULL);		//列出当前目录可见文件详细信息
execl(/bin/ls”,“ls”, "-i", "-l", NULL);		//显示文件的inode信息
...

在这里插入图片描述

为什么父进程的getpid()和子进程getppid()得到的值不一样,可以参考下面这篇文章:父进程中getpid()值与子进程中getppid()值不相同的问题及解释

1.4、孤儿进程、僵尸进程、守护进程

  • 孤儿进程(Orphan process):父进程已经结束,子进程还在继续执行。但由于子进程需要父进程来帮助其释放资源,所以孤儿进程会被托管在 i n i t init init进程之下。
  • 僵尸进程(Zombie process):子进程已经结束,但父进程还在运行,且此时父进程无法去帮助子进程释放资源,即父进程没有读到子进程的exit()函数。导致子进程死了无人收尸,故被称之为僵尸进程。
  • 守护进程(Daemon process):一些脱离于终端,且不与用户交互的后台进程叫做守护进程。它们的存在至关重要,在背后维护着系统或某个软件、程序正常运行。下图红框内就基本是Windows系统的守护进程。
    在这里插入图片描述

1.4.1、避免僵尸进程

以上这三种进程中,孤儿进程是可以成为进入守护进程的前提,而守护进程又在许多情况下挥发巨大的作业,那么只剩下僵尸进程是程序不愿意看到的。
避免僵尸进程可以用wait系列函数函数,我们来看看它的函数说明:

man 2 wait

在这里插入图片描述
使用wait()函数得先理清一些概念:

  1. 父进程一般不执行其他任务,它的存在就是为了生出一堆子进程,再派子进程去执行具体任务,然后如果子进程死亡了,父进程再去替它们收尸。
  2. 子进程死亡之后会向父进程发送一个SIGCHILD信号,请求父进程为自己收尸(回收资源),以避免自己成为僵尸进程。
  3. wait()函数的出现就完美的满足父进程只生娃、收尸、不干事的需求,当父进程调用这个函数之后就进入阻塞状态,只有在子进程死后发送SIGCHILD信号,父进程才会醒过来去收拾子进程的资源。

wait的函数原型是:

pid_t wait(int *status);

当调用wait()函数,父进程会自动检查子进程的状态,无需我们再干预。

int *status:是一个32位的整形数据,其中包含了退出码、终止信号等信息。通常通过一些宏函数来读取status中的具体信息。当然,如果你压根不想要读取这些信息,只想默默收尸走人,那这个参数可以是NULL。
返回值:如果成功,则返回子进程的pid,反之返回-1。

读取status的宏:

  • WEXITSTATUS:在进程正常退出的情况下读取status中的退出码并将其返回。如果退出码是负数,则用255去加这个负数。(退出码即exit(code)中的code)
  • WIFEXITED:通过解析status判断进程是否为正常退出,若是则返回1,否则返回0。

此外,还有waitpid,waitid等函数,前者常用于等待回收某个具体的子进程,后者我也不太懂了。。。
贴一段代码来看看wait怎么用:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{       
	pid_t pid = fork();     //create a child process

	if(pid)
	{       
		int status;
		wait(&status);		//waiting for the child to terminate and recliam its resources
		if(WIFEXITED(status))
		{       
			printf("The exit code is: %d\n", WEXITSTATUS(status));
		}
	}
	else
	{       
		printf("This is the child process.\n");
		sleep(2);
		exit(3);
	}
	return 0;
}

1.4.2、创建守护进程

前文提到,守护进程是托管在 i n i t init init下的子进程,且脱离控制终端独立运行于后台。由此引出创建一个守护进程的两个必要步骤:

  1. 使用fork()创建一个新的进程,然后在父进程中使用exit()退出。该步骤可以让子进程变成孤儿进程,进而被init进程托管。
  2. 在子进程中使用setsid()函数。该函数可以让子进程脱离原来的进程组和会话,进入一个全新的会话中去。这有这样,该进程才能脱离原来的控制终端。

这两个步骤使创建一个守护进程的必要步骤,再次也先暂停下来解释何为进程组和会话。
所谓进程组,顾名思义就是许多个进程组成的一个小组,该小组的id(Group Identity Document: GID)就是小组组长的pid。接着,会话中又会聚集了许多个小组,同理,会话id(Session Identity Dccument: SID)便是作为翘楚的进程组id(GID)。一般而言,一个会话使用一个控制终端,不过也有特殊,比如对于为守护进程所创建的新会话,我们不希望它有一个控制终端。
注:控制终端就是我们敲命令行的那个窗口,也称终端或终端窗口。在Ubuntu中直接叫terminal(终端),一个terminal对应一个shell进程。而shell是一个解释器,为终端和系统之间的交互提供桥梁。参考:link
在这里插入图片描述
接下来,添加几个步骤让讲守护进程的更具备撸棒性(robust)。

  1. 通过chdir()把当前的工作目录改成根目录。
  2. 重设文件掩码(umask),一般设为0。
  3. 关闭文件描述符,由于文件描述符是内核空间返回给应用层的一个文件“代号”,然而在守护进程中我们并不希望再与应用层产生联系,所以关闭文件描述符可以节省资源。
  4. 在子进程中再套一个进程,防止会话建立新的控制终端。

最后,就可以在守护进程中添加我们需要执行的代码了。

1.4.3、杀死守护进程

守护进程一般生命周期比较长,由于其脱离了控制终端,所以想要关闭守护进程只能等到系统完全关闭或者手动杀死它。比如用kill:

kill -9 [the pid of the daemon process]

1.5、综合练习

本次练习任务:

  1. 创建一个父进程和一个子进程,并分别打印这两个进程的pid。
  2. 在父进程离开后,打印此时托管子进程的进程的pid。
  3. 创建一个守护进程,要求更改其目录,关闭文件描述符号,修改文件掩码。
  4. 最后从控制终端杀死这个守护进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
        pid_t pid = fork();
        if(pid) 								//enter the parent process
        {
                printf("the pid of the parent processs is: %d.\n",getpid());
                exit(1);
        }
        else    								//enter the child process
        {
                sleep(1); 						//waiting for the parent process to terminate
                printf("the pid of the child processs is: %d, and parent is: %d. \n",getpid(),getppid());
                setsid();     		//create and enter a new session
                chdir("/");     				//change the working directory
                umask(0);       				//change the umask
                for(int i=0;i<3;i++){close(i);} //close the file descriptor
                while(1)
                {
                	//you can put any programs you like into this field.
                }
        }
        return 0;
}

输出结果是:

the pid of parent process is: 2600.
the pid of child process is: 2601, and its parent is: 1420.

可以看到子进程的父进程已经和原来创建它的父进程pid不一样了,我们通过搜索看看是谁托管了这个子进程。

ps -aux | grep 1420

在这里插入图片描述
可以看到是init进程托管了这个子进程。此外,当该孤儿进程使用setsid()函数变成守护进程之后,如果再使用printf()之类的函数将失去效果。因为守护进程没有其对应的控制终端,自然无法让printf()发挥作用。

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

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

相关文章

Ionic 组件 ion-item-divider ion-item-group ion-item-sliding ion-label ion-note

1 ion-item-divider Item dividers是块元素&#xff0c;可用于分隔列表中的items 。它们类似于列表标题&#xff0c;但它们不应该只放在列表的顶部&#xff0c;而应该放在items之间。 <ion-list><ion-item-group><ion-item-divider><ion-label> Secti…

使用小程序插件【用户信息功能页】获取用户昵称、头像、openid

摘要 因为获取用户信息的接口 wx.getUserInfo 存在滥用&#xff0c;使用不规范等原因&#xff0c;微信官方已经将这个接口权限回收&#xff0c;改为用户信息填写&#xff0c;即引导用户主动填写微信昵称和上传头像。这种做法确实是麻烦了点。 但是微信小程序插件&#xff0c;…

浏览器标签页之间的通信

前言 在开发管理后台页面的时候&#xff0c;会遇到这样一种需求&#xff1a;有一个列表页面&#xff0c;一个新增按钮&#xff0c;一个新增页面&#xff0c;点击新增按钮&#xff0c;在一个新的标签页中打开新增页面。并且&#xff0c;新增后要自动实时的更新列表页面的数据。…

机器学习概论

目录 一、机器学习概述 1、机器学习与人工智能、深度学习的关系 2、机器学习的范围 3、机器学习可以解决什么问题 给定数据的预测问题&#xff1a; 二、机器学习的类型 1、监督学习 分类&#xff08;Classification&#xff09; 回归&#xff08;Regression、Prediction&am…

Vue知识点总结

路由 使用 参数传递的两种方式 路由的params传参 路由的query传参 组件 概念 局部功能代码&#xff08;html、css js&#xff09;和资源(mp3 mp4 ttf .zip)的集合 非单文件组件 一个文件对应多个组件&#xff0c;以html结尾 使用 <xuexiao>即可使用 注意&#xf…

MySQL字符串需要注意的事项

char(N)&#xff0c;N在0-255间 varchar(N)&#xff0c;N在0-65536间 需要注意N是字符&#xff0c;不是字节&#xff0c;英文字母一个字符一个字节&#xff0c;阿拉伯字母一个字符两个字节&#xff0c;中文日文一个字符三个字节&#xff0c;emoji是一个字符四个字节 当今移动端…

arcgis 批量删除Table中的某些Field

当shp或者table文件较少时&#xff0c;可以手动删除每个文件中的某些字段&#xff0c;当文件较多时&#xff0c;就需要使用arcpy或者model进行处理。

SUB-1G芯片---PAN3031低功耗远距离无线收发芯片

PAN3031 是一款采用 ChirpIoT TM 调制解调技术的低功耗远距离无线收发芯片&#xff0c;支持半双工无线通信&#xff0c;工作频段为 370~590 MHz 和 740~1180MHz&#xff0c;该芯片具有高抗干扰性、高灵敏度、低功耗和超远传输距离等特性。最高具有-129dBm 的灵敏度&#xff0c;…

文件夹找不到了怎么恢复?4个正确恢复方法分享!

“我在电脑上保存了很多的文件和文件夹&#xff0c;今天在查找文件时&#xff0c;发现我有一整个文件夹都消失了&#xff0c;不知道怎么才能找到呢。有朋友可以帮帮忙吗&#xff1f;” 电脑中文件夹突然找不到了可能会引发焦虑&#xff0c;尤其是如果这些文件夹包含重要的数据。…

[工业自动化-6]:西门子S7-15xxx编程 - PLC系统硬件组成与架构

目录 一、PLC系统组成 1.1 PLC 单机系统组成 1.2 PLC 分布式系统 二、PLC各个组件 2.1 PLC上位机 2.2 PLC主站&#xff1a;PLC CPU控制中心 &#xff08;1&#xff09;主要功能 &#xff08;2&#xff09;主站组成 2.3 PLC分布式从站: IO模块的拉远 &#xff08;1&am…

拖拽式能源管理平台,轻松掌握能源情况!

随着科技的进步&#xff0c;能源问题变得日益紧迫。为了提高能源利用效率&#xff0c;减少浪费&#xff0c;各能源使用企业开始重视能源管理&#xff0c;并寻求高效的节能工具来协助管理。因此&#xff0c;智慧能源管理平台应运而生&#xff0c;为能源使用企业提供强大的节能管…

C++ Qt 学习(五):Qt Web 编程

1. Chrome 技术介绍 大多数 web 技术都是基于 chrome&#xff0c;例如 CEF、QCefView 以及 QWebEngineView&#xff0c;这些都是在 native 界面里用来显示 html 网页&#xff0c;并且可以与 web 交互 例如常见的登录窗口、优酷的视频区域、WPS 的稻壳商城等&#xff0c;这些都…

拓世科技集团打造数字人营销解决方案,为车企提供新的“增长担当”

汽车&#xff0c;已经渐渐融入了现代人的日常生活&#xff0c;从高端的身份标志转变为普罗大众的出行选择&#xff0c;它驶入了千家万户&#xff0c;成为了我们日常出行的可靠伙伴&#xff0c;见证着人们的生活故事和时代的变迁。 中国汽车市场的蓬勃发展引起了业内外的广泛关…

ES|QL(Elasticsearch 查询语言)入门

作者&#xff1a;Ninoslav Miskovic 通过使用 ES|QL 直接从 Discover 创建聚合、可视化和警报&#xff0c;缩短获得见解的时间。 什么是 ES|QL&#xff08;Elasticsearch 查询语言&#xff09;&#xff1f; ES|QL&#xff08;Elasticsearch 查询语言&#xff09;是 Elastic 全…

掌握互联网的未来:5G时代的新机遇

随着5G技术的快速发展&#xff0c;我们正步入一个全新的互联网时代。5G不仅仅是速度的飞跃&#xff0c;它还代表着无限的可能性和前所未有的创新机遇。本文将探讨5G如何重新定义互联网&#xff0c;并为您提供洞察如何抓住这波科技浪潮。 5G技术的核心优势 超高速度&#xff1a…

Python 3D建模指南【numpy-stl | pymesh | pytorch3d | solidpython | pyvista】

想象一下&#xff0c;我们需要用 python 编程语言构建某个对象的三维模型&#xff0c;然后将其可视化&#xff0c;或者准备一个文件以便在 3D 打印机上打印。 有几个库可以解决这些问题。 让我们看一下如何在 python 中从点、边和图元构建 3D 模型。 如何执行基本 3D 建模技术&…

人车实时精准管控!北斗让换流站作业更安全

换流站是高压直流输电系统的核心组成部分&#xff0c;对促进电网稳定运行、保障电力行业的可持续发展有着重要作用。长期以来&#xff0c;随着电网主变改扩建设工程的开展&#xff0c;站内作业人员安全管控压力随之增大&#xff0c;仅依靠传统的“人盯人”和“自主”管控模式较…

CLion配置libtorch找不到xxx.dll

项目场景&#xff1a; 使用CLion配置libtorch时遇到该问题 问题描述 使用CLion配置libtorch时&#xff0c;CMakeLists.txt文件写完后&#xff0c;cmake也能成功&#xff0c;但是一旦运行代码就会报错找不到xxx.dll&#xff0c;比如找不到torch_cuda.dll或找不到c10.dll 原因分…

随机链表的复制

题目描述 给你一个长度为n的链表&#xff0c;每个节点包含一个额外增加的随机指针random&#xff0c;该指针可以指向链表中的任何节点或空节点。构造这个链表的深拷贝。 深拷贝应该正好由n个全新节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节点的nex…

对话大众软件子公司:中国的智舱、智驾比欧洲早一代

作者 | 德新 编辑 | 王博 尤其在上海车展之后&#xff0c;大部分的外资车企都在转型调整。 2023年的上海车展是一个重要节点。在这之前&#xff0c;疫情阻断了国内和海外频繁的线下交流&#xff0c;而国内汽车的新能源化和智能化在这期间完成了一次飞跃式的发展。所以车展开…