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

Linux进程控制

目录

 进程终止

一、进程退出场景

二、进程常见退出方法

进程等待

为什么有进程等待

什么是进程等待

怎么做到进程等待

wait

waitpid


 进程终止


 
在 Linux 编程的世界里,进程的生命周期管理是极为关键的内容,而进程终止作为其中一个重要环节,有着丰富的细节和不同的实现方式。今天,我们就深入探讨 Linux 进程终止的相关知识,带你全面了解进程退出场景、常见退出方法以及背后的原理 。
 

一、进程退出场景
 


(一)代码运行完毕,结果正确(不关心)
 
(二)代码运行完毕,结果不正确
 
虽然代码完整地执行了,但由于算法逻辑瑕疵、数据处理失误等原因,导致最终结果与预期不符。例如,在一个排序程序中,因排序算法实现有误,输出的序列并非有序的,进程虽然运行结束,但结果不正确,这提示我们需要去排查程序的逻辑错误。
 
(三)代码异常终止
 

这种情况就比较糟糕了,进程在运行过程中遇到了意外状况,无法继续正常执行而被迫终止。常见的如访问了非法内存地址( segmentation fault )、除零错误( floating point exception )等,系统会发送相应信号,让进程异常退出,以此来保护系统的稳定性和安全性 。此时不关心退出码,更关心为什么抛异常(进程收到对应信号)

进程是否正确可以通过return返回值进行判断,

返回值表示含义如下


 


二、进程常见退出方法
 


(一)正常终止
 
正常终止的进程,我们可以通过在终端执行  echo $?  命令来查看进程退出码,退出码能反映进程的退出状态。主要有以下几种方式:
 
1. 从 main 返回
 
在 C 语言程序中, main  函数是程序的入口,当  main  函数执行到  return  语句时,进程会正常终止。 return  的值会作为进程的退出码,父进程可以通过相关机制获取这个值,以此了解子进程的运行结果。例如:
 

#include <stdio.h>
int main() {printf("This is a simple program.\n");return 0; // 进程正常终止,退出码为 0
}


这里  return 0  表示程序正常运行结束,若  return  其他非零值,一般可用来表示程序运行中出现了某种自定义的“异常”情况,方便父进程做相应处理。
 
2. 调用 exit
 
 exit  函数是一个常用的进程正常终止函数,其声明为:

 
#include <stdlib.h> 
void exit(int status);

(注意:头文件是  <stdlib.h>  ,不是文中提到的  <unistd.h>  ,文中此处可能笔误 )
 exit  函数在使进程终止前,会做一系列清理工作:
3. _exit
 
 _exit  函数的声明为:
 

#include <unistd.h>
void _exit(int status);


 
 
参数  status  定义了进程的终止状态,父进程通过  wait  系列函数来获取该值。需要注意的是,虽然  status  是  int  类型,但仅有低 8 位可以被父进程所用。所以当  _exit(-1)  时,在终端执行  echo $?  会发现返回值是 255(因为 -1 的补码在低 8 位表示为 255 )。 _exit  函数相对比较“直接”,它不会像  exit  那样做复杂的清理工作,只是简单地终止进程,关闭进程相关的资源,把状态返回给父进程。
 
我们来看一个对比  exit  和  _exit  行为的实例:


示例 1:使用 exit 的情况
 

#include <stdio.h>
#include <stdlib.h>
int main() {printf("dlihmr"); exit(0); 
}


这里  printf("hello");  的输出内容,因为  exit  会刷新缓冲区,所以 “hello” 会被输出到终端。
 
示例 2:使用 _exit 的情况
 

#include <stdio.h>
#include <unistd.h>
int main() {printf("dlihmr"); _exit(0); 
}


 
 由于  _exit  不会刷新缓冲区, printf  输出的 “hello” 还在缓冲区中,没来得及输出到终端,进程就终止了,所以看不到输出内容 。缓冲区一定不在内核,而是在用户空间。
 echo $?是返回上级最近进程的退出码

errno的使用

errno  是记录系统最后一次错误代码的机制 ,它本质是一个 int  类型的值,在 <errno.h>  头文件中定义 。当 Linux C 等环境下的 API 函数发生异常时,通常会将 errno  变量(使用时需包含 errno.h  头文件 )赋予一个整数值,不同数值对应不同错误含义,借助它能推测程序出错原因,是调试程序的重要手段。比如调用 open  函数打开文件失败,可通过查看 errno  知晓是文件不存在、权限不足等具体问题 。


(二)异常退出


 
最常见的就是我们在终端按下  ctrl + c  ,这会向进程发送  SIGINT  信号(中断信号 ),进程接收到这个信号后会异常终止。当然,系统还有其他多种信号可以让进程异常退出,比如  SIGSEGV (段错误信号 )、 SIGFPE (浮点异常信号 )等,当进程触发相应的错误条件时,系统会发送这些信号,促使进程异常退出,以此来告知用户或父进程,程序运行出现了严重问题 。
 结合kill演示

给予对应的kill退出指令,进程会按要求结束返回错误码
三、return 退出补充说明
 
 return  是在函数中常用的退出方式,在  main  函数中,执行  return n  等同于执行  exit(n)  。因为调用  main  函数的运行时函数(可以简单理解为系统启动  main  执行的相关底层逻辑 )会将  main  的返回值当做  exit  的参数,进而按照  exit  的流程去终止进程。例如:
 

