fork()和僵尸进程
2018-01-03@望京
关于fork()函数,Unix/Linux提供的fork()系统调用,fork()一次返回两次,
操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回;
子进程永远返回 0,而父进程返回子进程的ID。
父进程结束时,子进程并不会随父进程立刻结束;同样,父进程不会等待子进程执行完。
- Python里fork()的使用
示例一
#!/usr/bin/python3 import os print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac
pid = os.fork()
if pid == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid)) ####### 结果 #########
Process (25912) start...
I (25912) just created a child process (25913).
I am child process (25913) and my parent is 25912.
示例二
#!/usr/bin/python3 import os
import time #创建子进程之前声明的变量
source = 10
try:
pid = os.fork()
if pid == 0: #子进程
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
#在子进程中source自减1
source -= 1
time.sleep(1)
else: #父进程
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
print(source)
time.sleep(2)
except OSError as e:
print("exception ",e) ####### 结果 ########
I (12807) just created a child process (12808).
10
I am child process (12808) and my parent is 12807.
9
- 服务器里的僵尸进程是怎么产生的?
参考:https://www.cnblogs.com/yuxingfirst/p/3165407.html
参考:http://blog.csdn.net/qq_20218109/article/details/52078076
参考:http://blog.51cto.com/forlinux/1422438
子进程结束后,但是父进程还没有结束的时候,子进程是出于Zombie状态的,这个需要父进程去收集子进程的信息释放子进程。
如果父进程结束了子进程没有结束,那么子进程就会寄托给pid为1的进程来管理。
给进程设置僵尸状态的目的是维护子进程的信息,以便父进程在以后某个时间获取,
这些信息包括子进程的进程ID、终止状态以及资源利用信息(CPU时间,内存使用量等等);
僵尸进程的产生示例
子进程先于父进程退出,同时父进程又没有调用wait/waitpid,则该子进程将成为僵尸进程。
#!/usr/bin/python3 import os
import time try:
pid = os.fork()
if pid == 0: #子进程
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else: #父进程
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
time.sleep(50)
print('%s end' % os.getpid())
except OSError as e:
print("exception ",e) ###### 结果 ######
I (19668) just created a child process (19669).
I am child process (19669) and my parent is 19668.
19669 end
19668 end # 这里sleep了50秒才打印
程序运行时,结合ps命令查看僵尸进程情况:
root@10.13.17.16[13:30:40]$ ps -A -ostat,ppid,pid,cmd | grep -e '^[Zz]'
Z+ 19668 19669 [forkkk.py] <defunct>
root@10.13.17.16[13:31:28]$
root@10.13.17.16[13:31:28]$ ps -A -ostat,ppid,pid,cmd | grep forkkk
S+ 10582 19668 /usr/bin/python3 ./forkkk.py
Z+ 19668 19669 [forkkk.py] <defunct> # 显然pid是19669,即子进程的id,子进程已经执行结束了,但是此时父进程19668还没有执行完。
S+ 17673 19678 grep forkkk
root@10.13.17.16[13:31:30]$
- 如何避免出现僵尸进程呢/如何回收僵尸进程?
1. 信号方式
当子进程退出的时候,内核都会给父进程一个SIGCHLD信号,
所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait(或waitpid),就可以清理退出的子进程以达到防止僵尸进程的目的。
#!/usr/bin/python3 import os
import time
import signal def childhandler(*args, **kwargs):
while True:
try:
result = os.waitpid(-1, os.WNOHANG)
except:
break
else:
print("Reaped child process %(pid)d, status is %(status)s" % \
{'pid':result[0], 'status':result[1]}) if __name__ == '__main__':
signal.signal(signal.SIGCHLD, childhandler)
print("Before the fork, parent PID is %s" % os.getpid())
pid = os.fork()
if pid:
print("Hello from the parent, the child PID is %s" % pid)
print("Parent sleeping 10 seconds ...")
time.sleep(10)
print("Parent sleep done.")
else:
print("Child sleeping 3 seconds ...")
time.sleep(3)
print("Child sleep done.") ###### 结果 ######
Before the fork, parent PID is 18998
Hello from the parent, the child PID is 18999
Parent sleeping 10 seconds ...
Child sleeping 3 seconds ...
Child sleep done.
Reaped child process 18999, status is 0
Parent sleep done.
os.waitpid() -1 表示等待主进程的所有子进程终止
os.WNOHANG 表示如果没有已经终止的子进程就立即返回
2. 父进程轮询检查子进程并回收
#!/usr/bin/python3 import os
import time def recovery():
while True:
try:
result = os.waitpid(-1, os.WNOHANG)
#result = os.wait()
except:
break
else:
return result if __name__ == '__main__':
print("Before the fork, parent PID is %s" % os.getpid())
pid = os.fork()
if pid:
print("Hello from the parent, the child PID is %s" % pid)
while True:
res = recovery()
time.sleep(0.5)
print('one loop...')
if res[0]:
print("Reaped child process %(pid)d, status is %(status)s" % \
{'pid':res[0], 'status':res[1]})
break
print("Parent run end...")
else:
print("Child sleeping 3 seconds ...")
time.sleep(3)
print("Child sleep done.") ###### 结果 ######
Before the fork, parent PID is 3391
Hello from the parent, the child PID is 3392
Child sleeping 3 seconds ...
one loop...
one loop...
one loop...
one loop...
one loop...
one loop...
Child sleep done.
one loop...
one loop...
Reaped child process 3392, status is 0
Parent run end...
轮询的方式没有办法很及时的处理僵尸进程(取决于轮询的时间间隔)。
注意:
- result = os.waitpid(-1, os.WNOHANG) 这种方式不会阻塞调用者。
- result = os.wait() 这种会阻塞调用者,导致不会进行轮询。
3. fork()两次/Python实现守护进程
父进程一次fork()后产生一个子进程,随后父进程立即执行waitpid(子进程pid, NULL, 0)来等待子进程结束,然后子进程fork()后产生孙子进程随后立即exit(0)。
这样子进程顺利终止(父进程仅仅给子进程收尸,并不需要子进程的返回值),然后父进程继续执行。
这时的孙子进程由于失去了它的父进程(即是父进程的子进程),将被转交给Init进程托管。
于是父进程与孙子进程无继承关系了,它们的父进程均为Init,Init进程在其孙子进程结束时会自动收尸,这样也就不会产生僵尸进程了。
详情见:http://blog.tangyingkang.com/post/2016/10/20/python-daemon/ *****
fork()两次不是必须的:https://segmentfault.com/a/1190000008556669 *****
import os
import sys
import atexit def daemonize(pid_file=None):
"""
创建守护进程
:param pid_file: 保存进程id的文件
:return:
"""
# 从父进程fork一个子进程出来
pid = os.fork()
# 子进程的pid一定为0,父进程大于0
if pid:
# 退出父进程,sys.exit()方法比os._exit()方法会多执行一些刷新缓冲工作
sys.exit(0) # 子进程默认继承父进程的工作目录,最好是变更到根目录,否则回影响文件系统的卸载
os.chdir('/')
# 子进程默认继承父进程的umask(文件权限掩码),重设为0(完全控制),以免影响程序读写文件
os.umask(0)
# 让子进程成为新的会话组长和进程组长
os.setsid() # 注意了,这里是第2次fork,也就是子进程的子进程,我们把它叫为孙子进程
_pid = os.fork()
if _pid:
# 退出子进程
sys.exit(0) # 此时,孙子进程已经是守护进程了,接下来重定向标准输入、输出、错误的描述符(是重定向而不是关闭, 这样可以避免程序在 print 的时候出错) # 刷新缓冲区先,小心使得万年船
sys.stdout.flush()
sys.stderr.flush() # dup2函数原子化地关闭和复制文件描述符,重定向到/dev/nul,即丢弃所有输入输出
with open('/dev/null') as read_null, open('/dev/null', 'w') as write_null:
os.dup2(read_null.fileno(), sys.stdin.fileno())
os.dup2(write_null.fileno(), sys.stdout.fileno())
os.dup2(write_null.fileno(), sys.stderr.fileno()) # 写入pid文件
if pid_file:
with open(pid_file, 'w+') as f:
f.write(str(os.getpid()))
# 注册退出函数,进程异常退出时移除pid文件
atexit.register(os.remove, pid_file)
- 扩展:Python中fork的代价
在Unix系统上面启动守护进程
fork()和僵尸进程的更多相关文章
- fork和僵尸进程
1. 关于fork fork()函数: 用于创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息:在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行 ...
- 为什么fork()2次会避免产生僵尸进程
什么是僵尸进程:用fork()创建子进程后,子进程已终止但父进程没有对它进行善后处理,那么子进程的进程描述符就一直保存在内存中,子进程就是僵尸进程. 怎么产生僵尸进程: 1.父进程没有SIGCHLD信 ...
- 2次使用fork避免产生僵尸进程和不去处理SIGCHLD信号
1.如下代码所示 #include <unistd.h> #include <sys/types.h> #include <unistd.h> int main(i ...
- 【转】Linux杀死fork产生的子进程的僵尸进程defunct
僵尸进程 就是 已经结束,但是还没有清理出去的.用kill -9 $PID 也无法杀死. 所以程序中应该避免出现僵尸进程. 用fork之后,父进程如果没有wait /waitpid 等待子进程的话,子 ...
- 为何要fork()两次来避免产生僵尸进程??
最近安装书上说的,开始搞多进程了..看到了一个好帖子,学习学习 http://blog.sina.com.cn/s/blog_9f1496990100y420.html 首先我们要明白,为什么要避免僵 ...
- 为何要fork()两次来避免产生僵尸进程?
为何要fork()两次来避免产生僵尸进程? 当我们只fork()一次后,存在父进程和子进程.这时有两种方法来避免产生僵尸进程: 父进程调用waitpid()等函数来接收子进程退出状态. 父进程先结 ...
- 1.1 Linux中的进程 --fork、孤儿进程、僵尸进程、文件共享分析
操作系统经典的三态如下: 1.就绪态 2.等待(阻塞) 3.运行态 其转换状态如下图所示: 操作系统内核中会维护多个队列,将不同状态的进程加入到不同的队列中,其中撤销是进程运行结束后,由内核收回. 以 ...
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid
本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID 1 进程标识符(Process Identifie ...
随机推荐
- 【hdu 4658】Integer Partition (无序分拆数、五边形数定理)
hdu 4658 Integer Partition 题意 n分拆成若干个正整数的和,每个正整数出现小于k次,分拆方案有多少.(t<=100,n<=1e5) 题解 之前写过一篇Partit ...
- 【HDU-6148】 Valley Numer(数位dp)
百度之星2017复赛1005 HDU-6148 Valley Numer 题意 不出现上升后直接下降数位的数,不超过n的有几个.前导零不算. 题解 dfs(当前数位的位置len,这位的数num,是否在 ...
- 【Gym - 100812G 】Short Path (SPFA)
BUPT2017 wintertraining(15) #7B 题意 n个点m条无向有权边(2 ≤ n ≤ 10^5, 1 ≤ m ≤ 10^5),每个点标记了0或1,求所有1中,最近的两个1的下标及 ...
- Nagios故障 CHECK_NRPE: Socket timeout after 10 seconds.
Nagios 的警报信息如下,意思是 nrpe 进程执行某些脚本超过了 10 秒钟,就会发警报 CHECK_NRPE: Socket timeout after 10 seconds 修改 comma ...
- [luogu4149][bzoj2599][IOI2011]Race【点分治】
题目描述 给一棵树,每条边有权.求一条简单路径,权值和等于 K,且边的数量最小. 题解 比较明显需要用到点分治,我们定义\(d\)数组表示当前节点到根节点\(rt\)之间有多少个节点,也可以表示有多少 ...
- Nifi 老是死机
1. nifi服务器配置 CPU:4核 内存:7G 2. 改动如下: nifi.provenance.repository.rollover.time=30 secs --> 36000 s ...
- 【bfs】拯救少林神棍(poj1011)
Description 乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位.然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度.请你 ...
- 【linux】/dev/null作用和/dev/random
一. /dev/null /dev/null属于字符特殊文件,它属于空设备,是一个特殊的设备文件,它会丢弃一切写入其中的数据,写入它的内容都会永远丢失,而且没有任何可以读取的内容. 我们用file命 ...
- 微信支付退款(PHP后端)
应用场景 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上. 微信 ...
- CodeFroces--Joseph’s Problem
题目意思:给出n k 求 k%1 + k%2 +k%3+...+k%n 的和 利用分块的思想 我们知道 k%i ==k-k/i*i 同时 一段连续的区间的 k/i 是相等的 #include< ...