【Linux探索学习】第十七弹——进程终止:深入解析操作系统中的进程终止机制

Linux学习笔记:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在操作系统中,进程终止是一个至关重要的阶段,它标志着进程的生命周期结束。进程终止可能是因为任务完成,也可能是因为异常或外部干预。本文将详细讲解操作系统中的进程终止相关知识,包括终止的原因、类型、实现方式、Linux系统中的具体操作,以及其影响和管理策略,并配以表格和代码示例,帮助全面掌握这一主题。


目录

一、什么是进程终止?

二、进程终止的主要原因

三、进程终止的类型

四、Linux中的进程终止实现

4.1 运行完毕且正常终止

4.1.1 使用return终止进程

4.1.2 使用exit终止进程

4.2 errno常量和strerror函数

4.2.1 strerror函数

4.2.2 errno常量

4.3 异常终止:abort

4.4 强制终止:kill

4.4 子进程资源回收:wait 和 waitpid

五、进程终止的影响

5.1 资源释放

5.2 僵尸进程

如何避免僵尸进程?

六、信号与进程终止

常见信号与作用

示例代码:捕获SIGTERM信号

七、进程终止的常见问题与解决

7.1 僵尸进程问题

7.2 非预期终止

八、总结


一、什么是进程终止?

进程终止(Process Termination)是操作系统中进程生命周期的最后一个阶段,意味着操作系统回收该进程的所有资源,包括内存、文件描述符、CPU时间等,使这些资源可以被其他进程使用。


二、进程终止的主要原因

进程可能因多种原因终止:

终止原因描述
正常终止进程完成所有任务后自然结束,例如程序执行到return语句或调用exit函数。
异常终止由于未处理的错误或异常导致进程终止,例如除以零、非法访问内存等。
外部干预进程被操作系统或其他进程强制终止,例如接收到SIGKILL信号。
父进程终止当父进程终止且子进程未被接管时,子进程可能成为孤儿进程,由initsystemd进程接管。
资源耗尽进程因超出系统资源限制(如内存、文件句柄等)被操作系统强制终止。

三、进程终止的类型

进程终止根据触发方式可以分为以下几类:

类型触发方式常见场景
正常终止调用exit()、返回主函数程序完成任务后自然结束。
异常终止未处理的错误或调用abort()例如访问非法地址、未处理的信号等。
强制终止外部进程调用kill()、操作系统干预父进程发送SIGKILL信号或管理员手动终止进程。
核心转储终止错误导致生成核心转储文件例如段错误(SIGSEGV)导致的异常。

一般进程终止的场景包含一下三种:

1. 代码运行完毕,结果正常

2. 代码运行完毕,结果不正常

3. 代码异常终止

下面我们会对上面的内容做出讲解


四、Linux中的进程终止实现

在Linux操作系统中,进程终止主要通过以下系统调用和信号实现:

4.1 运行完毕且正常终止

4.1.1 使用return终止进程

我们平时用的最多的方式就是return,我们先来看下面一个简单的代码

  #include<stdio.h>                    int main()   {   printf("This is a test message");return 0;}        

我们平时所写的代码main函数中一般都有一个返回值,那么这个返回值是干什么的呢?

main函数返回值是返回给进程看的,本质表示:进程运行完成时是否是正确的结果,如果是一般返回0,如果不是返回其它数字代表不同的退出信息(退出码)

我们可以通过这个指令打印退出码:

echo $?

4.1.2 使用exit终止进程

exit系统调用用于正常终止进程,并返回一个状态码给操作系统或父进程。

我们使用exit一般是在进程正常终止但没有正常执行的场景,或者是在合适的地方进行截停的场景,我们来看下面一段代码:

#include<stdio.h> 
#include<stdlib.h>                                                                                                                                                                                           
void print()      
{                 printf("hello linux\n");printf("hello linux\n");printf("hello linux\n");printf("hello linux\n");exit(20);     printf("hello linux\n");printf("hello linux\n");
}                 
int main()        
{                 print();      return 10;    
}                 

我们来看一下上面内容的执行结果和返回值:

我们发现返回值是exit中的返回值,并不是return的返回值,而且打印也只执行了四行,所以我们可以知道带有exit的进程,在执行到它时会直接返回,并不会再继续执行后面的内容,返回值也返回exit的返回值,这一点与return有较大差别


4.2 errno常量和strerror函数

4.2.1 strerror函数

上面我们提到我们可以通过不同的退出码来代表不同的错误信息,那么不同的退出码究竟各自代表什么信息呢?我们可以通过strerror函数来查看

