1 梳理:
应用层的下一层是传输层,而http协议一般是使用tcp的,所以实现tcp的重要性就不言而喻。
由于tornado中实现了ioloop这个反应器以及iostream这个对连接的异步读写,所以tcp就很容易实现异步。
在tornado的tcpserver文件中,实现了TCPServer这个类,他是一个单线程的,非阻塞的tcp 服务。
为了与上层协议(在tornado中就是HTTPServer)交互,TCPServer提供了一个接口:handle_stream, 要求其子类必需实现该方法,该方法就是主要用来处理应用层逻辑的。
TCPserver大体上实现了两种启动方式:单进程模式以及多进程模式(多进程模式需要Linux环境)。 因为多进程方式是单进程的复杂版本,所以讲了多进程那么单进程就很好理解了。
下面就开始吧
2 准备知识点
因为多进程模式需要Linux环境,所以需要对Linux有个基本的了解
在Linux中,创建一个子进程只需要调用fork()系统调用就可以了,fork调用会返回两次,子进程返回0,父进程返回子进程的pid。然后子进程和父进程继续执行fork调用之后的语句,子进程获得父进程数据空间,堆,栈的完全副本(也就是内存空间是独立的)。因为fork调用之后经常会执行exec,所以Linux一般采用写时复制(copy on write),父进程和子进程共享统一数据空间,只有当某个内存区域被修改时,才将该区域复制为副本。
另外,尽管父进程打开的文件描述符都“复制”到了子进程,但由于父子进程的文件描述符指向同一个文件表项,所以不管是父进程或者是子进程对文件描述符进行修改,都会反映到子进程或者父进程中。所以可以这么说:父子进程共享文件描述符。
import os import socket import fcntl def set_close_exec(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFD) fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) a = '你好' sk = socket.socket() set_close_exec(sk.fileno()) sk.bind(('127.0.0.1', 8888)) sk.listen(1) def start_child(): id = os.fork() if id == 0: print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) print(a) print('----------', sk.fileno()) return else: print('I (%s) just created a child process (%s).' % (os.getpid(), id)) print('haha') start_child() print('done')