news 2026/5/23 21:34:01

【 linux 】理解进程状态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【 linux 】理解进程状态


目录

1.僵尸进程与孤儿进程

1.1 孤儿进程

1.2 僵尸进程(Z)

2.进程状态

3.进程退出与进程等待

3.1 进程退出

3.2 进程等待

3.2.1 wait和waitpid对比

3.3 WEXITSTATUS 和 WIFEXITED


1.僵尸进程与孤儿进程

1.1 孤儿进程

父进程结束了子进程还没有结束就成为了孤儿进程,linux系统所有孤儿进程会被init进程(进程号为1)自动领养,当孤儿进程执行结束时,init进程会进行回收,孤儿进程依然占据着cpu和内存。

孤儿进程示例

1.2 僵尸进程(Z)

僵尸进程是指子进程已经结束了,但是父进程没有回收子进程。子进程此时不会占据cpu资源和内存,但会占据进程号,系统的进程号是有限的,如果一直占用不回收进程号就不够用了。父进程调用wait/waitpid来回收子进程,如果父进程结束了或者被杀死了,那僵尸进程会变成孤儿进程被系统回收。

僵尸进程示例

可以输入这串命令观察进程状态:

while : ;do ps ajx | head -1&& ps ajx | grep zonbine |grep -v grep ;sleep 1 ;done

2.进程状态

常见的进程状态有运行,阻塞,暂停。

运行状态用R表示,代表程序正在运行或者在队列中等待被调度

阻塞状态分为D(可中断睡眠)和S(不可中断睡眠)S通常为等待输入信号,可以是键盘或者调用了sleep函数。按下ctrl c或者kill进程就会立刻响应D不能被任何信号打断,为了数据保护,大文件拷贝,数据库拷入磁盘等场景。

暂停态分为T和tT为ctrl z主动挂起 t是由调试器(gdb)触发

每一个进程都有一个PCB(进程控制块),PCB是进程存在的唯一标志。具体为一个task_struct的结构体。操作系统为了管理进程,先描述在组织。组织形式就是用红黑树,哈希,链表。一个CPU有一个调度队列,代码和数据是和进程PCB分开的,当进程执行时代码数据会从磁盘加载到内存。

抽象如下

设备也有一个设备队列,结构和调度队列类似,当进程处于阻塞状态时,就会将进程的PCB和等待响应的设备以指针的形式连接,对进程的操作本质上就是对链表的增删改查

抽象如下

3.进程退出与进程等待

3.1 进程退出

进程结束都会有退出码,运行完结果正确退出码为0,运行完结果错误退出码非0,异常退出(退出码无意义)每个进程的PCB都会有退出码的信息,并把退出码返回给父进程,父进程通过wait来获得子进程的退出信息。一共有140个退出信息,可以通过steerror函数查看,echo $?会打印最近一个进程退出时的退出码

0: Success 1: Operation not permitted 2: No such file or directory 3: No such process 9: Bad file descriptor 13: Permission denied 110: Connection timed out ...
3.2 进程等待

父进程可以通过fork创建子进程,每个进程都有自己的pid,可以通过getpid和getppid查看。

fork返回值为0代表子进程,返回值大于0(子进程pid)代表父进程。父子进程是按照时间片交替执行的。如果想让子进程执行完再去执行父进程叫做进程等待,是通过wait系统调用实现的,此时父进程就变成阻塞状态

pid_t waitpid(pid_t pid,int* stream,int options)

第二个参数会存储子进程的退出码,低7个比特位全0,如果非0代表退出码无意义

如果不想让父进程一直处于阻塞状态第三个参数可以设为WNOHANG通常搭配循环来完成。返回值大于0,等待结束;等于0,调用结束但子进程没有退出;小于0,等待失败。父进程需要在一个循环里反复调用来检查状态

