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

Linux 入门八:Linux 多进程

一、概述

1.1 什么是进程?

在 Linux 系统中,进程是程序的一次动态执行过程。程序是静态的可执行文件,而进程是程序运行时的实例,系统会为其分配内存、CPU 时间片等资源。例如,输入 ls 命令时,系统创建进程执行 ls 程序来显示文件列表。进程是资源分配的基本单位,理解进程对掌握 Linux 系统运行机制至关重要。

1.2 查看进程

在 Linux 中,可使用 ps 命令查看系统中当前运行的进程。下面是一些常用的 ps 命令参数组合:

  • ps -ef
    • 功能:以全格式显示所有进程的详细信息。
    • 步骤
      1. 打开终端。
      1. 输入 ps -ef 并回车。
    • 示例输出
 
UID PID PPID C STIME TTY TIME CMDroot 1 0 0 00:00 ? 00:00:01 /sbin/init splashroot 2 0 0 00:00 ? 00:00:00 [kthreadd]

  • 参数解释
    • UID:进程所有者的用户 ID。
    • PID:进程的 ID 号。
    • PPID:父进程的 ID 号。
    • C:CPU 占用率。
    • STIME:进程启动时间。
    • TTY:进程关联的终端。
    • TIME:进程使用的 CPU 时间。
    • CMD:启动进程的命令。

在 Linux 中,除了 ps -ef,还可使用以下命令查看进程:​

  • ps aux:​
  • 功能:显示所有进程的详细资源使用情况(如内存、CPU 占用率)。​
  • 示例输出:​
TypeScript取消自动换行复制USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ​root 1 0.0 0.1 24176 4360 ? Ss 00:00 0:01 /sbin/init splash ​

  • %CPU:CPU 占用百分比。​
  • %MEM:内存占用百分比。​
  • STAT:进程状态(如 S 表示睡眠,R 表示运行)。​
  • top 命令:​
  • 功能:动态实时显示进程资源占用情况,类似 Windows 任务管理器。​
  • 操作:输入 top 后,可按 q 退出。

二、进程的创建

2.1 fork 函数

在 C 语言里,fork 函数是创建新进程的关键函数。它的作用是复制当前进程,生成一个子进程,原进程则成为父进程。fork 函数的原型如下:

#include <unistd.h>pid_t fork(void);
  • 返回值
    • 在父进程中,fork 函数返回子进程的 PID(一个正整数)。
    • 在子进程中,fork 函数返回 0。
    • 若 fork 失败,返回 -1。
创建进程的步骤
  1. 包含必要的头文件:#include <unistd.h> 和 #include <stdio.h>。
  1. 调用 fork 函数创建子进程。
  1. 根据 fork 的返回值判断当前是父进程还是子进程,并执行相应的代码。
示例代码
#include <unistd.h>#include <stdio.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");} else if (pid == 0) {// 子进程printf("我是子进程,我的 PID 是 %d,父进程的 PID 是 %d\n", getpid(), getppid());} else {// 父进程printf("我是父进程,我的 PID 是 %d,子进程的 PID 是 %d\n", getpid(), pid);}return 0;}
编译和运行步骤
  1. 把上述代码保存为 fork_example.c。
  1. 打开终端,进入代码所在目录。
  1. 使用 gcc 编译代码:gcc fork_example.c -o fork_example。
  1. 运行编译后的可执行文件:./fork_example。

三、僵尸进程

3.1 形成条件

僵尸进程的形成需要满足以下三个条件:

  1. 子进程优先于父进程结束。
  1. 父进程不结束。
  1. 父进程不调用 wait 函数。

当子进程结束时,它会向父进程发送一个 SIGCHLD 信号,但如果父进程没有调用 wait 或 waitpid 函数来回收子进程的资源,子进程就会变成僵尸进程。

3.2 如何避免僵尸进程

方法一:父进程调用 wait 函数

wait 函数的作用是等待任意一个子进程结束,并回收其资源。其原型如下:

