进程组成:
可执行的程序(二进制或sh等可执行脚本)
数据
上下文环境
fork操作执行时:
给新进程分配新的内存空间、进程ID
复制父进程的数据、上下文环境到这块新的内存空间
程序则使用共享内存
这时子进程保存了父进程的文件描述符的所有副本,可以进程跟父进程一样的操作了。
对于监听一个socket来说,多个进程同时在accept处阻塞,当有一个连接进入,多个进程同时被唤醒,但之间只有一个进程能成功accept,而不会同时有多个进程能拿到该连接对象,操作系统保证了进程操作这个连接的安全性。
扩展:上述过程,多个进程同时被唤醒,去抢占accept到的资源,这个现象叫“惊群”,而根据网上资料,Linux 内核2.6以下,accept响应时只有一个进程accept成功,其他都失败,重新阻塞,也就是说所有监听进程同时被内核调度唤醒,这当然会耗费一定的系统资源。
而2.6以上,则已经不存在惊群现象了,但是由于开发者开发程序时使用了如epoll等异步通知技术,仍然会造成惊群,如有需要更高性能要求,或许参考nginx的实现方案。
# -*- coding: utf-8-*-
import socket
import os
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("", 6000))
s.listen(10)
gv = "x"
print "global value gv:", gv
print "parent pid:", os.getpid()
for i in xrange(3):
pid = os.fork() # fork后, 父进程以及子进程的上下文环境都是执行到当前位置, 然后继续往下执行
if pid == 0: # pid=0, 子进程获取到的状态
conn, address = s.accept() # 在有连接进入之前, 所有进程都阻塞在这里
conn.recv(1)
conn.send('ok')
print "child pid:", os.getpid()
elif pid == 1:
print "parent pid:", os.getpid()
elif pid == -1: # pid=-1为系统异常状态
print "error"
os.waitpid(pid, 0) # 主进程等待
print "parent pid:", os.getpid(), "end"