比如我们来看一下退出码0到10所代表的信息:

#include<stdio.h>
#include<string.h>
int main()
{for(int i=0;i<=10;i++){printf("%d: %s\n",i,strerror(i));}return 0;
}                

运行结果:

4.2.2 errno常量

上面我们讲到进程在退出是会有退出码,我们可以通过echo来查看退出码,那我们如何获取呢?

C/C++中其实还定义了一个叫errno的常量来记录错误码

所以我们就可以将errno常量与strerror函数结合使用,用errno来记录进程的错误码,然后传给strerror函数得到错误信息,比如下面的例子:

#include<stdio.h> 
#include<unistd.h>                                                                                                                                                                                           
#include<string.h>
#include<stdlib.h>
#include<errno.h>     //注意要带好头文件
int main()        
{                 int ret=0;    char *p=(char*)malloc(1000*1000*1000*4);    //这个扩容肯定会出错的,因为扩容空间太大了if(p==NULL)   {             printf("mallo error, %d:%s\n",errno,strerror(errno));   //errno会记录错误码,将它传到strerror中就可以得到错误信息ret=errno;    //将错误码作为返回值返回,从而让父进程得到返回信息}             else          {             printf("malloc success\n");}             return ret;   
}                 

运行结果:


4.3 异常终止:abort

4.2和4.3都牵扯到了信号的内容,这里我们主要还是以了解为主,后期我们会详细讲解信号的知识

abort函数用于非正常终止进程,通常在遇到不可恢复的错误时调用。

示例代码:abort

#include <stdlib.h>
#include <stdio.h>int main() {printf("遇到严重错误,程序终止!\n");abort();  // 异常终止return 0;  // 不会被执行
}

调用abort会产生一个信号(SIGABRT),通常会生成一个核心转储文件供调试使用。


4.4 强制终止:kill

kill系统调用或命令用于向目标进程发送信号,例如SIGKILL信号会立即强制终止目标进程。

示例代码:kill

#include <signal.h>
#include <unistd.h>
#include <stdio.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程while (1) {printf("子进程正在运行: PID = %d\n", getpid());sleep(1);}} else if (pid > 0) {// 父进程sleep(5);printf("终止子进程: PID = %d\n", pid);kill(pid, SIGKILL);  // 发送SIGKILL信号}return 0;
}

执行结果:

我们发现子进程在被执行了五次后被终止掉了,对应的就是代码中执行了五次后会执行kill指令杀死进程


4.4 子进程资源回收:waitwaitpid

进程等待与回收我们会在下一篇详细讲解

当子进程终止后,其状态会保留在系统中,直到父进程回收。这种未被回收的子进程称为僵尸进程

示例代码:回收子进程资源

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {// 子进程printf("子进程开始: PID = %d\n", getpid());sleep(2);printf("子进程结束\n");} else if (pid > 0) {// 父进程int status;wait(&status);  // 等待子进程终止if (WIFEXITED(status)) {printf("子进程正常终止,状态码: %d\n", WEXITSTATUS(status));}}return 0;
}

执行结果:


五、进程终止的影响

5.1 资源释放

进程终止后,操作系统会回收以下资源:

  • 内存:代码段、数据段、堆栈等。
  • 文件描述符:关闭该进程打开的所有文件。
  • CPU时间:释放进程的时间片。

5.2 僵尸进程

当子进程终止但父进程未调用waitwaitpid回收其状态时,子进程会变成僵尸进程

如何避免僵尸进程?
  1. 父进程调用waitwaitpid回收子进程。
  2. 使用信号处理机制,如捕获SIGCHLD信号。

六、信号与进程终止

常见信号与作用

信号描述默认行为
SIGKILL强制终止进程,无法捕获或忽略。终止
SIGTERM请求终止进程,进程可以选择捕获或忽略。终止
SIGABRT异常终止进程,通常由abort触发。终止并生成核心转储
SIGCHLD子进程终止或停止时通知父进程。忽略
示例代码:捕获SIGTERM信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>void handle_sigterm(int sig) {printf("接收到SIGTERM信号,进程终止\n");exit(0);
}int main() {signal(SIGTERM, handle_sigterm);  // 注册信号处理函数while (1) {printf("程序正在运行...\n");sleep(2);}return 0;
}

七、进程终止的常见问题与解决

7.1 僵尸进程问题

问题:父进程未回收子进程,导致系统中出现僵尸进程。