#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);
  • 参数:status 用于存储子进程的退出状态。
  • 返回值:返回结束的子进程的 PID。
示例代码
#include <unistd.h>#include <stdio.h>#include <sys/types.h>#include <sys/wait.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");} else if (pid == 0) {// 子进程printf("子进程开始执行,PID 是 %d\n", getpid());sleep(2);printf("子进程结束\n");} else {// 父进程int status;pid_t child_pid = wait(&status);printf("父进程回收了 PID 为 %d 的子进程\n", child_pid);}return 0;}
方法二:使用 signal 函数处理 SIGCHLD 信号

可通过 signal 函数捕获 SIGCHLD 信号,并在信号处理函数中调用 wait 或 waitpid 函数。

示例代码
#include <unistd.h>#include <stdio.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>void sigchld_handler(int signo) {pid_t pid;int status;while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {printf("回收了 PID 为 %d 的子进程\n", pid);}}int main() {signal(SIGCHLD, sigchld_handler);pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");} else if (pid == 0) {// 子进程printf("子进程开始执行,PID 是 %d\n", getpid());sleep(2);printf("子进程结束\n");} else {// 父进程printf("父进程继续执行\n");sleep(5);}return 0;}

四、孤儿进程

4.1 形成条件

孤儿进程的形成需要满足以下两个条件:

  1. 父进程优先于子进程结束。
  1. 子进程未结束。

当父进程结束后,子进程就会变成孤儿进程,此时它会被进程 ID 为 1 的 init 进程接管。

4.2 被进程 ID 为 1 的进程接管

init 进程会负责回收孤儿进程的资源,确保系统资源不会被浪费。

示例代码
#include <unistd.h>#include <stdio.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");} else if (pid == 0) {// 子进程printf("子进程开始执行,父进程 PID 是 %d\n", getppid());sleep(5);printf("子进程继续执行,父进程 PID 是 %d\n", getppid());} else {// 父进程printf("父进程结束\n");}return 0;}

在这个示例中,父进程会先结束,子进程在睡眠 5 秒后,会发现自己的父进程 ID 变成了 1。

五、守护进程(后台进程)

5.1 实现过程

守护进程是一种在后台持续运行的进程,通常在系统启动时就开始运行,并且不受用户登录和注销的影响。以下是创建守护进程的详细步骤:

步骤 1:创建子进程,父进程退出
#include <unistd.h>#include <stdio.h>#include <stdlib.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程继续执行// 后续步骤...return 0;}
步骤 2:在子进程中创建新会话
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程创建新会话pid_t sid = setsid();if (sid < 0) {perror("setsid 失败");exit(EXIT_FAILURE);}// 后续步骤...return 0;}

setsid 函数的作用是创建一个新的会话,使子进程成为新会话的首进程,并且脱离原有的控制终端。

步骤 3:改变工作目录
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程创建新会话pid_t sid = setsid();if (sid < 0) {perror("setsid 失败");exit(EXIT_FAILURE);}// 改变工作目录if (chdir("/") < 0) {perror("chdir 失败");exit(EXIT_FAILURE);}// 后续步骤...return 0;}

chdir 函数用于将工作目录切换到根目录,避免工作目录被卸载导致进程无法正常工作。

步骤 4:设置文件权限掩码
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程创建新会话pid_t sid = setsid();if (sid < 0) {perror("setsid 失败");exit(EXIT_FAILURE);}// 改变工作目录if (chdir("/") < 0) {perror("chdir 失败");exit(EXIT_FAILURE);}// 设置文件权限掩码umask(0);// 后续步骤...return 0;}

umask 函数用于设置文件权限掩码,确保守护进程创建的文件具有预期的权限。

步骤 5:关闭不需要的文件描述符
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失败");exit(EXIT_FAILURE);}if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程创建新会话pid_t sid = setsid();if (sid < 0) {perror("setsid 失败");exit(EXIT_FAILURE);}// 改变工作目录if (chdir("/") < 0) {perror("chdir 失败");exit(EXIT_FAILURE);}// 设置文件权限掩码umask(0);// 关闭不需要的文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 守护进程的主循环while (1) {// 执行守护进程的任务sleep(1);}return 0;}

