1.linux 程序员手册
(1)安装man-pages(linux 程序员手册)
#yum install -y man-pages
#man 2 select //1用户命令,2系统调用 3例程,库函数 4设备,/dev目录文件 5文件格式描述 6游戏 7杂项 8系统管理员工具,root权限 9其他 n新文档,o老文档,l本地文档
(2)查看linux内核版本
#uname -a
2.select
使用select是先对操作的fd进行查询,是否可读,可写,异常等,查到结果之后才发起真正的IO操作
(1)手册:
学习最好的文档应该是官方文档,可以自己查阅
#man 2 select
(2)函数:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); nfds:nfds是三个集合中编号最高的文件描述符数加1。 readfds:返回可读的fd集合 writefds:返回可读的fd集合 exceptfds:返回异常的fd集合 timeout:超时时间:0立刻返回(轮询很有用),null一直阻塞(中断信号可以结束),>0超时返回
主要工作:
A.内核收到数据包到达信号,会调用select()
B.select()会把用户空间的fds拷贝到内核空间,然后遍历fds,找到有数据的fd标记好
C.将内核空间fds的结果,拷贝回给用户空间(可读,可写,异常集合)
D.内核再发中断信号告诉用户进程,可以来read() fd的数据了
(3)优缺点:
优:多个fd集中拷贝,一次select()只需拷贝一次,减少用户态和内核态之间切换
缺:
A. 监听fd数量最大1024
B. 每次都需要遍历全量fd才知道那个有数据到达
3.poll
poll和select(2)类似,都是等待一组文件描述符中的一个准备就绪
(1)手册
#man 2 poll
(2)函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout); fds: pollfd的结构体数组,存放fd的 struct pollfd { //存放fds参数的结构体 int fd; /* file descriptor */ //文件描述符 short events; /* requested events */ //设置我们希望发生的事件(可读,可写,异常) short revents; /* returned events */ // 实际发生的事件(由内核填充:可读,可写,异常) }; nfds:调用方指定fds数组中的项数 timeout:超时时间
poll和select(2)类似,都是等待一组文件描述符中的一个准备就绪
区别:
poll遍历长度不限制
select遍历长度限制1024(和linux设置宏定义有关)
(3)优缺点
优:遍历长度不限制了
缺:A.需要遍历才知道fd是否可读可写
4.epoll
(1)手册
#man 2 epoll_create/epoll_ctl/epoll_wait
(2)函数
int epoll_create(int size); size:初始监听fds大小,可动态扩展 //1.创建一个epfd,申请空间 //2.初始化一颗红黑树,用于存放所以监听的fds //3.初始化一个就绪list链表,后面监听到有数据到达的就添加到这里 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); epfd:epoll_create创建的epfd,用于epoll事件操作 op:epfd绑定fd动作,新增,修改,删除 fd: socket连接分配的fd,要监听的fd event:epoll_event 要监听事件(EPOLLIN|EPOLLET可读,可写等) //作用:注册一个监听事件(将epfd,fd,事件绑一起) //1.将用户空间的fd拷贝到内核,根据op动作:操作插入,删除,或修改,结果保存在红黑树上 //2.当中断信号来时,符合监听事件,内核向准备就绪链表中插入数据 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); //1.等待事件发生,监听就绪链表 //2.有数据到达时,通知用户进程拷贝数据 struct epoll_event { __uint32_t events; epoll_data_t data; };
(3)select,poll,epoll 比较
多路复用器 | select | poll | epoll |
fd存储数据结构 | 数组 | 链表 | 红黑树+就绪链表(双向链表) |
遍历时间复杂度 | O(n) | O(n) | O(1) |
工作模式 | 轮询 | 轮询 | 信号驱动 |
前面有说过
Windows的IOCP实现了真正的异步IO
Linux系统下,异步IO模型在2.6版本才引入,目前并不完善,其底层实现仍使用epoll
大多数的高并发服务器端的程序,一般都是基于Linux系统的,因此大多还采用IO多路复用模型