进程并非总是处于运行中,至少cpu没运行在它身上时它就是非运行的。进程有几种状态,不同的状态之间可以实现状态切换。下图是非常经典的进程状态描述图,个人感觉右图更加易于理解。 运行态:进程正在运行,也即是cpu正在它身上。 就绪(等待)态:进程可以运行,已经处于等待队列中,也就是说调度类下次可能会选中它 睡眠(阻塞)态:进程睡眠了,不可运行。 各状态之间的转换方式为:(也许可能不太好理解,可以结合稍后的例子) (1)新状态->就绪态:当等待队列允许接纳新进程时,内核便把新进程移入等待队列。 (2)就绪态->运行态:调度类选中等待队列中的某个进程,该进程进入运行态。 (3)运行态->睡眠态:正在运行的进程因需要等待某事件(如IO等待、信号等待等)的出现而无法执行,进入睡眠态。 (4)睡眠态->就绪态:进程所等待的事件发生了,进程就从睡眠态排入等待队列,等待下次被选中执行。 (5)运行态->就绪态:正在执行的进程因时间片用完而被暂停执行;或者在抢占式调度方式中,高优先级进程强制抢占了正在执行的低优先级进程。 (6)运行态->终止态:一个进程已完成或发生某种特殊事件,进程将变为终止状态。对于命令来说,一般都会返回退出状态码。 注意上面的图中,没有"就绪-->睡眠"和"睡眠-->运行"的状态切换。这很容易理解。对于"就绪-->睡眠",等待中的进程本就已经进入了等待队列,表示可运行,而进入睡眠态表示暂时不可运行,这本身就是冲突的;对于"睡眠-->运行"这也是行不通的,因为调度类只会从等待队列中挑出下一次要运行的进程。 再说说运行态-->睡眠态。从运行态到睡眠态一般是等待某事件的出现,例如等待信号通知,等待IO完成。信号通知很容易理解,而对于IO等待,程序要运行起来,cpu就要执行该程序的指令,同时还需要输入数据,可能是变量数据、键盘输入数据或磁盘文件中的数据,后两种数据相对cpu来说,都是极慢极慢的。但不管怎样,如果cpu在需要数据的那一刻却得不到数据,cpu就只能闲置下来,这肯定是不应该的,因为cpu是极其珍贵的资源,所以内核应该让正在运行且需要数据的进程暂时进入睡眠,等它的数据都准备好了再回到等待队列等待被调度类选中。这就是IO等待。 其实上面的图中少了一种进程的特殊状态——僵尸态。僵尸态进程表示的是进程已经转为终止态,它已经完成了它的使命并消逝了,但是内核还没有来得及将它在进程列表中的项删除,也就是说内核没给它料理后事,这就造成了一个进程是死的也是活着的假象,说它死了是因为它不再消耗资源,调度类也不可能选中它并让它运行,说它活着是因为在进程列表中还存在对应的表项,可以被捕捉到。僵尸态进程并不占用多少资源,它仅在进程列表中占用一点点的内存。大多数僵尸进程的出现都是因为进程正常终止(包括kill -9),但父进程没有确认该进程已经终止,所以没有通告给内核,内核也就不知道该进程已经终止了。僵尸进程更具体说明见后文。 另外,睡眠态是一个非常宽泛的概念,分为可中断睡眠和不可中断睡眠。可中断睡眠是允许接收外界信号和内核信号而被唤醒的睡眠,绝大多数睡眠都是可中断睡眠,能ps或top捕捉到的睡眠也几乎总是可中断睡眠;不可中断睡眠只能由内核发起信号来唤醒,外界无法通过信号来唤醒,主要表现在和硬件交互的时候。例如cat一个文件时,从硬盘上加载数据到内存中,在和硬件交互的那一小段时间一定是不可中断的,否则在加载数据的时候突然被人为发送的信号手动唤醒,而被唤醒时和硬件交互的过程又还没完成,所以即使唤醒了也没法将cpu交给它运行,所以cat一个文件的时候不可能只显示一部分内容。而且,不可中断睡眠若能被人为唤醒,更严重的后果是硬件崩溃。由此可知,不可中断睡眠是为了保护某些重要进程,也是为了让cpu不被浪费。 其实只要发现进程存在,且非僵尸态进程,还不占用cpu资源,那么它就是睡眠的。包括后文中出现的暂停态、追踪态,它们也都是睡眠态。