关闭标准输入、标准输出和标准错误输出的文件描述符,防止守护进程与控制终端交互。

编译和运行步骤

  1. 把上述代码保存为 daemon_example.c。
  1. 打开终端,进入代码所在目录。
  1. 使用 gcc 编译代码:gcc daemon_example.c -o daemon_example。
  1. 运行编译后的可执行文件:./daemon_example。此时,守护进程会在后台持续运行。

通过以上步骤,你可以逐步掌握 Linux 多进程的相关知识,包括进程的创建、僵尸进程和孤儿进程的处理,以及守护进程的实现。在实际应用中,多进程编程可以提高程序的并发性能,充分利用多核 CPU 的资源。

相关文章:

Linux 入门八:Linux 多进程

一、概述 1.1 什么是进程&#xff1f; 在 Linux 系统中&#xff0c;进程是程序的一次动态执行过程。程序是静态的可执行文件&#xff0c;而进程是程序运行时的实例&#xff0c;系统会为其分配内存、CPU 时间片等资源。例如&#xff0c;输入 ls 命令时&#xff0c;系统创建进程…...

单调栈 —— 1.基本概念与核心算法

1. 基本概念 1.1 知识预备 在理解单调栈之前&#xff0c;我们需要先掌握两个基础概念&#xff1a;栈&#xff08;Stack&#xff09; 和 单调性&#xff08;Monotonicity&#xff09;。 什么是栈&#xff08;Stack&#xff09; 栈是一种**后进先出&#xff08;LIFO, Last-In…...

工程师 - 场效应管分类

What Are the Different Types of FETs? Pulse Octopart Staff Jul 31, 2021 Field effect transistors (FETs) are today’s workhorses for digital logic, but they enjoy plenty of applications outside of digital integrated circuits, everything from motor driver…...

用infoNCE微调Embedding模型

infoNCE 代码1&#xff1a;&#xff08;样本格式为query_n个positive_n个hardnegative&#xff09; PairwiseModel并不是模型&#xff0c;而是连接model和loss的一个包装类。 PairwiseModel接收两种类型样本 【query pos pair】or【query pos neg triplet】。 CrossEntropy…...

Debezium报错处理系列之第128篇:增量快照报错java.lang.OutOfMemoryError: Java heap space

Debezium报错处理系列之第128篇:增量快照报错java.lang.OutOfMemoryError: Java heap space 一、完整报错二、错误原因三、解决方法Debezium从入门到精通系列之:研究Debezium技术遇到的各种错误解决方法汇总: Debezium从入门到精通系列之:百篇系列文章汇总之研究Debezium技…...

AI——使用pandas

文章目录 1、pandas介绍2、为什么使用pandas3、pandas的数据结构1、Series2、DataFrame3、MultiIndex 4、pandas基本数据操作1、索引操作2、赋值操作3、排序4、算术运算5、逻辑运算6、逻辑运算函数7、统计函数8、累计统计函数9、自定义运算 5、pandas读取文件和存储1、csv文件2…...

SMT贴片组装工艺优化与高效生产

内容概要 现代SMT贴片组装工艺的优化与高效生产涉及多维度技术协同&#xff0c;其核心在于构建精密可控的制造体系。本文系统梳理了从焊接参数调控到智能检测部署的全链路关键环节&#xff0c;重点解析影响生产效能的核心变量及其相互作用机制。通过对比不同贴装设备的速度-精…...

2025认证杯挑战赛B题【 谣言在社交网络上的传播 】原创论文讲解(含完整python代码)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了认证杯数学中国数学建模网络挑战赛第一阶段B题目谣言在社交网络上的传播完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半…...

用docker容器创建属于自己的一方小世界!容器中,盖周天之变,化吾为王~

