epoll
epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll原理:
- 内存映射(mmap): (共享内存,类似于公有变量 )新建一块不属于应用程序内存及操作系统内存,但两者都能调用的内存,将要操作的进程放进去
- 事件就绪通知: 进程调动时(收到消息时)响应 ,epoll事先于公共内存里注册一个文件描述符,**文件描述符(fd)**就绪时就会激活进程,即收到数据时激活进程
区别于普通单进程单线程:
- 普通单进程单线程HTTP服务器为轮询 select/poll(有/无数量限制的轮询)
- 普通单进程单线程HTTP服务器是将应用程序内存内资源(进程)复制进操作系统内存,处理后删除
epell实现非阻塞长链接HTTP服务器:
# 创建一个epoll对象
epl = select.epoll()
# 将监听套接字对应的fd注册到epoll中
epl.register(tcp_server_socket.fileno(), select.EPOLLIN)
fd_event_dict = dict()
while True:
fd_event_list = epl.poll() # 默认会堵塞,直到 os监测到数据到来 通过事件通知方式 告诉这个程序,此时才会解堵塞
# [(fd, event), (套接字对应的文件描述符, 这个文件描述符到底是什么事件 例如 可以调用recv接收等)]
for fd, event in fd_event_list:
# 等待新客户端的链接
if fd == tcp_server_socket.fileno():
new_socket, client_addr = tcp_server_socket.accept()
epl.register(new_socket.fileno(), select.EPOLLIN)
fd_event_dict[new_socket.fileno()] = new_socket
elif event == select.EPOLLIN:
# 判断已经链接的客户端是否有数据发送过来
recv_data = fd_event_dict[fd].recv(1024).decode("utf-8")
if recv_data:
service_client(fd_event_dict[fd], recv_data)
else:
fd_event_dict[fd].close()
epl.unregister(fd)
del fd_event_dict[fd]