63-Linux如何解决僵死进程

一:回顾

1.每一个进程都有一个地址空间,叫做进程的虚拟地址空间,范围从0x0000 0000 ~ 0xffff fffff,上面(高地址)1G供内核使用,下面3G(低地址)供用户使用,代码段从0x0804 8000开始 0x0000 0000~0x0804 8000是空闲的,最下面是代码段,然后是数据段,然后是堆,然后是栈,堆是从低地址往高地址增长的,栈是从高地址往低地址增长的

63-Linux如何解决僵死进程_第1张图片

2.fork()的时候会把整个虚拟地址空间都复制(即整个进程的实体)

3.逻辑地址(虚拟地址)和物理地址(绝对地址):打印的时候打印的地址或者通过监视器监视的都是逻辑地址,因为无法预知哪些物理地址是空闲的,只能在虚拟地址空间进行布局

4.打印物理地址没有意义(程序结束,再运行,这时的物理地址又不一样了,但是物理地址也是可以打印的,只不过没有意义),只有运行程序的时候才会分配物理地址(被加载到内存)

5.复制进程的时候,先复制的是PCB,然后才是复制进程的实体,如果PCB复制不成功,那么进程的实体就不用复制了。理论上来说,父进程有多大的虚拟地址空间,那么子进程也应该有多大的虚拟地址空间

6.写时拷贝:复制的时候父子进程共享暂时不会修改的页面,如果有父进程或者子进程要对页面进行修改时,才进行复制,写时拷贝会把复制的时间推迟,或者有时候都不需要复制,这样可以提高fork的效率,可以减少内存空间复制的数量

7.代码段是只读的

二:学习

僵死进程:

1.僵死进程概念:子进程先于父进程结束,父进程没有调用 wait() 获取子进程的退出码,此时子进程就变成僵死进程了

2.什么是退出码:退出码就是子进程中有一个 return 0 或者 exit(0) ,这个0就是退出码,退出码存在于PCB(进程控制块也叫进程描述符)中,父进程 wait() 获取子进程PCB中的退出码,然后返回到父进程,当父进程获取了PCB的退出码后,PCB才会被回收

3.子进程结束后,进程的实体先被回收,只有父进程调用 wait() 获取了子进程的退出码以后,PCB才会被回收,这时才算完全回收了(就是这么设计的,linux/unix 都设计成这样)

4.状态有:就绪 执行 堵塞 僵死状态

5.僵死状态时PCB不回收有什么危害:①占用内核空闲②软件层面的资源也会占用

6.僵死状态:子进程已经结束,就等着父进程获取子进程PCB里面的退出码后将PCB回收

7.如果子进程的父进程先结束了,这时子进程就变成孤儿进程了,这时系统就会给他找一个父进程( init 进程,之前 init 进程的 pid 恒为1,但是现在不一定是 init 进程了,可能是其他的一些进程去接管孤儿进程),总之都会给孤儿进程分配父进程,这时 init 进程会执行 wait ,获取子进程的退出码,然后回收子进程,就是这种设计机制

8.执行代码时,一开始还显示僵死进程,但是父进程结束后,僵死进程就消失了,这是因为父进程结束后,又为子进程重新找了个父进程( init 进程接管),这个父进程负责 wait 获取子进程的退出码,然后释放子进程,这时僵死状态就消失了

9.如果父进程先于子进程结束,那么一定不会产生僵死进程,因为子进程还没有结束,父进程先结束了,那么这时子进程的父进程就是init进程,那么子进程结束时一定会被回收

10.getpid()获取自己的id,getppid()获取父进程的id

11.以前的版本一定是1号进程( id 为1的进程,init),但是现在较新的版本可能不是1号(id为1的进程,init),但是他们的作用都是一样的,都是获取子进程的退出码,然后回收子进程

12.为什么子进程结束后没有给出命令提示符呢?但是敲击回车发现并没有阻塞,这是因为父进程结束后,已经给出命令提示符了,只是子进程还没有结束,又继续在命令提示符后面输出了,把命令提示符覆盖了,其实早就已经给出了,并不是没有给出命令提示符

13.如何解决僵死进程:①在父进程中调用 wait() ②让父进程先结束

14. wait 的函数原型:pid_t wait (int *wstatus); wait的返回值就是子进程的退出码,获取哪个子进程,就返回哪个子进程的退出码 wait 需要引用#include 头文件

