Linux的进程在不同的周期内可以拥有不同的状态,各个状态之间也可以进行切换,下面就来简单介绍一下Linux操作系统下的各个进程状态。
- 运行态(TASK_RUNNING):这种状态下进程已经获得CPU使用权,或者已经准备就绪,随时都可以运行。在一些实时操作系统(比如ucos)中,把处于准备就绪的进程称为就绪态,但是Linux把就绪态和运行态都统称为运行态。
- 可中断睡眠态(TASK_INTERRUPTIBLE):当进程等待的资源没有被满足时,进程就会进入睡眠状态,进入中断的睡眠态的进程有两种唤醒方法,一种是等待的资源被满足,另一种是被信号唤醒,被唤醒后的进程就进入了运行态(在ucos等实时操作系统中会进入就绪态)。
- 不可中断睡眠态(TASK_UNINTERRUPTIBLE):不可中断睡眠态和可中断睡眠态一样,都是进程的资源没有被满足而进入睡眠,但是进入不可中断睡眠态的进程不能被信号唤醒,只有当进程资源满足时才会唤醒。被唤醒后的进程就进入了运行态(在ucos等实时操作系统中会进入就绪态)。
- 停止态(TASK_STOPPED):当进程收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU时,进程会进入停止态,向进程发送SIGCONT可让进入进入运行态。进入停止态的进程不会被调度器调度。
- 僵尸态:(TASK_ZOMBIE):当子进程退出时,父进程没有回收子进程的资源,这是子进程就会进入僵尸态。
下面通过几个的程序简单来说明一些进程的一些状态
- 运行态
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
printf("hello getpid = %d getppid = %d\r\n",getpid(),getppid());
while(1);
return 0;
}
我们让上述程序在打印出自己的pid和父进程的pid后就一直进入死循环,让他一直运行,观察其状态。
利用ps -aux命令查看进程号为4727的进程状态
可以看到图中用红笔圈出来的进程号为4727的进程状态为R+,表示该进程正在处于运行状态。
- 睡眠态
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
printf("hello getpid = %d getppid = %d\r\n",getpid(),getppid());
sleep(10);
return 0;
}
在程序中加入sleep函数,让其睡眠10,运行程序观察其状态。
可以看到进程号为4751的进程状态为S+,表示该进程进入睡眠态。
- 僵尸态
僵尸态进程是当子进程退出时,父进程没有回收子进程的资源导致子进程进入僵尸态。下面来看一段代码,模拟子进程进入僵尸态。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
int status;
pid = fork();
if(pid == 0) // 子进程
{
printf("I am child getpid = %d getppid = %d\r\n",getpid(),getppid());
exit(1);
}
else if(pid > 0) // 父进程
{
printf("I am father getpid = %d getppid = %d\r\n",getpid(),getppid());
sleep(20);
waitpid(pid,&status,0);
}
else // fork失败
{
printf("fork error\r\n");
}
return 0;
}
代码开始首先会调用fork函数生产子进程,在子进程中会打印出自己的pid和父进程pid,然后调用exit函数退出。而父进程会睡眠20秒,最后才回收子进程资源。
可以看到当程序运行时,父进程就立刻进入睡眠,此时状态为S+,进入睡眠态。而子进程则立刻退出,此时父进程并没有及时回收子进程资源,导致子进程进入了僵尸态,进程状态此时为Z+。
- 孤儿进程
操作系统还有一个叫孤儿进程的概念,孤儿进程是当父进程比子进程早退出时,导致子进程没有父进程,这时的子进程就被称为孤儿进程。所有孤儿进程的父进程此时变为进程1,也就是init进程成为孤儿进程的父进程。下面来看一段程序。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
int status;
pid = fork();
if(pid == 0) // 子进程
{
while(1)
{
printf("I am child getpid = %d getppid = %d\r\n",getpid(),getppid());
sleep(1);
}
}
else if(pid > 0) // 父进程
{
printf("I am father getpid = %d getppid = %d\r\n",getpid(),getppid());
sleep(5);
exit(1);
}
else // fork失败
{
printf("fork error\r\n");
}
return 0;
}
首先程序会调用fork函数创建子进程,然后子进程会每隔1秒循环打印出自己的pid和父进程pid。父进程在打印出自己的pid后,睡眠3秒退出。此时观察子进程打印出的pid值。
可以看到刚开始运行时子进程的pid为72,72是创建子进程的父进程的进程号,3秒后父进程退出,父进程退出前并没有回收子进程的资源,这时的子进程变成了孤儿进程。此时子进程的父进程进程号变成了进程1,也就是init进程成为了孤儿进程的父进程。