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')
可以通过这段代码简单测验一下fork调用的特性

相关文章:

  • 2021-09-06
  • 2018-04-10
  • 2021-07-02
  • 2022-12-23
  • 2022-12-23
  • 2021-09-30
猜你喜欢
  • 2021-05-22
  • 2021-10-21
  • 2022-02-03
  • 2021-07-27
  • 2021-11-08
相关资源
相似解决方案