目次
使用体系中
运行状态
阻塞状态
历程状态转换
Linux体系中
检察历程状态
深度睡眠状态
T 停息状态
Z 僵尸状态
孤儿状态
文章手稿
xmind:
文章手稿可见文末
弁言
介绍体系中的历程状态及其管理方式。将通过结合使用体系原理和现实代码示例,详细阐明历程的各种状态、转换过程以及处理惩罚方法。
使用体系中
一个历程通常有三种状态
- 就绪状态(Ready):表现历程已经具备运行所必要的齐备条件,只必要等待CPU的分配就可以运行。历程处于就绪状态时,通常会被添加到就绪队列,等待调治器分配CPU资源。
- 运行状态(Running):表现历程正在被CPU实行。处于运行状态的历程正在使用CPU进行盘算或其他使用。
- 阻塞状态(Blocked):表现历程由于某些缘故原由暂时无法继承实行,必要等待一些特定条件的排除之后才能继承运行。比方,当历程等待I/O使用完成大概等待某个资源可用时,会转入阻塞状态。历程在阻塞状态时,通常会被移动到阻塞队列中,等待条件的满足。
我们下面将对运行,阻塞,和阻塞挂起进行介绍~
运行状态
历程只要在运行队列中,就叫做 运行态。
阻塞状态
关于历程:
① 一个历程使用资源的时候,可不但仅是在申请 CPU 资源
② 历程大概会申请其它资源:磁盘、网卡、显卡,表现器资源……
假如我们申请 CPU 资源无法暂时无法得到满足,这就必要排队的 "运行队列" 。那么假如我们申请其他慢设备的资源呢?是必要排队的(task_struct 在历程排队)。
当访问某些资源(磁盘,网卡等),假如该资源暂时没有准备好,大概正在给其他历程提供服务,那么此时:
① 当前历程要从 runqueue 中逐出。
② 将当前历程放入对应设备的形貌结构体中的waitqueue 。
历程状态:看PCB在哪个队列
内存不敷了,使用体系就会把 该历程的代码和数据置换到磁盘上,进行 历程挂起。
历程状态转换
历程状态的转换可以通过以下示例阐明:
- #include <stdio.h>
- #include <unistd.h>
- int main() {
- while (1) {
- printf("进程[%d]正在运行...\n", getpid());
- sleep(1); // 模拟阻塞状态
- }
- return 0;
- }
复制代码 通过运行上述代码并观察历程状态,可以明白历程在不同状态之间的转换过程。
三种状态的图示如下:
Linux体系中
历程状态用整数表现,这些整数存储在历程的task_struct结构体中。常见的历程状态包括:运行(R)、睡眠(S)、磁盘睡眠(D)、停止(T)、死亡(X)、僵尸(Z)和孤儿历程。
历程状态一览
状态代码状态名称形貌R运行(Running)历程正在运行或在运行队列中等待S睡眠(Sleeping)历程在等待某事件完成,可被信号叫醒D磁盘睡眠(Disk Sleep)历程在等待I/O使用完成,不可被信号叫醒T停止(Stopped)历程被停息,可通过信号规复X死亡(Dead)历程已终止,从历程列表中移除Z僵尸(Zombie)历程已退出,父历程尚未读取其状态孤儿(Orphan)父历程已退出,被init历程收养 检察历程状态
使用ps aux或ps axj命令可以检察体系中历程的状态。比方:
这些命令输出的状态字段展示了历程当前的状态。
背后的缘故原由让人暖心,cpu太快了,print表现器等待的时间在他看来就是在sleep了
深度睡眠状态
这个D状态我们就不模拟了……大概会把我的机子磁盘打满(害怕.dog)
T 停息状态
比如看视频,听音乐,下载,都会有停息。当你点击停息的时候下载对应的代码就不跑了,此时这个历程你就可以以为是停息状态。
再比如说我们调试程序,让程序打断点之后让程序运行起来,程序在打断点处停住的时候是将历程停息了,以是你在gdb 调试或在 VS 下调试时你会发现程序会停下来,这就是停息。
是历程挂起的一种。
我们可以先来看一下kill
接下来可以来尝试一下;
$ kill -19 4026,就会发现
gdb下的停息状态,测试一下
- $ gdb process # 进入gdb调试
- (gdb) l # 查看代码
- (gdb) b 9 # 打断点
- q + 回车 # 退出
复制代码
Z 僵尸状态
僵尸状态:当一个 Linux 中的历程退出的时候,一般不会直接进入 X 状态(死亡,资源可以立马被回收),而是进入 Z 状态。
为什么呢~
历程为 Z 状态,就是为了维护退出信息,可以让父历程大概 OS 读取记录的,退出信息会写入 test_struct。
以下是创建僵尸历程的代码示例:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main() {
- pid_t id = fork();
- if (id < 0) {
- perror("fork");
- return 1;
- } else if (id == 0) { // 子进程
- printf("子进程[%d]开始运行...\n", getpid());
- sleep(5);
- printf("子进程[%d]退出...\n", getpid());
- exit(0);
- } else { // 父进程
- printf("父进程[%d]正在睡眠...\n", getpid());
- sleep(30); // 父进程延迟回收子进程
- }
- return 0;
- }
复制代码 我们可以写一个监控脚本来看一下~
- while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1; echo "######" ; done
复制代码
在另一个终端中运行ps命令可以看到子历程进入僵尸状态。
僵尸历程的危害
僵尸历程虽然不再运行,但它们仍然占用体系资源(如历程控制块task_struct)。假如父历程不及时回收子历程,会导致体系资源浪费,甚至内存泄漏。
避免僵尸历程
可以通过以下方式:
- 父历程及时调用wait()或waitpid()回收子历程。
- 使用信号处理惩罚机制,在子历程退出时通知父历程进行回收。
孤儿状态
孤儿历程:父亲没了(bushi
即:父历程先退出了,子的父就酿成1 号历程了,相称于被os领养了
测试一下:
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- int main(void) {
- pid_t id = fork();
- if (id == 0) {
- // child
- int cnt = 5;
- while (1) { // 死循环,孩子进程就不退了
- printf("我是子进程,我还剩下 %ds\n", cnt--);
- sleep(1);
- }
- printf("我是子进程,我已经变僵尸了,等待被检测\n");
- exit(0);
- }
- else {
- // father
- int cnt = 3;
- while (cnt) {
- printf("我是父进程,我: %d\n", cnt--);
- sleep(1);
- }
- exit(0);
- }
- }
复制代码 监控一下:
- while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep; sleep 1;echo "######";done
复制代码
我们可以top看一下1究竟是什么
❓ 疑问:父历程退出,为什么父历程没有酿成僵尸?我们怎么没有看到父历程 为Z ?
- 那是由于父历程的父历程是bash ,它会自动回收它的子历程,也就是这里的父历程。这里之以是没有看到父历程酿成僵尸,是由于被 bash 回收了, z->x 的状态很快,以是你没看到。
- 那为什么刚才我自己代码中的父历程创建的子历程,父历程没有回收子历程呢?那是由于你的代码压根就没有写回收,以是你的子历程就没有回收。
那我们怎么停息呢,ctrl+c 只能干掉前台历程,
以是孤儿历程就要用到我们的杀历程:kill -9来停息啦
文章手稿
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |