进程的整个生命周期如下:
一个进程被fork出来后,进入就绪态;当被调度到获得CPU执行时,进入执行态;如果时间片用完或被强占时,进入就绪态;资源得不到满足时,进入睡眠态(深度睡眠或浅度睡眠),比如一个网络程序,在等对方发包,此时不能占着CPU,进入睡眠态,当包发过来时,进程被唤醒,进入就绪态;如果被暂停,进入停止态;执行完成后,资源释放,此时父进程wait4还未收到它的信号,进入僵死态。
即整个周期可能会涉及的状态有:就绪态,执行态,僵死态,停止态,睡眠态。
就绪态和执行态
就绪态:进程准备就绪,等待被CPU执行时的状态。即进程已经具备运行条件,但是CPU还没有分配过来,需等待被CPU调度到,进入执行态。
执行态:占用CPU,在CPU上执行。
僵死态
僵死态:进程结束时,其他资源都释放,只留下了task_struct,等待父进程wait4函数处理时的状态。
一个进程如果进入僵死态时,它占用的系统资源都已释放了,只是保留了task_struct等待父进程处理。
如果一个进程一直是僵死态,通过kill是无法杀掉的,除非将它的父进程杀掉,它才会消失。
系统中有僵尸态的进程对系统资源来说没影响,可能是你写的程序有问题,未正常退出,使得父进程无法处理。
实验下:
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid,wait_pid;
int status;
pid = fork();
if (pid==-1) {
perror("Cannot create new process");
exit(1);
} else if (pid==0) {
printf("child process id: %ld\n", (long) getpid());
pause();
_exit(0);
} else {
#if 1 /* define 1 to make child process always a zomie */
printf("ppid:%d\n", getpid());
while(1);
#endif
do {
wait_pid=waitpid(pid, &status, WUNTRACED | WCONTINUED);
if (wait_pid == -1) {
perror("cannot using waitpid function");
exit(1);
}
if (WIFEXITED(status))
printf("child process exites, status=%d\n", WEXITSTATUS(status));
if(WIFSIGNALED(status))
printf("child process is killed by signal %d\n", WTERMSIG(status));
if (WIFSTOPPED(status))
printf("child process is stopped by signal %d\n", WSTOPSIG(status));
if (WIFCONTINUED(status))
printf("child process resume running....\n");
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(0);
}
}
这段代码的关键是waitpid函数,当子进程结束时,会返回一个状态status,父进程可对该状态进行处理。
gcc编译下,运行a.out,会看到系统有两个a.out:
root 27934 94.0 0.0 4212 344 pts/1 R+ 14:50 0:05 ./a.out
root 27935 0.0 0.0 4212 84 pts/1 S+ 14:50 0:00 ./a.out
此时将子进程杀掉,会看到子进程变成了僵尸进程:
[root@localhost process]# kill 27935
[root@localhost process]# ps aux | grep a.out
root 27934 98.7 0.0 4212 344 pts/1 R+ 14:50 0:13 ./a.out
root 27935 0.0 0.0 0 0 pts/1 Z+ 14:50 0:00 [a.out] <defunct>
无论你怎么去杀27935,它始终都存在
[root@localhost process]# kill -9 27935
[root@localhost process]# kill -9 27935
[root@localhost process]# kill -9 27935
[root@localhost process]# kill -9 27935
[root@localhost process]# ps aux | grep a.out
root 27934 98.7 0.0 4212 344 pts/1 R+ 14:50 0:13 ./a.out
root 27935 0.0 0.0 0 0 pts/1 Z+ 14:50 0:00 [a.out] <defunct>
此时将父进程结束,它们就一起消失了
[root@localhost process]# ps aux | grep a.out
root 28335 0.0 0.1 112704 996 pts/0 S+ 14:58 0:00 grep --color=auto a.out
停止态
停止态:人为地暂停进程时的状态。
在linux中,按ctrl+z,当前终端下运行的进程就会进入停止态。
按fg或bg恢复该进程的运行。
fg与bg的区别是:fg是前台运行,bg是后台运行。
有个工具叫cpulimit,它限制进程的原理就是不断地停止进程,恢复进程,最终达到限制进程资源的效果。
编译运行如下代码
main()
{
while(1);
}
通过top可以看到该进程占用了近100%的CPU:
top - 15:05:25 up 4:38, 2 users, load average: 0.22, 0.06, 0.06
Tasks: 118 total, 3 running, 115 sleeping, 0 stopped, 0 zombie
%Cpu(s): 41.9 us, 0.0 sy, 0.0 ni, 58.1 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 499256 total, 52248 free, 68688 used, 378320 buff/cache
KiB Swap: 1572860 total, 1504420 free, 68440 used. 393644 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
28717 root 20 0 4208 348 272 R 100.0 0.1 0:13.89 a.out
此时用cpulimit将它限制到20%
[root@localhost opt]# cpulimit -l 20 -p 28717
Process 28717 found
再通过top命令看时,它已经被限制到20%了:
top - 15:09:56 up 4:42, 3 users, load average: 0.56, 0.50, 0.25
Tasks: 124 total, 1 running, 122 sleeping, 1 stopped, 0 zombie
%Cpu(s): 8.6 us, 0.2 sy, 0.0 ni, 91.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 499256 total, 40720 free, 77464 used, 381072 buff/cache
KiB Swap: 1572860 total, 1504676 free, 68184 used. 384452 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
28717 root 20 0 4208 348 272 T 19.3 0.1 4:00.92 a.out
睡眠态
睡眠态分浅度睡眠和深度睡眠,区别在于:
浅度睡眠:可以被资源和信号唤醒
深度睡眠:只能被资源唤醒,比如你挂载一个NFS,当NFS服务器挂了时,你对这个挂载做不了任何操作,比如用kill命令发送任何信号都无效,处理的方法是:1.等待NFS服务器恢复;2.重启你的服务器。
睡眠态涉及到中断等内容,后面还会继续总结,这里先介绍下概念。
小结
这次总结了进程生命周期的各个状态:就绪态,运行态,停止态,僵尸态,睡眠态,以及它们之间的转换关系。
下次我们来看看进程的管理方式。