当前位置: 首页 > news >正文

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 processif(pid){       int status;wait(&status);		//waiting for the child to terminate and recliam its resourcesif(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 terminateprintf("the pid of the child processs is: %d, and parent is: %d. \n",getpid(),getppid());setsid();     		//create and enter a new sessionchdir("/");     				//change the working directoryumask(0);       				//change the umaskfor(int i=0;i<3;i++){close(i);} //close the file descriptorwhile(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()发挥作用。

相关文章:

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

Linux 1、进程1.1、进程的六种状态1.2、创建子进程1.3、添加子进程任务1.4、孤儿进程、僵尸进程、守护进程1.4.1、避免僵尸进程1.4.2、创建守护进程1.4.3、杀死守护进程 1.5、综合练习 1、进程 进程可以简单的理解为一个正在执行的程序&#xff0c;它是计算机系统中拥有资源和…...

[题] 不容易系列之(3)―― LELE的RPG难题 #DP

题目 不容易系列之(3)―― LELE的RPG难题 思路 简单的DP题。 代码 #include<bits/stdc.h> using namespace std; //默认以0开头&#xff0c;以1和2结尾。f[i][1]表示长度为i的以1结尾的涂抹方案 //状态转移方程&#xff1a;若以1结尾&#xff0c;则前面一个格子只能是…...

pip 安装任意软件包报错

现象 使用 pip 命令时提示 查看源码 可以看到是从 pip 包中导入 main失败&#xff0c;点击查看目录 main 文件不见了&#xff0c;判断是文件缺失&#xff0c;重装 pip 即可 # python3 下载 pip curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py # python2 下载…...

NLP之Bert实现文本多分类

文章目录 代码代码整体流程解读debug上面的代码 代码 from pypro.chapters03.demo03_数据获取与处理 import train_list, label_list, val_train_list, val_label_list import tensorflow as tf from transformers import TFBertForSequenceClassificationbert_model "b…...

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

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

基于FPGA的图像RGB转HSV实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1. RGB与HSV色彩空间 4.2. RGB到HSV转换原理 5.算法完整程序工程 1.算法运行效果图预览 将FPGA的仿真结果导入到matlab中&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a …...

小型企业如何数字化转型?ZohoCRM助力小企业转型

小型企业数字化之路倍加艰难&#xff0c;其组织规模有限、资源有限&#xff0c;数字化布局或转型&#xff0c;也存在与数字平台匹配度的问题。其实小型企业可以通过CRM客户管理系统实现高效的客户关系管理&#xff0c;进一步提高市场竞争力。 建立高效易用的客户关系管理系统 …...

聊聊模板引擎<Template engine>

模板引擎是什么 模板引擎是一种用于生成动态内容的工具&#xff0c;通常用于Web开发中。它能够将静态的模板文件和动态数据结合起来&#xff0c;生成最终的HTML、XML或其他文档类型。模板引擎通过向模板文件中插入变量、条件语句、循环结构等控制语句&#xff0c;从而实现根据…...

多平台商品采集——API接口:支持淘宝、天猫、1688、拼多多等多个电商平台的爆款、销量、整店商品采集和淘客功能

item_get-获得淘宝商品详情 item_get_app-获得淘宝app商品详情原数据 item_get_pro-获得淘宝商品详情高级版 item_search-按关键字搜索淘宝商品 item_search_img-按图搜索淘宝商品&#xff08;拍立淘&#xff09; item_search_shop-获得店铺的所有商品 API请求地址 公共…...

UI自动化测试框架设计(Selenium)

...

towr code阅读

1. Introduction towr是非常优美的足式机器人规划代码&#xff0c;通过阅读towr重要的几个迭代版本的代码深入了解。 2 v0.1 第一代的版本&#xff0c;foot的位置是提前给定的&#xff0c;只对COG的trajectory进行优化。 2.1 cost 公式 仅仅只考虑加速度&#xff0c; ∫ …...

Channel扇出模式

文章目录 扇出模式reflectSelect 方式 扇出模式 有扇入模式&#xff0c;就有扇出模式&#xff0c;扇出模式是和扇入模式相反的。扇出模式只有一个输入源 Channel&#xff0c;有多个目标 Channel&#xff0c;扇出比就是 1 比目标 Channel 数的值&#xff0c;经常用在设计模式中…...

学者观察 | 联邦学习与区块链、大模型等新技术的融合与挑战-北京航空航天大学童咏昕

导语 当下&#xff0c;数据已成为经济社会发展中不可或缺的生产要素&#xff0c;正在发挥越来越大的价值。但是在数据使用过程中&#xff0c;由于隐私、合规或者无法完全信任合作方等原因&#xff0c;数据的拥有者并不希望彻底和他方共享数据。为解决原始数据自主可控与数据跨…...

ubuntu连接蓝牙耳机

本人也是经历了重重困难&#xff0c;特写此篇希望对读者能够带来帮助 1. 编辑 /etc/bluetooth/main.conf 文件&#xff0c;设定ControllerMode bredr 这一步使用vim编写完成后&#xff0c;保存退出的时候&#xff0c;会显示说没有修改权限&#xff0c;执行以下命令 sudo chm…...

长春理工大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;主页 | 教育漏洞报告平台 兑换价格&#xff1a;10金币 获取条件&#xff1a;提交长春理工大学任意中危或以上级别漏洞...

Excel和Chatgpt是最好的组合。

内容来源&#xff1a;bitfool1 Excel和Chatgpt是最好的组合。 您可以轻松地自动化数据处理。 我向您展示如何在不打字公式的情况下将AI与Excel一起使用&#xff1a; 建立chatgpt 主要目的是使用Chatgpt自动编写Excel宏。 这消除了键入公式的需求&#xff0c;并让您在自然语言…...

Java用Jsoup库实现的多线程爬虫代码

因为没有提供具体的Python多线程跑数据的内容&#xff0c;所以我们将假设你想要爬取的网站是一个简单的URL。以下是一个基本的Java爬虫程序&#xff0c;使用了Jsoup库来解析HTML和爬虫ip信息。 import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nod…...

layui控件开发,实现下拉搜索从数据库获取数据

1 标签部分使用带搜索的下拉框 <div class"layui-inline"><label class"layui-form-label">单位</label><div class"layui-input-inline"><select name"org" lay-search id"org_dwbh" lay-filt…...

让代码变美的第一天 - 观察者模式

文章目录 丑陋的模样变美步骤第一步 - 基本预期第二步 - 核心逻辑梳理第三步 - 重构重构1 - 消息定义重构2 - 消息订阅重构3 - 消息发布 高级用法按顺序订阅异步订阅多消息订阅 丑陋的模样 当我们开发一个功能&#xff0c;代码可能如下&#xff1a; private void test() {fun…...

微服务-网关设计

文章目录 引言I 网关部署java启动jar包II 其他服务部署细节2.1 服务端api 版本号III 网关常规设置3.1 外部请求系统服务都需要通过网关访问3.2 第三方平台回调校验文件的配置IV 微服务日志跟踪4.1 打印线程ID4.2 封装线程池任务执行器4.3 将自身MDC中的数据复制给子线程4.4 微服…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...