前面的网络编程的例子使用多进程也是可以实现的:
import socket
import os
def main():
listen_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_IP)
listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_sock.bind(('0.0.0.0', 9090))
listen_sock.listen(0)
while True:
conn_sock, client_addr = listen_sock.accept()
pid = os.fork()
if pid == 0: # I am child process
serve(conn_sock, client_addr)
else: # I am parent process
conn_sock.close()
def serve(conn_sock, client_addr):
print('connected from %s:%s' % client_addr)
input = conn_sock.recv(8192)
while 'done' != input.strip():
conn_sock.sendall(input)
input = conn_sock.recv(8192)
conn_sock.sendall('bye!\n')
conn_sock.close()
main()
其中os.fork()之后会创建一个子进程。子进程默认会继承所有父进程的所有file descriptor(也就包括打开的socket),已经拥有fork前父进程的所有内存状态。所以子进程可以拿着父进程打开的conn_sock继续与客户端通信。而父进程在把conn_sock交给子进程之后,就不再需要开着这个socket的fd了,所以就需要关闭掉。否则,子进程调用conn_sock.close()之后,客户端的连接其实还没有断开,因为父进程还拿着fd不放呢。
从调度的角度来看,前面说的两个要素:
- 状态的保存:仍然是保存在栈上。每个进程都有一个主线程。fork出来的子进程的主线程的栈上保存了对应客户端的conn_sock。
- 不同socket之间的调度:仍然是利用内核的scheduler,只是这次调度的是process。对于内核来说调度逻辑是差不多的,区别就是process还要额外更新一下虚拟内存的映射表,使得多个process彼此之间不可见对方的内存。
从效率上来说,具有多线程一样的问题,而且内存占用会更高,切换成本也更高。多线程和多进程的版本从代码可读性上来说还是非常不错的,很好懂,从上至下平铺直叙的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。