
各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。 如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步! 也欢迎关注我的blog主页: 落羽的落羽
进程,是有不同的状态的。
在Linux内核源码中能看到这样的代码:

实际上这些就是进程不同的状态。
查询进程状态,使用ps命令,有一列stat,即代表进程的状态

进程的运行队列,是操作系统内核中管理进程的数据结构,通常是一个链表的队列,存放着的就是进程的task_struct,用于记录所有等待CPU调度的进程,方便快速选择下一个执行的进程。进程处于R状态时,必然在 CPU 的调度队列(运行队列)中! 每个计算机硬件都有一个这样的队列。比如键盘,当一个进程运行到了scanf语句,需要等用户从键盘输入数据,此时这个进程的task_struct就会移动到键盘的调度队列中等待读取,读取到数据后再移动到CPU的调度队列中。其他计算机硬件也是类似的道理。
当代大多数计算机,会给每一个进程分配一个时间片。它的时间片执行完,就会自动让出CPU,另一个进程接着执行。CPU内只有一套寄存器,保存着进程的临时数据,它的时间结束后,这些临时数据转移到task_struct中的成员变量“上下文数据”中存放,寄存器中继续保存下一个进程的临时数据。再轮到这个进程执行时,临时数据再转移到寄存器中。
举个例子:
下面这段代码,执行后,会不断循环打印一句hello后睡眠一秒。


查询发现这个进程是S状态,但其实只有程序执行到sleep语句时才会是S状态,如果你恰好能在执行printf语句的那几纳秒时执行了ps查询,就会显示是R状态。可惜几纳秒相比sleep的一秒占比太小了,我反复查询了好多次也没遇到显示R,大家也可以再去试试。
这段代码很简单,等待读取一个数:


当我还没有输入数据时,进程等待中,进程的task_struct在键盘设备的等调度队列中,也是S状态。
换句话说,运行状态与阻塞状态的本质是:看进程的task_struct是在CPU还是其他硬件的调度队列中!
D:深度睡眠状态、不可中断休眠状态,是进程进行磁盘等外设IO操作时的状态,是Linux特有的一种进程状态。
刚才说的S状态,除了可以输入ctrl c中断进程,其实还可以使用kill命令: kill主要用于向进程发送信号,它有非常多的功能选项:

-9选项,可以杀掉一个进程,效果和ctrl c中断进程是一样的,用法是kill -9 进程pid:

而这种D状态,是无法被中断的,只能等待硬件的操作完成才会退出这种状态!
我们发现ps查询进程状态时,状态字母后会带一个
+号,这代表着这个进程是前台进程,没有+则是后台进程。前台进程同一时间只能有一个,后台进程可以有多个。只有前台进程能获取键盘数据,所以输入ctrl c只能中断前台进程,中断后台进程只能用kill -9命令。执行一个命令后加&,则指定这个进程是后台进程。
kill -19 进程pid可以暂停一个进程,kill -18 进程pid可以取消暂停
演示kill发送信号暂停进程

进程暂停后恢复,发现变成了后台进程。此时想中断进程只能用kill -9了。
正常暂停是T状态,还有一种追踪暂停状态t,这其实是gdb背景下追踪进程,调试的时候会显示这种状态:

我们创建一个进程,本质都是要让他完成某种任务的。当一个进程运行结束后,不应该直接挂掉,而是需要OS或父进程检查进程的返回信息,判断任务是否完成,此时这个进程就是僵尸状态。而检查完毕后,资源确定可以释放,进程才能变成死亡状态并被回收。
僵尸状态是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回信息时,就会产生僵尸进程,僵尸状态会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。也就是说,只要子进程退出,父进程还在运行,但父进程没有读取到子进程的状态,子进程就进入僵尸状态。
进程退出时,退出信息是main函数返回值 或 收到的信号值,退出信息会保存在自己的task_struct中,需要OS或父进程检查后才能释放。命令行启动的进程退出时,会由-bash自动检查回收,而我们自己的程序中如果fork了子进程,如果强行中断子进程后,父进程也不回收,子进程就会处在Z状态:

父进程可以使用系统调用wait、waitpid等来回收子进程。
子进程必须回收!如果不回收,子进程会一直处于僵尸状态,导致他的task_struct无法释放,就造成了内存泄漏!进程正常结束了,内存泄漏也不存在了。
如果父进程提前退出了,那么子进程退出变成僵尸进程后该怎么办呢?此时这个子进程就称为孤儿进程。
演示:

结论:

1号进程是系统初始化进程,这样一来,孤儿进程都由系统接管,孤儿进程结束后系统就会自动回收它们,不用担心它们变成僵尸进程了。
进程的task_struct存在于内存中,当内存空间资源不足时,一些进程对应的代码和数据会交换到磁盘上的swap分区,此时进程称为挂起状态。代码和数据换入swap分区,称为swap in,换出则称为swap out。 但是swap分区是在磁盘上的,换入换出涉及到内存和磁盘的IO操作,时间就比内存内的数据流动慢了很多,但将数据换到swap分区目的也是腾出内存空间。swap分区不建议过大,过度的swap会使系统变慢,这一过程本质就是用时间换空间。
如果内存资源还是不足,系统会使阻塞状态S的进程也挂起到swap分区,称之为“阻塞挂起”。 如果内存资源再不足,系统会使当前CPU运行队列中,不是正在运行进程的其他进程也挂起,轮到他运行时再换出,称之为“运行挂起”。 如果内存资源依旧不足,Linux系统就会选择性地杀掉部分进程!
本篇完,感谢阅读!