15.在父进程里面加入 wait() ,父进程就会等到子进程结束后,父进程才会往下执行,这时父子进程就变成串行执行了,其实这样不太好(后面会讲更好的方法)

16.man 2 wait 帮助手册 q退出 1是进程 2是系统调用

17.ps:执行ps可以看到父子进程正在执行

18.ps显示的 就是僵死的意思

19.父进程先结束,为什么子进程一定不会变成僵死进程?因为父进程结束后,子进程会被init进程接管,子进程结束时,init进程会调用wait获取子进程的退出码,然后释放子进程的PCB,所以子进程一定不会变成僵死进程

20.fork()也是系统调用

三:(***面试)代码演示僵死进程及怎么解决僵死进程

1.演示僵死状态(只需让子进程先结束,父进程后结束即可)

#include 
#include 
#include 
#include 
#include 

int main()
{
	int n = 0;
	char *s = NULL;

 	pid_t pid = fork();//复制进程
	assert(pid != -1);
	
	if(pid == 0)
	{
		n = 2;
		s = "child";
	}
	else
	{
		n = 10;
		s = "parent";
	}
	for(int i = 0;i<n;i++)
	{
		printf("s=%s\n",s);
		sleep(1);
	}
	exit(3);//退出进程
}

两次打印完子进程后,理论上子进程应该被回收了,但是通过ps显示子进程结束后,进程并没有被回收,而是出现了一句提示3489 pts/0 00:00:00 main ,的意思就是僵死

63-Linux如何解决僵死进程_第2张图片

退出 3,就是代码中的exit(3)中的退出码3

63-Linux如何解决僵死进程_第3张图片

父进程结束后,僵死进程消失了,这是因为父进程结束后,又为子进程重新找了个父进程(init进程接管),这个父进程负责wait获取子进程的退出码,然后释放子进程,这时僵死状态就消失了

63-Linux如何解决僵死进程_第4张图片

2.父进程结束后,系统会重新为子进程分配父进程(可能这时的父进程的ppid不是1,之前是1),一开始父进程的id是3660,子进程的id是3661,当父进程结束后,子进程的父进程的id变成了1671(之前的ppid是1),虽然id和之前不一样了,但是他们的作用都是一样的,都是获取子进程的退出码,然后回收子进程

63-Linux如何解决僵死进程_第5张图片

为什么子进程结束后没有给出命令提示符呢?但是敲击回车发现并没有阻塞,这是因为父进程结束后,已经给出命令提示符了,只是子进程还没有结束,又继续在命令提示符后面输出了,把命令提示符覆盖了,其实早就已经给出了,并不是没有给出命令提示符

63-Linux如何解决僵死进程_第6张图片

3.怎么解决僵死进程

#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	int n = 0;
	char *s = NULL;

 	pid_t pid = fork();//复制进程
	assert(pid != -1);
	
	if(pid == 0)
	{
		n = 3;
		s = "child";
	}
	else
	{
		n = 10;
		s = "parent";
		int code_exit = 0;
		wait(&code_exit);
		printf("code_exit=%d\n",code_exit);
	}
	for(int i = 0;i<n;i++)
	{
		printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
		sleep(1);
	}
	exit(3);//退出进程
}

父进程在wait那一行代码阻塞住了,父进程先等子进程结束,wait获取子进程的退出码后才会继续运行父进程

63-Linux如何解决僵死进程_第7张图片

最初父子进程都存在,当子进程结束后,父进程获取了子进程的退出码后,父进程才继续执行,这时可以看出子进程已经回收了,并没有出现僵死状态

63-Linux如何解决僵死进程_第8张图片

父子进程都结束后,通过ps显示,父子进程都已经消失了,所以父进程通过wait可以防止子进程产生僵死状态

63-Linux如何解决僵死进程_第9张图片

为什么wait获取子进程退出码的时候code_exit=768,而不是3呢?

768的二进制数为0011 0000 0000是0011左移了8位得到的,为什么要把退出码左移8位呢,因为退出码的值都比较小,一般就在一个字节以内(-128~127),他只需要一个字节,但是32位系统上都是4个字节,他就把退出码左移了8位(移到第二个字节的位置,他有他其他的意义在里面),他用前面1个字节表达其他的意思,所以打印的时候需要把code_exit右移8位即可,或者通过其他函数得到退出码

你可能感兴趣的:(Linux,linux,服务器)