用docker容器创建属于自己的一方小世界&#xff01;容器中&#xff0c;盖周天之变&#xff0c;化吾为王~ 分别查看用户id和组id。 命令&#xff1a; 1、id -u 2、id -g 创建并运行容器 docker run -d -p 31404:22 -v /home/liub:/home -v /data:/app/data --user 1004:1004 --…...

vue拓扑图组件

vue拓扑图组件 介绍技术栈功能特性快速开始安装依赖开发调试构建部署 使用示例演示截图组件源码 介绍 一个基于 Vue3 的拓扑图组件&#xff0c;具有以下特点&#xff1a; 1.基于 vue-flow 实现&#xff0c;提供流畅的拓扑图展示体验 2.支持传入 JSON 对象自动生成拓扑结构 3.自…...

前端防御性编程

关于防御性编程 你是否遇到过&#xff0c;接口请求失败或者返回数据错误&#xff0c;导致系统白屏或者前端自身写的代码存在一些缺陷&#xff0c;导致整个系统不够健壮&#xff0c;从而导致系统白屏 常见的问题与防范 最常见的问题 访问了null或者undefined的属性 null.a …...

Linux服务器网卡深度解析:从ifconfig输出到生产环境性能调优实战

Linux服务器网卡深度解析&#xff1a;从ifconfig输出到生产环境性能调优实战 Linux服务器网卡深度解析&#xff1a;从ifconfig输出到生产环境性能调优实战一、背景二、生产环境的服务器部署情况三、拆解一个真实的 ifconfig 输出1、先看 MAC 地址2、再看设备的 interrupt 和 me…...

【愚公系列】《Python网络爬虫从入门到精通》048-验证码识别(滑动拼图验证码)

🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! 👉 江湖人称"愚公搬代码",用七年如一日的精神深耕技术领域,以"…...

SpringBoot分布式项目中实现智能邮件提醒系统

一、应用场景与需求分析 在电商、OA、客服等系统中,邮件提醒是用户触达的重要方式。本文针对以下典型需求进行方案设计: 多类型支持:订单超时、服务到期、待办通知等场景动态内容:支持纯文本/HTML/模板引擎内容格式智能重发:24小时未处理自动升级提醒级别高可用性:分布式…...

对shell脚本敏感命令进行加密执行

我要加密这条命令&#xff1a;rm /root/scripty.sh 如何利用openssl aes-256-cbc 实现加密和解密&#xff0c;并执行命令 加密、解密并执行命令的完整流程 以下是使用 openssl aes-256-cbc 加密命令 rm /root/scripty.sh&#xff0c;解密并执行的详细步骤&#xff1a; 1. 加密…...

《嵌套调用与链式访问:C语言中的函数调用技巧》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、嵌套调用&#xff08;一&#xff09;定义&#xff08;二&#xff09;实现方式&#xff08;三&#xff09;优点&#xff08;四&#xff09;缺点 二、链式…...

Python-控制语句

控制语句 控制语句和逻辑思维 控制语句:把语句组合成能完成一定功能的小逻辑模块分类:顺序、选择、循环“顺序结构”:代表“先执行a,再执行b”的逻辑“条件判断结构”:代表“如果…,则…”的逻辑“循环结构”:代表“如果…则重复执行…”的逻辑条件判断结构 选择结构通…...

教程:在Typora中显示拼音——附处理工具

原因 因为自己普通话不标准&#xff0c;希望可以制作适合自己的带拼音的文档&#xff0c;可以把平常看到的内容、说过的话作为练习普通话的材料。 在市面上&#xff0c;带拼音的材料、书籍并不多&#xff0c;而且有可能是一些比较生僻的内容。所以希望可以自己制作这样的材料…...

OpenCV 图形API(30)图像滤波-----腐蚀操作函数erode()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用特定的结构元素腐蚀图像。 cv::gapi::erode 是 OpenCV 的 G-API 模块中用于执行图像腐蚀操作的函数。腐蚀是一种基本的形态学操作&#xff…...

【KWDB 创作者计划】第二卷:开发者实战篇