解决

  • 调用waitwaitpid
  • 使用SIGCHLD信号处理函数自动回收。

7.2 非预期终止

问题:进程意外终止导致数据未保存。

解决:通过信号处理函数捕获终止信号,并在终止前完成必要的清理工作。


八、总结

进程终止是操作系统中管理资源的重要环节。通过本文的讲解,我们了解了进程终止的主要原因、类型以及Linux中的具体实现方式。进程终止不仅影响单个进程的生命周期,还对系统资源的利用和稳定性产生重要影响。

通过合理地使用exitkillwait等系统调用,以及信号机制,可以高效管理进程终止,避免僵尸进程问题,提高系统性能和可靠性。希望通过本文的详细分析和代码示例,你能更加深入理解操作系统的这一重要机制!

本篇笔记:


感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

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

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

相关文章

Vue 3 组件通信教程

Vue 3 组件通信教程 1. Props 父传子 1.1 基础用法 在 Vue 3 中&#xff0c;我们使用 defineProps 来声明组件的 props&#xff1a; <!-- 子组件 ChildComponent.vue --> <script setup> const props defineProps({message: String,count: {type: Number,requ…

MySQL更新JSON字段key:value形式

MySQL更新JSON字段key:value形式 1. 介绍 ‌MySQL的JSON数据类型‌是MySQL 5.7及以上版本中引入的一种数据类型&#xff0c;用于存储JSON格式的数据。使用JSON数据类型可以自动校验文档是否满足JSON格式的要求&#xff0c;优化存储格式&#xff0c;并允许快速访问文档中的特定…

javax.xml.ws.soap.SOAPFaultException: ZONE_OFFSET

javax.xml.ws.soap.SOAPFaultException 表示 SOAP 调用过程中发生了错误&#xff0c;并且服务端返回了一个 SOAP Fault。 错误信息中提到的 ZONE_OFFSET 可能指的是时区偏移量。在日期和时间处理中&#xff0c;时区偏移量是指格林威治标准时间 (GMT) 的偏移量。如果服务期望特…

软路由设置ip地址实现一机一IP

软路由作为一种灵活且强大的网络设备&#xff0c;越来越受到家庭和小型企业用户的青睐。通过软路由配置代理IP&#xff0c;不仅可以提升网络性能&#xff0c;还能保护隐私和实现更多高级功能。本文将详细介绍如何在软路由中配置代理IP&#xff0c;帮助你轻松实现更高效的网络管…

介绍一下strupr(arr);(c基础)

hi , I am 36 适合对象c语言初学者 strupr(arr)&#xff1b;函数是把arr数组变为大写字母 格式 #include<string.h> strupr(arr); 返回值为arr 链接分享一下arr的意义(c基础)(必看)(牢记)-CSDN博客 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #incl…

【VRChat 全身动捕】VIVE 手柄改 tracker 定位器教程,低成本光学动捕解决方案(持续更新中2024.11.26)

更新 0.0.1&#xff08;2024/11/26&#xff09;&#xff1a; 1.解决了内建蓝牙无法识别、“steamVR 蓝牙不可用” 的解决方案 2.解决了 tracker 虽然建立了连接但是在 steamVR 界面上看不到的问题 3.解决了 VIVE 基站1.0 无法被蓝牙识别 && 无法被 steamVR 搜索到 &…

C++设计模式之组合模式中如何实现同一层部件的有序性

在组合模式中&#xff0c;为了实现同一层上部件的有序性&#xff0c;可以采取以下几种设计方法&#xff1a; 1. 使用有序集合 使用有序集合&#xff08;如 std::list、std::vector 或其他有序容器&#xff09;来存储和管理子部件。这种方法可以确保子部件按照特定顺序排列&am…

Web 端语音对话 AI 示例:使用 Whisper 和 llama.cpp 构建语音聊天机器人

大语言模型&#xff08;LLM&#xff09;为基于文本的对话提供了强大的能力。那么&#xff0c;能否进一步扩展&#xff0c;将其转化为语音对话的形式呢&#xff1f;本文将展示如何使用 Whisper 语音识别和 llama.cpp 构建一个 Web 端语音聊天机器人。 系统概览 如上图所示&…

网络地址转换

NAT概述 解决公有地址不足&#xff0c;并且分配不均匀的问题 公有地址&#xff1a;由专门的机构管理、分配&#xff0c;可以在因特网上直接通信 私有地址&#xff1a;组织和个人可以任意使用&#xff0c;只能在内网使用的IP地址 A、B、C类地址中各预留了一些私有IP地址 A&…