#include <stdio.h>
int main() {printf("Program is ending...\n");return 2; // 等同于 exit(2),进程终止,退出码为 2
}


 这样的设计让我们在编写简单程序时,使用  return  就能方便地实现进程的正常终止,并且传递退出码 。
 
四、总结
 
进程终止在 Linux 进程管理中占据重要地位,不同的退出场景对应着程序不同的运行结果状态。而  return (在  main  中 )、 exit 、 _exit  等不同的退出方法,有着各自的特点和适用场景。 _exit  简单直接,适合对清理要求不高、追求快速终止的情况; exit  因为具备丰富的清理机制,在一般的应用程序中,当需要妥善处理资源释放、保证数据完整性时,更为常用; return  则在  main  函数中提供了简洁的退出方式,和  exit  有着紧密的联系 。
 
 

进程等待

为什么有进程等待

僵尸进程无法杀死,需要进程等待来消灭他,进而解决内存泄漏问题--必须解决的

我们要通过进程等待,获得子进程退出情况--知道我们给子进程的任务,他完成的怎么样--不必要

什么是进程等待

通过调用wait/waitpid,来进行堆子进程进行状态检测与回收功能!


怎么做到进程等待

代码:

原理:

 我们发现,子进程在运行结束后成为僵尸进程,状态Z。

父进程通过调用wait/waitpid,来进行僵尸进程的回收问题。

wait

 

运行结果

我们发现5s后子进程变为僵尸进程,当再过5s父进程结束wait等待回收子进程pid,子进程成功释放。

因此,进程等待是必须的(回收僵尸进程,避免内存泄漏)

wait是等待任意子进程

父进程执行for循环,子进程内部创建

回收操作 

子进程运行结束变为僵尸进程之后成功回收 

如果子进程一直不死,那么父进程也不死,默认wait时,调用这个系统调用时不返回,默认叫做阻塞状态。(类似于scanf等待输入操作)

waitpid

wait的功能waitpid子集 

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
 当正常返回的时候waitpid返回收集到的子进程的进程ID;
 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
 pid:
 Pid=-1,等待任一个子进程。与wait等效。
 Pid>0.等待其进程ID与pid相等的子进程。
 status:
 WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)


 WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
 options:
 WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。(见下文第三个参数

第一个参数:等谁

第二个参数:获取进程退出结果(输出型参数

操作系统会把id进程推出信息拷贝到status。上文exit(1)

 

但是status结果是256 

子进程退出场景有三种,父进程期望获得子进程什么信息?

1.子女进程代码是否异常

2.没有异常,结果对吗?exitcode,不对为什么?通过退出码表示

statu返回256原因:

status36位,前8位表示进程是否出异常,收到什么样的信号,第8位cure dump

信号表,从1开始,若1-7位是不是0,说明 代码是否跑完

8-16表示退出状态,及退出码。1+0000 0000==256。通过statu反应代码是否异常。

父进程想获取子进程信息为什么需要系统调用,直接传全局变量不好吗??(进程独立性)。

位操作查看(区分256和1)

代码

结果 

想让waitpid错误返回示例(父进程等待的子进程不是自己的) 

第三个参数 (设置等待方式)

--0:阻塞:子进程退出不满足条件,父进程等待(阻塞,不被调用)

非阻塞轮询。(父进程等待过程中可以做别的事,不会一直扽等子进程)

演示:

 

http://www.lqws.cn/news/213679.html

相关文章:

  • ZephyrOS 嵌入式开发Black Pill V1.2之Debug调试器
  • JAVA——反射
  • Windows 系统安装 Redis 详细教程
  • nginx日志的一点理解
  • Xxl-job——源码设计思考
  • Kerberos面试内容整理-未来发展趋势
  • 【大模型】大模型RAG(Retrieval-Augmented Generation)面试题合集
  • 解密LSTM(长短期记忆网络):让机器拥有记忆力的魔法网络
  • 【PhysUnits】15.17 比例因子模块 (ratio.rs)
  • 第二部分 方法,还是方法——“信管法则”的四大要点
  • 号外!PLC和安川伺服,通过Profinet转EtherCAT网关同步多个工作站的运动
  • SpiritTools:一款小而精的实用工具箱
  • 20250607在荣品的PRO-RK3566开发板的Android13系统下实现长按开机之后出现插入适配器不会自动启动的问题的解决
  • 20250607在荣品的PRO-RK3566开发板的Android13的uboot中使用gpio命令来配置GPIO的状态
  • 【Hugging Face】实践笔记:Pipeline任务、BERT嵌入层、Train任务、WandB解析
  • Python 训练营打卡 Day 38-Dataset和Dataloader类
  • Pytorch学习——自动求导与计算图
  • Spring AI与Spring Modulith核心技术解析
  • 如何判断指针是否需要释放?
  • [面试精选] 0104. 二叉树的最大深度
  • 初识redis
  • Kafka 消息模式实战:从简单队列到流处理(一)
  • c++ 静态成员变量
  • 《高精度》题集
  • 【题解-洛谷】B3622 枚举子集(递归实现指数型枚举)
  • 【Latex】Windows/Ubuntu 绘制 eps 矢量图通用方法(drawio),支持插入 Latex 数学公式
  • 一款“短小精悍的”手机录屏软件
  • 安达发|装饰材料行业APS生产排程软件:破解生产困局,智造升级新引擎
  • Java高级 |【实验八】springboot 使用Websocket
  • Spring中循环依赖问题的解决机制总结