Linux进程状态是指Linux系统中进程所处的不同执行阶段或条件,状态决定了一个进程的后续动作
在Linux中,状态本质上是进程taskt_struct结构体中的一个整形变量
体现在Linux内核源码为:
static const char * const task_state_array[] = { "R (running)", /* 0 */ "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */ "X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };
就绪状态(Ready):进程已经准备好执行但是还没有被处理器调度执行的状态
运行状态(Running):进程正在被处理器执行的状态
阻塞状态(Blocked):进程因为等待某些事件(如等待I/O操作完成、等待信号量等)而被阻塞的状态
等待状态(Waiting):进程因为等待某些事件(如I/O操作完成、信号等)而被阻塞的状态
暂停状态(Paused):进程被暂停执行,可以自行恢复执行
挂起状态(Suspended):进程被操作系统挂起执行,资源被释放,进程状态被保存到磁盘上。挂起状态可以分为内存挂起状态和磁盘挂起状态两种。
终止状态(Terminated):进程执行完毕或者因为某些原因被终止的状态
一个进程已经准备就绪,可以随时被系统调度此时就是运行状态
相当于合并了就绪和运行,统称运行状态
注意:不要认为被调度执行后才算运行状态
我们知道,在等待运行时,进程需要排队等待被调度,就有一个就绪队列
但当内存中存在许多进程时就会使得CPU中的资源不够同时分配给许多进程,于是从就绪队列调度后仍然存在一个队列–运行队列
需要特别注意的是,在Linux中进程的任何排队操作,都是进程的PCB在排队,把所有的进程PCB用数据结构组织起来,就形成了我们的运行队列
这些存在运行队列中的进程都被称作运行状态
一个进程被拿到cup中执行时,并不是等这个进程执行完毕才切换下一个进程
而是这多个进程在一个时间段内所有代码都会被执行 —— 这就叫做【并发执行】
每一个进程执行一个时间片的时间后就会从cpu上拿下来,然后放上下一个进程。这个动作就称作为【进程切换】。
一个时间片通常是10ms左右
1.前台进程
在Shell命令行中输入并执行某条命令,默认情况下会启动一个前台进程
2.后台进程
比如我写了一个死循环,上面的状态显示的是R+
挂起状态是一个重要的概念,它指的是将当前处于运行状态的数据保存在内存中,并让进程或系统等待某个事件的到来再继续执行,这相当于使计算机或进程进入一种睡眠状态
挂起状态分为系统挂起和进程挂起
1.系统挂起
2.进程挂起
3.挂起的场景与特点
一般计算机处于挂起状态的前提是计算机的资源已经比较吃紧了,不得不将一些数据换到磁盘中(磁盘中的swap分区)
阻塞状态是进程在等待某个事件或资源(如I/O操作完成、信号量释放等)时所处的状态。
在这种状态下,进程无法继续执行,因为它需要等待的资源或事件尚未发生。
此时进程不会占用CPU资源,操作系统会调度其他可执行的进程
例如我们平时的命令行等待我们输入命令时就是一个阻塞状态
S(sleeping)在本质上也是阻塞状态的一种,可以说是阻塞的一种分支
S状态是Linux中可中断的睡眠状态,表示进程正在等待某个事件或资源
S状态下的进程可以被信号或中断唤醒并继续执行。
将进程运行起来我们可以看到其是S+的状态,因为命令行此时正在等待用户的输入,遇到I/O操作。
D状态是Linux中不可中断的睡眠状态,也称为磁盘睡眠状态。在这种状态下,进程正在等待磁盘I/O操作或其他无法被中断的硬件操作完成。
进程无法响应任何信号或中断。
进程只能等待操作完成后才能继续执行。
学了挂起状态,我们可以知道,在内存不足时,会通过挂起状态来释放我们的内存
但是如果在极端情况下,即使挂起状态释放内存也不够呢?这时操作系统就没办法了,只能亲自动手开始杀进程了
但是这时用户有一个特别重要的信息要通过进程写入磁盘,但这个进程又刚好被操作系统杀了呢?这可就麻烦了,信息丢失可是个大问题
于是为了保证某些正在干重要事情的进程不被杀掉,给这些进程加了一块“免死金牌” D状态,操作系统在杀进程时,如果进程只是一个普通的‘S’,操作系统可以杀,但如果是‘D’,那么因为有免死金牌,操作系统不能杀
这个免死金牌具体来说就是给该进程的状态设置为深度睡眠状态。那么这个进程就不会被杀死了,当磁盘写完数据后告知进程,那么它就可以将自己放入【运行队列】里去运行,此时它的状态就变成R了
那如果D状态的进程特别多呢?
一般系统中D状态的进程是很少见的,如果你看到了D状态的进程,那就说明你的系统里挂不远了,洗洗睡吧
1.停止状态(T)
这种状态通常发生在以下情况下:
当进程读取一个设备,但是被设备拒绝了,而操作系统又不想杀掉你,此时,为了减少你的非法操作,就会把该进程置为停止状态。
当用户使用Ctrl+Z组合键将一个正在前台运行的进程移至后台时,该进程会进入停止状态
处于暂停状态的进程,不仅仅是等待硬件资源,也可能是等待软件信号等,所以,他也可以被看作是一个阻塞状态的分支
2.进程跟踪状态(t)
T停止状态和S睡眠状态的主要区别在于:
死亡状态就是所有资源释放进程的任务结束了,使这个进程退出,同时把这个进程的资源释放(通过父进程回收)
(就如一个人过世,处理好他的后事)
杀进程的方法:
1.发送9信号
kill -9 PID
2.通过进程名、
killall 进程名
僵死状态形象点说有点像是没死透的死亡状态
进程在死亡后需要将进程的资源给父进程回收
简单说就是父进程既然创建了子进程后,定然会给子进程布置了任务,子进程完成任务后需要将任务的完成情况报备给父进程,否则父进程怎么知道你有没有好好完成任务呢?
因此,进程在退出后,却没有把进程的资源给父进程回收,此时的进程就处于僵死状态,此时的进程又被称作僵尸进程
在系统中每个进程在创建时都会给进程分配一个PID,子进程的PID是父进程用来识别和操作子进程的唯一标识符
在退出时会返回给父进程的一个值,用于表示子进程的退出状态或执行结果。父进程通过读取子进程的退出码来获取子进程的结束信息
父进程回收子进程时会用wait函数回收子进程的PID,并将子进程的退出状态保存在通过第二个参数传递的地址中(如果提供了该参数)
#include #include #include #include #include int main() { pid_t pid=fork();//创建父子进程 if(pid==0) //子进程进 { //....... printf("I am child, pid: %d, ppid: %d\n", getpid(), getppid()); exit(0);//返回退出码 //子进程结束 } //父进程 sleep(15); int status; pid_t p=wait(&status); printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid()); }
在sleep(15)前,子进程结束,但是还没有被父进程回收,显示的是Z+僵死状态
休眠15秒过后,父进程对子进程进行回收,子进程的僵死状态结束
如果一个父进程一直不去不去回收已经退出的子进程的信息,就会造成内存泄露
进程一般退出的时候,它的代码和相关数据会被释放,但是,它的task_struct对象依然还在内存中,仍然占用内存,占用进程表,这些僵尸进程会累积起来,占用大量的进程表项。
进程表是有限资源,当进程表被填满后,系统可能无法再创建新的进程,导致系统资源泄露和性能下降。
前面我们知道,僵尸进程是能够恢复的,只要被父进程回收就行
可万一,子进程好好地,父进程出事了呢?
父进程如果提前退出,子进程后退出,变成僵尸进程之后,那该如何处理呢?这种情况下的进程就称作孤儿进程
但其实这种情况比僵尸进程要好的多,因为在Linux系统中,存在init进程
#include #include #include int main() { pid_t id = fork(); if (id < 0) { perror("fork"); return 1; } else if (id == 0) {//child printf("I am child, pid : %d\n", getpid()); sleep(10); } else {//parent printf("I am parent, pid: %d\n", getpid()); sleep(3); exit(0); } return 0; }
甚至于如果出现了僵尸进程,我们还可以将僵尸进程的父进程杀掉,这样僵尸进程就会变成孤儿进程,这样就能正常被init进程回收
孤儿进程的存在通常不是一个问题,因为系统能够自动处理它们。然而,在某些情况下,如果系统中产生了大量的孤儿进程,并且这些进程长时间运行而不结束,那么它们可能会占用系统资源,影响系统性能