目录
网络IO过程
在网络通信中,数据最先抵达底层的网卡中,由于安全性考虑,只有内核能直接与底层硬件通信,所以数据会先从网卡复制到内核缓冲区,再复制到用户空间供用户进程使用,
缓冲区是为了避免频繁硬件IO(得中断),可积累一定数据再批量处理
所以网络IO可以分为两个阶段:
- 数据准备阶段: 用户发送的数据经过网络传输到达网络,然后内核将它们复制到缓冲区里
- 数据复制阶段:应用从缓冲区里把数据复制到用户空间
5种网络IO模型
阻塞IO:数据准备、数据复制阶段全部阻塞,如下图所示
非阻塞IO:数据准备阶段不阻塞,而是不断轮询数据是否准备完毕,然后数据复制阶段阻塞,如下图所示
IO多路复用:通过一个系统调用,让内核监视多个网络IO事件(文件描述符),一旦就绪(数据抵达内核缓冲区),就通知进程进行系统调用来读写(内核->用户进程)
- 让一个线程同时负责多个socket fd的IO操作
- 数据准备、数据复制阶段全部阻塞
信号驱动式IO:数据准备阶段通过信号机制实现异步,数据复制阶段阻塞
异步IO:数据准备、数据复制阶段都不阻塞,相当于等内核把事都干完自己再去处理数据
对比
阻塞IO 非阻塞IO IO复用 信号驱动式IO 异步IO 数据准备阶段 阻塞 不阻塞,轮询 阻塞 不阻塞,异步 不阻塞,异步 数据复制阶段 阻塞 阻塞 阻塞 阻塞 不阻塞,异步 优点 不占CPU时间 实时性好,当数据准备完成后立刻开始复制 一个线程负责监控多个网络IO事件 非阻塞 缺点 一个线程对应一个socket连接 占CPU,不断轮询 同步、阻塞 需要底层内核提供支持
Linux下3种IO复用模型
IO复用让一个进程阻塞在select, poll, epoll系统调用上,让内核监视多个文件描述符,一旦就绪(数据抵达内核缓冲区),就通知进程进行读写(内核->用户进程)
select
#define FD_SETSIZE 1024 struct fd_set{ //每一位表示一个文件描述符, //容量数目由FD_SETSIZE确定,所以处理的文件描述符数目受到了限制 long int fds_bits[FD_SETSIZE/sizeof(long int)];//所以可以监测FD_SETSIZE个 }; int select(int nfds,fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timeout); nfds: select监听的最大文件描述符值+1 timeout: select的超时时间,-1表示阻塞 返回值: 就绪的文件描述符的总数
为什么默认监控大小是1024? 因为进程拥有的文件描述符上限是1024(ulimit -n 数目,修改)
poll
struct poll_fd{ int fd; short events; //注册的事件,监听该文件描述符上的哪些事件 short revents; //实际发生的事件 }; int poll(poll_fd *fds, int nfds, int timeout);
select和poll区别:
- select能处理的文件描述符数受限,定长数组,默认1024,可修改;poll基于链表存储,不受限制
- select用数组位置表示fd编号,poll直接存储fd值
epoll
用一组函数完成任务,将用户关心的文件描述符的事件放在内核中的事件表(得额外一个文件描述符来标识),
不像select,poll每次调用都要传入描述符集合、事件集合(复制到内核)struct epoll_event{ uint32 events; data; //union类型,可作为fd }; //内核创建事件表:一个红黑树(存储注册事件),返回其描述符 //还创建一个就绪事件的双向链表 int epoll_create(int size); //操作内核事件表 //向红黑树中插入事件、向内核注册回调函数,中断时向双向链表插入数据(事件发生时加入链表) int epoll_ctl(int op,int fd, epoll_event *event); op操作类型:注册、修改、删除fd上的事件 fd: 要操作的文件描述符 event: 指定事件 //等待事件就绪并返回,查看就绪链表 int epoll_wait(int epfd, epoll_event *events, int maxevents, int timeout); events: 就绪的事件会被复制到该值中 maxevents: 最多监测多少个事件
水平触发、边缘触发
水平触发允许 暂时不对就绪事件处理,下次会继续提醒;
而边缘触发则要求必须处理就绪事件,下次不会提醒,不会重复触发事件,效率高创建一个epoll时
- 在epoll文件系统中创建一个文件
- 创建一个红黑树,存所有注册事件的文件描述符socket
- 创建一个list链表,存放就绪事件
epoll的优势
- epoll将事件表放在内核,减少了描述符列表、事件列表在内核空间、用户空间的频繁复制,且只需要注册一次,不像它俩每次调用都得移动一次
- epoll返回就绪的事件,不需要遍历所有监测描述符
- 没有连接数目限制
同步和异步:调用函数后是否得等待调用结果才返回,指通信方式
阻塞、非阻塞:指等待调用结果时程序的状态,是挂起还是不挂起
学习自
Linux高性能服务器编程
UNIX网络编程 卷1