python fork()多进程
来源:互联网 发布:淘宝网中老年女衬衫 编辑:程序博客网 时间:2024/05/18 09:12
一、理解fork()
fork()是一个绝对唯一的调用。Python中的大多数函数会之返回一次,因为sys.exit()会终止程序,所以它就不会返回。相比之下,Python的os.fork()是唯一返回两次的函数,任何返回两次的函数,在某种意义上,都可以调用os.fork()来实现。在调用fork()之后,就同时存在两个正在运行程序的拷贝。但是第二个拷贝并不是从开始就重新开始的。两个拷贝在对fork()调用后会继续——进程的整个地址空间被拷贝。这时可能会出现错误,而os.fork()可以产生异常。
对fork的调用,返回针对父进程而产生新进程的PID。对于子进程,它返回PID 0.因此,它的逻辑如下:
def handle(): pid = os.fork() if pid: #parent close_child_connections() handle_more_connections() else: #child close_parent_connections() process_this_connections()二、zombie进程
fork()的语义是建立在父进程对找出子进程什么时候,以及如何终止感兴趣的假定上的。例如,一个shell脚本会对找出正在运行的程序中的退出代码感兴趣。父进程不仅可以找出退出代码,还可以找出根据信号,进程是坏掉还是终止。父进程是通过os.wait()或一个类似的调用来得到这些信息的。
在子进程终止和父进程调用wait()之间的这段时间,子进程被成为zombie进程。它停止了运行,但是内存结构还为允许父进程执行wait()保持着。在子进程终止后,必须调用wait()函数,否则系统系统资源会被大量的zombie进程消耗掉,最终会使服务器不可用。
操作系统可以非常容易地完成这个工作。每当子进程终止的时候,它会向父进程发送SIGCHLD信号(信号是一个通知进程某些事件的基本方法)。父进程可以设置一个信号处理程序来接受SIGCHLD和整理已经终止的子进程。
如果父亲进程在子进程之前终止,子进程会一直执行。系统会通过把它们的父进程设置为init(PID 1)来重新制定父进程。init进程就会负责清楚zombie进程。
三、fork()性能
由于fork()函数每次在客户端连接的时候必须在整个服务器中拷贝,所以或许有人会认为它是一个很慢的方法。事实上,fork()的性能对于几乎所有具有高负载的系统来说是可忽略的。
大多数的操作系统,例如linux,是通过copy-on-write内存来实现fork()的。这就意味着,只有内存需要被拷贝(当有进程要修改它)的时候,它才会真正被拷贝。实际上,对fork()的调用通常是瞬间的。
对fork()的调用是应用在整个系统中的。例如,当使用Shell,输入ls,Shell就会调用fork()来产生一个fork的拷贝,新的进程将调用ls。
四、fork()示例
#!/usr/bin/env python#coding:utf-8import os,timeprint 'before the fork,my PID is',os.getpid()if os.fork():print 'Hello from the parent. My PID is',os.getpid()else:print 'Hello from the child. My PID is',os.getpid()time.sleep(1)print 'Hello from both of us.'
两个进程应该同时执行,当程序执行到该点的时候,实际上存在着两个程序的拷贝在执行。所以问候语在代码中只出现一次,而结果中却显示两次。
五、zombie示例
#!/usr/bin/env pythonimport os,timeprint 'Before the fork,my PID is',os.getpid()pid = os.fork()if pid:print 'Hello from the parent.The child will be PID %d' % pidprint 'Sleeping 120 seconds...'time.sleep(120)
子进程会在fork()之后立刻终止,父进程在sleep,能看出子进程出现了zombie,可以从第三列中的Z和输出最后的<defunct>看出来。一旦父进程终止了,将可以确定两个进程都不存在了。
六、使用信号解决zombie问题
#!/usr/bin/env pythonimport os,time,signaldef chldHandler(signum,stackframe):while 1:try:result = os.waitpid(-1,os.WNOHANG)except:breakprint 'Reaped child process %d' % result[0]signal.signal(signal.SIGCHLD,chldHandler)print 'before the fork,my PID is:',os.getpid()pid = os.fork()if pid:print 'Hello from the parent.The child will be PID %d' %pidprint 'Sleeping 10 seconds...'time.sleep(10)print 'Sleep done.'else:print 'Child sleeping 5 seconds...'time.sleep(5)
首先,这个程序定义了信号处理程序chldhandler()。每次收到SIGCHLD的时候,就会调用这个函数。它有一个简单的循环调用os.waitpid(),它的第一个参数-1,意思是等待所有的已经终止的子程序,而第二个参数是说如果没有已经终止的进程存在,就立刻返回。如果有子进程在等待,waitpid()返回一个进程的PID的tuple和退出信息。否则,它产生一个异常。使用wait()或waitpid()来搜集终止进程的信息被称为收割(reaping).
示例中子进程睡眠5秒钟后,父进程就开始收割。time.sleep()有一种特殊情况,如果任意一个信号处理程序被调用,睡眠会被立刻终止,而不是继续等待剩余的时间。
七、总结
大多数服务器都需要同时处理多个客户端。对于服务器的设计者来说,有几种方法可以实现它,其中最简单的就是forking,它主要适用于Linux和UNIX平台。
为了使用fork,需要调用os.fork(),它会返回两次。这个函数把子进程的进程ID返回给父进程,还会把零值返回给子进程。
当某个进程终止的时候,除非该进程的父进程调用了wait()或waitpid(),否则终止信息会一直保持在系统上。因此使用foring的程序必须确保在子进程终止时要调用wait()或waitpid(),方法之一是信号处理程序,还可以使用轮询(polling),定期检查终止的子程序。
使用forking的服务器通常会调用fork()来为每一个到来的连接建立一个新进程。对于进程中不使用的文件描述符,重要的一点是父进程和子进程都应该关闭。
如果文件被修改,锁定是非常重要的。锁定可以避免数据损坏。如果多个进程同时修改一个文件,或者一个进程读取文件的时候,另一个进程正在写文件,都会损坏文件。
如果系统不能执行fork,os.fork()函数可以产生异常。为了防止服务器当机,必须处理这个异常。
- python fork()多进程
- Python 多进程 fork()详解
- Python多进程——fork()
- python 创建进程fork
- Python fork 守护进程<转>
- python fork子进程(一)
- Python 进程,子进程( os.fork() )
- 多进程总结-fork
- 多进程fork
- fork 多进程调试
- perl fork多进程
- fork创建多进程
- C++ fork多进程
- Linux多进程-fork()
- Python进程分支fork和exec详解
- python fork子进程(二)
- python中fork创建新的进程
- Python 使用 os.fork() 创建子进程
- 关于Libsvm的SMO问题
- 使用mysql数据库读写CSV文件
- CF 390C - Inna and Candy Boxes(分类统计)
- 浅析一下搜索引擎的排名机制
- ORACLE 11G在用EXPORT导出时,空表不能导出
- python fork()多进程
- ucos平台上 printf 输出浮点数据出错
- IOS开发——使用shareSDK授权新浪微博、腾讯微博、微信并获取用户资料
- spring 多数据源配置
- 黑马程序员-IOS UITableView NSIndexPath属性讲解
- 王立平--android中的权限
- Ceph添加/删除Mon(ceph.conf)
- C++ 内存池 -- C++ Memory Pool
- 第九周项目五程序填充问题