最近一台机器的systemd内存高达30%多,一直不变,后来排查是僵尸进程,什么是僵尸进程呢,只能google,百度等先了解,然后自己总结了一下,虽然这是基础的东西,但是对于我来说就如新大陆一样。花了一下午可算明白了。模拟的时候主要是先要理解fork这个函数的东西。总结的不对的地方望给位大哥指出
什么是僵尸进程?什么是孤儿进程?
僵尸进程:一个进程使用fork() 创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么这些进程将会成为孤儿进程。孤儿进程将会被init进程(进程号为1)所收养,并由init进程对他们完成状态收集工作
模拟僵尸进程和孤儿进程
1:使用os.fork()之后,子进程是父进程的复制品,在内存中会把父进程的代码及内存分配情况拷贝一份生成子进程的运行空间,这样子进程与父进程的所有代码都一样,两个进程之间的运行时独立的,互不影响。在父进程中获取到的pid是子进程的pid号,在子进程中获取的pid是0.
python脚本:
#!/usr/bin/python import os,sys,time pid = os.fork() if pid == 0: print "this is child pid=",pid else: print "This is parent pid=",pid
在执行上面这段代码的时候,会有两个独立的运行空间独立的代码来执行,在父进程中执行的时候,由于返回的是子进程的pid,所以不会等于0,就走else。
在子进程的独立的运行空间执行上面的那份代码,因为fork给子进程返回的是0,所以就走if那条。
执行结果:
fork()函数被调用一次,返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID
2: getpid(),petppid()
os.getpid() 获取的是当前进程的进程号
os.getppid() 获取当前进程的父进程的进程号
python脚本:
#!/usr/bin/python import os,sys,time pid = os.fork() getpid = os.getpid() getppid = os.getppid() if pid == 0: print "this is child pid=%d,getpid=%d,getppid=%d" %(pid,getpid,getppid) else: print "This is parent pid=%d,getpid=%d,getppid=%d"%(pid,getpid,getppid)
执行结果:
这里为什么子进程的父进程PID是1呢,而不是创建它的父进程ID104809呢,这问题就是上面孤儿进程现象了,看上面的输出结果可以看到,父进程先执行完,后执行的子进程,这就意味着,子进程执行完了的时候,父进程已经不在了,就成了孤儿进程,孤儿进程最终会由init,也就是进程1收养,把它杀掉
3:模拟僵尸进程
python脚本:
#!/usr/bin/python import os,sys,time pid = os.fork() getpid = os.getpid() getppid = os.getppid() if pid == 0: print "this is child pid=%d,getpid=%d,getppid=%d" %(pid,getpid,getppid) else: print "This is parent pid=%d,getpid=%d,getppid=%d"%(pid,getpid,getppid) time.sleep(10)
执行结果:
这里可以看到子进程的父进程pid确实是创建自己的父进程,但是子进程已经成了僵尸进程,通过top命令或者grep def查看
子进程成了僵尸进程是因为子进程结束的时候,父进程还在睡觉,不能调用wait()或waitpid()去获取子进程的终止状态,但是等父进程醒来的时候,就会把僵尸进程给处理掉
4:真实环境下僵尸进程产生了怎么解决
暂时知道的办法是:根据僵尸进程找出僵尸进程的父进程,通过杀父进程的方式删掉僵尸进程,但是线上环境不要轻易删父进程
常用的命令有:
ps -ef |grep def 查看僵尸进程
ps -e -o pid,stat |grep Z 查看僵尸进程的pid
ps -e -o ppid,stat |grep Z 查看僵尸进程的父进程
5:如何避免僵尸进程的产生
这还有待研究
参考博文及书籍
《UNIX环境高级编程中文版》下载地址:https://pan.baidu.com/s/1mios8Wg 密码:zlig