# linux-进程-02-进程的生命周期

进程的整个生命周期如下:

lifecycle.png

一个进程被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.重启你的服务器。

睡眠态涉及到中断等内容,后面还会继续总结,这里先介绍下概念。

# 小结

这次总结了进程生命周期的各个状态:就绪态,运行态,停止态,僵尸态,睡眠态,以及它们之间的转换关系。

下次我们来看看进程的管理方式。