​KWDB技术白皮书卷二&#xff1a;开发者实战篇 ​1. 自然语言到量子查询的编译系统 1.1 NL2QSQL翻译引擎架构 运行时流程图解&#xff1a; ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────…...

设计模式:里氏代换原则 - 继承设计的稳定之道

里氏代换原则&#xff08;Liskov Substitution Principle, LSP&#xff09;作为面向对象设计的基石之一&#xff0c;为我们提供了解决之道。它指导我们如何构建高扩展性和低维护成本的继承体系&#xff0c;避免代码行为不一致导致的混乱和错误。 一、错误的继承设计如何毁掉系…...

Node.js中fs模块详解

Node.js 中 fs 模块&#xff08;非 Promise&#xff09;API 详解 Node.js 的 fs 模块提供了同步和异步的文件系统操作。以下是非 Promise 版本的 API 详解&#xff1a; 1. 文件读取操作 const fs require(fs);// 异步读取文件 fs.readFile(file.txt, utf8, (err, data) >…...

特殊定制版,太给力了!

今天给大家分享一款超棒的免费录屏软件&#xff0c;真的是录屏的好帮手&#xff01; 这款软件功能可以录制 MP4、AVI、WMV 格式的标清、高清、原画视频&#xff0c;满足你各种需求。 云豹录屏大师 多功能录屏神器 它的界面特别简洁&#xff0c;上手超快&#xff0c;用起来很顺…...

go:实现最简单区块链

1.新建文件夹命名为blockchain,在此文件夹下分别创建两个文件一个为block.go另一个为chain.go如下图所示: 2.写入代码: block.go package blockchainimport ("bytes""crypto/sha256""encoding/gob""log""strconv""ti…...

工业相机使用笔记

目前工业相机有多种分类方式&#xff0c;以下是基于不同原理和特点的类别总结&#xff1a; 按维度分类 2D相机&#xff1a; 原理&#xff1a;通过镜头将二维平面上的物体成像在图像传感器上&#xff0c;传感器上的像素点阵列捕捉物体的光信号&#xff0c;并转换为电信号或数字…...

系分论文《论面向服务开发方法在设备租赁行业的应用》

系统分析师论文系列 【摘要】 2022年5月&#xff0c;我司承接某工程机械租赁企业"智能租赁运营管理平台"建设项目&#xff0c;我作为系统分析师主导系统架构设计。该项目需整合8大类2000余台设备资产&#xff0c;覆盖全国15个区域运营中心与300家代理商&#xff0c;实…...

【Code】《代码整洁之道》笔记-Chapter12-迭进

第12章 迭进 12.1 通过迭进设计达到整洁目的 假使有4条简单的规则&#xff0c;跟着做就能帮助你创建优良的设计&#xff0c;会如何&#xff1f;假使遵循这些规则&#xff0c;你就能洞见代码的结构和设计&#xff0c;更能轻易地应用SRP和DIP之类的原则&#xff0c;便会如何&…...

04--网络属性设置与多路复用

一、TCP可靠性分析 二、 scoket 属性设置 1、socket 属性设置表 NAMEgetsockopt, setsockopt - get and set options on sockets获取 和 设置 套接字属性 SYNOPSIS#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int getsockopt(int so…...

AI领域再突破,永洪科技荣获“2025人工智能+创新案例”奖

在2025年的今天&#xff0c;人工智能已从技术概念全面渗透至产业核心。中国作为全球AI技术应用的前沿阵地&#xff0c;正通过“人工智能”行动加速推进技术与实体经济深度融合。 这一背景下&#xff0c;永洪科技凭借其“国内某头部ICT人力资源板块GenAI项目”荣获“2025全国企业…...

基于疾风大模型的新能源储能优化系统:方法、实现与案例分析

一、引言 随着可再生能源渗透率不断提高,储能系统在电力系统中的重要性日益凸显。传统储能控制方法主要基于规则策略和简单优化算法,难以应对高比例新能源场景下的复杂决策需求。本文将详细介绍如何利用疾风大模型(Gale Model)构建智能化的新能源储能优化系统,包含核心方…...