#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> int main() { pid_t pid = fork(); if (pid == 0) { printf("子进程(PID: %d)启动,运行3秒...\n", getpid()); sleep(3); printf("子进程工作完成,准备退出。\n"); exit(42); } else { int status; printf("父进程开始非阻塞轮询等待子进程...\n"); while (1) { pid_t ret = waitpid(pid, &status, WNOHANG); if (ret > 0) { printf("父进程成功回收子进程(PID: %d),退出码为: %d\n", ret, WEXITSTATUS(status)); break; } else if (ret == 0) { printf("子进程还没结束,父进程先去处理点其他任务...\n"); sleep(1); } else{ perror("waitpid error"); break; } } printf("父进程所有任务处理完毕,退出。\n"); } return 0; }
3.2.1 wait和waitpid对比

wait是c语言封装的函数,底层调用的就是waitpid。wait只需要传一个参数,其余参数都有默认值,函数原型为pid_t wait(int *wstatus);传入NULL表示父进程不关心子进程怎么退出的,只是单纯的阻塞等待,把子资源回收,传&status搭配WEXITSTATUS 和 WIFEXITED获取退出信息。wait只能等待任意一个子进程,不能像waitpid那样通过第一个参数pid指定等待的子进程。

3.3 WEXITSTATUS 和 WIFEXITED

这是两个宏,通常在一起组合使用来判断进程的退出情况。如果WIFEXITED为真,代表子进程正常退出,WEXITSTATUS打印退出进程的退出码

if (WIFEXITED(status)) { printf("正常退出,退出码: %d\n", WEXITSTATUS(status)); }

status是waitpid函数的第二个参数,使用WEXITSTATUS查看退出码就不需要右移8位了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 21:29:28

使用PythonOpenAI兼容SDK分钟级接入Taotoken全模型

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 使用Python OpenAI兼容SDK分钟级接入Taotoken全模型 对于习惯使用OpenAI官方Python SDK的开发者来说&#xff0c;接入Taotoken平台…

作者头像 李华
网站建设 2026/5/23 21:27:17

QML 性能优化的“黑魔法”:为什么你一定要了解 `layer.enabled`?

QML 性能优化的“黑魔法”&#xff1a;为什么你一定要了解 layer.enabled&#xff1f; 在 Qt/QML 开发中&#xff0c;我们追求极致的交互体验。但你是否遇到过这种情况&#xff1a;仅仅是给一个复杂的页面加了一个简单的透明度淡入动画&#xff0c;整个界面就开始疯狂掉帧&…

作者头像 李华
网站建设 2026/5/23 21:26:30

为什么视频代剪辑的质量会影响内容传播效果

为什么你精心拍摄的视频&#xff0c;观众却划走了&#xff1f; 你是否也遇到过这样的情况&#xff1a;花了一整天拍摄Vlog&#xff0c;素材画面清晰、光线充足&#xff0c;可剪出来发到平台后&#xff0c;播放量寥寥无几&#xff1f;或者婚礼当天感动全场的瞬间&#xff0c;回看…

作者头像 李华
网站建设 2026/5/23 21:24:16

城市地下管网可视化监控管理系统方案

城市地下管网涵盖给水、排水、燃气、热力、电力及通信等管线&#xff0c;是保障城市正常运转的“生命线”和“血脉”。随着我国城镇化进程的加快&#xff0c;目前我国城市地下管网规模已位居全球第一&#xff0c;总长度超过378万公里。 然而&#xff0c;规模庞大的同时&#xf…

作者头像 李华
网站建设 2026/5/23 21:23:07

基于 RV1126B 评估板的 Linux 系统开发详解(二)

2 U-Boot开发 2.1 U-Boot源码说明 进入U-Boot源码目录&#xff0c;执行如下命令&#xff0c;查看U-Boot源码目录。 Host# ls -l 2.2配置U-Boot U-Boot可使用menuconfig进行配置&#xff0c;请参考“配置内核选项”章节&#xff0c;配置menuconfconfig所需依赖环境。 在Lin…

作者头像 李华