电脑无互联网连接怎么解决?分享5种解决方案

无互联网连接是指设备无法与互联网进行通信或连接失败。这可能会导致我们无法正常上网&#xff0c;给我们的日常生活和工作带来很大的不便。但请不要担心&#xff0c;下面将为您介绍一些解决无互联网连接问题的方法。 一、检查网络是否正常连接 首先&#xff0c;确保您的路由器…

使用 F5 TTS 文字转音频

F5 TTS 支持 ZeroShot 音频克隆&#xff0c;只有将需要音频传给模型&#xff0c;模型既可以生成以对应声音生成的音频&#xff0c;F5 最强大的地方就是可以使用定制的人声。F5 使用了 DIT 架构进行训练&#xff0c;结构如下&#xff1a; 本地使用 F5 TTS F5 使用很简单&#x…

【Redis】Redis 预备知识

目录 1. 基本全局命令 KEYS EXISTS DEL EXPIRE TTL TYPE 2. 数据结构和内部编码 3. 单线程架构 Redis 提供了5种数据结构&#xff0c;理解每种数据结构的特点对于 Redis 开发运维非常重要&#xff0c;同时掌握每种数据结构的常见命令&#xff0c;会在使用 Redis 的时…

【从零开始的LeetCode-算法】3304. 找出第 K 个字符 I

Alice 和 Bob 正在玩一个游戏。最初&#xff0c;Alice 有一个字符串 word "a"。 给定一个正整数 k。 现在 Bob 会要求 Alice 执行以下操作 无限次 : 将 word 中的每个字符 更改 为英文字母表中的 下一个 字符来生成一个新字符串&#xff0c;并将其 追加 到原始的…

云原生革命:构建未来应用的无限可能

在这个数字化飞速发展的时代&#xff0c;云原生技术如同一股不可阻挡的潮流&#xff0c;正深刻改变着软件开发和部署的方式。它不仅仅是一种技术变革&#xff0c;更是一场关于如何更高效、更灵活地构建和运行应用的革命。今天&#xff0c;我们就来深入探讨云原生的魅力所在&…

软件设计模式复习

一、软件生存周期 二、软件开发过程模型 瀑布模型 特征&#xff1a; 从上一阶段承接的成果物作为本阶段的工作对象&#xff1b; 对上一阶段成果实施本阶段的活动&#xff1b; 给出本阶段的成果&#xff0c;作为下一阶段的输入&#xff1b; 对本阶段的工作进行评审&#xff0c…

centos7 yum install 失败,mirrorlist.centos.org连接不上

由于centos7停止支持,导致mirrorlist.centos.orgdns解析都是失效啦,yum命令没法安装程序. 换一个镜像源就好 sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/…

搭建文件服务器并使用Qt实现文件上传和下载(带账号和密码)

文章目录 0 背景1 搭建文件服务器2 代码实现文件上传和下载2.1 在pro文件中添加网络支持2.2 创建网络管理类2.3 文件上传2.4 文件下载 3 扩展&#xff08;其他方法实现文件上传和下载&#xff09;3.1 python3.2 npm3.3 ftp服务器 4 完整的代码 0 背景 因为需要使程序具备在远程…

JVM 常见面试题及解析(2024)

目录 一、JVM 基础概念 二、JVM 内存结构 三、类加载机制 四、垃圾回收机制 五、性能调优 六、实战问题 七、JVM 与其他技术结合 八、JVM 内部机制深化 九、JVM 相关概念拓展 十、故障排查与异常处理 一、JVM 基础概念 1、什么是 JVM&#xff1f;它的主要作用是…

自动化运维(k8s)之微服务信息自动抓取:namespaceName、deploymentName等全解析

前言&#xff1a;公司云原生k8s二开工程师发了一串通用性命令用来查询以下数值&#xff0c;我想着能不能将这命令写成一个自动化脚本。 起初设计的 版本一&#xff1a;开头加一条环境变量&#xff0c;执行脚本后&#xff0c;提示输入&#xff1a;需要查询的命名空间&#xff0c…

鸿蒙一次开发,多端部署,响应式布局

鸿蒙一次开发&#xff0c;多端部署&#xff0c;响应式布局 一、定义屏幕相关常量 BreakpointConstants.ets import BreakpointType from ../bean/BreakpointType export default class BreakPointConstants{/*** 小屏幕设备的Breakpoints 标记*/static readonly BREAKPOINT_…