分销商城网站开发价格,海南住房和城乡建设网站,天门市网站建设,商城网站 备案博客内容#xff1a;多路转发的常见方式select#xff0c;poll#xff0c;epoll 文章目录 一、五种IO模型二、多路转发的常见接口1.select2、poll3、epoll 总结 前言 Linux下一切皆文件#xff0c;是文件就会存在IO的情况#xff0c;IO的方式决定了效率的高低。
一、五种… 博客内容多路转发的常见方式selectpollepoll 文章目录 一、五种IO模型二、多路转发的常见接口1.select2、poll3、epoll 总结 前言 Linux下一切皆文件是文件就会存在IO的情况IO的方式决定了效率的高低。
一、五种IO模型
阻塞IO 内核将数据准备好之前系统调用会一直等待。 比较常见的IO方式在我们使用scanf、cin等函数需要从键盘等外设中获取数据如果我们一直不输入数据程序会运行到对应的地方卡住进行阻塞等待。非阻塞IO 非阻塞IO如果内核数据没有准备好系统调用任然会返回。系统中的父进程在对于子进程的回收时参数可以设置非阻塞的。比较形象的说法就是排队时可以听着音乐时不时的看看是否轮到自己。在阻塞的基础上多了一个对于文件描述符的轮询对于cpu来说就是比较大的浪费。特定场合下才会被使用。信号驱动IO 信号的IO方式内核将数据准备好后使用SIGIO信号通知引用程序进行IO操作。常见的就是生产消费模型。多路转发IO 多路转接也叫多路复用。表面看来就是阻塞IO但是比较不同的是阻塞IO是一对一而多路转接是多对一。就是广撒网的方式。异步IO 在内核数据拷贝完成时通知程序。值得注意的是信号IO是告知程序什么时候可以进行数据的拷贝。
可以说IO就是等待读写。IO效率取决于等待的时间。
二、多路转发的常见接口
1.select
函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)参数一就是对于所需要监视的最大文件描述符1。第二、三、四个参数分别是对可读文件描述符集合可写文件描述符集合异常文件描述符的集合。参数的类型是位图位图的位置代表的数文件描述符对应的位置表示对应的文件描述符是否是被监视不需要的直接置null。 为了使用方便可以这组fd_set的接口
void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位参数五就是对于这些文件描述符的一种方式。参数直接取值为null表示是阻塞时读取。为0是非阻塞{n,0} 表示在规定时间内没有事件发生 超时就会返回少于规定时间返回剩余的时间。select是进行IO等待了。 中间的位图参数是一个输入输出型的参数如果select返回后会把以前加入但是无事件发生的fd清空就需要使用一个数组进行数据的保存。 在使用中每一次都需要手动设置fd集合。需要从用户态拷贝到内核态在用户态、内核态都需要进行遍历所有的fd。开销比较大。最重要的是select使用的位图结构会存在文件描述符数量受限的情况。
最重要的一点在使用select函数时用户需要传递一个数组作为参数用于存储可读、可写和错误事件的文件描述符集合。这是因为操作系统内核不知道用户需要监视哪些文件描述符因此用户需要自己维护一个数组来告诉内核要监视哪些文件描述符。而且这个数组需要在每次调用select函数时重新初始化告诉内核新的监视对象。因此select需要用户自己维护数组。
2、poll
poll函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};参数说明
fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返 回的事件集合。nfds表示fds数组的长度. 。timeout表示poll函数的超时时间, 单位是毫秒(ms) 返回值小于0表示失败等于0表示poll等待超时。dyu0表示监听的文件描述符就绪二返回。 poll在使用上比select简单而且解决了select表示文件描述符表数量受限的问题。但是poll在返回后还是需要轮询pollfd来获取就绪的文件描述符。同时在每次的调用时都需把大量的pollfd结构从用户态拷贝到内核态。连接的大量客户端在同一时刻可能只有很少的处于一个就绪状态。随着监视的描述符数量的增长效率也会出现线性的下降。
3、epoll
epoll是为了处理大批量的句柄而改进的poll。 使用epoll的三部曲
调用epoll_create创建一个epoll句柄;调用epoll_ctl 将要监控的文件描述符进行注册;调用epoll_wait 等待文件描述符就绪
int epoll_create(int size);参数自从linux2.6.8之后 size参数是被忽略的.用完之后, 必须调用close()关闭。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数第一个就是epoll模型epoll_create的返回值。参数二是表示的动作通常使用宏来表示 EPOLL_CTL_ADD 注册新的fd到epfd中EPOLL_CTL_MOD 修改已经注册的fd的监听事件EPOLL_CTL_DEL 从epfd中删除一个fd 参数三是需要监听的文件描述符参数四是监听文件描述符的什么事件。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 参数events是分配好的epoll_event结构体数组.epoll将会把发生的事件赋值到events数组中 (events不可以是空指针内核只负责把数据复制到这个events数组中不会去帮助我们在用户态中分配内存).maxevents告之内核这个events有多大这个maxevents的值不能大于创建epoll_create()时的size.参数timeout是超时时间 (毫秒 0会立即返回-1是永久阻塞).如果函数调用成功返回对应I/O上已准备好的文件描述符数目如返回0表示已超时, 返回小于0表示函数失败。 为啥epoll好 通过与select的缺点对应比较。 接口方便虽然拆分了三个函数但是更加高效使用select时需要每次都访问自己维护的数组。输入输出的参数进行了分离。数据拷贝更加的轻量化在对于文件描述符的添加时不是一直都都需要调用EPOLL_CTL_ADD进行拷贝到内核中的不会像select一样操作频繁。事件回调机制避免使用了遍历使用的是回调函数机制就绪的文件描述符结构加入就绪队列中epoll_wait返回直接访问就绪队列的就是知道哪些文件描述符已经就绪。事件复杂度是O(1)的。没有数量上的限制。 以上的优点何以见得epoll的底层原理就是 每一个epoll对象都有一个独立的eventpoll的结构体用来存放epoll_ctl方法向epoll对象中添加进来的事件。事件挂载到红黑树上重复添加就是对于红黑树的节点的增查改操作。添加的事件都会与外界的设备程序建立回调关系事件一旦发生就会触发对应的回调函数。回调的方法在内核中ep_poll_callback它再将发生的事件添加到rdilist双向链表中双向链表作为epoll_wait的消息队列。对于同一个事件节点是被红黑树和消息队列的俩个数据结构共用的。 三者之间的对比
特性selectpollepoll可监视的fd数最大为FD_SETSIZE没有限制没有限制事件触发方式轮询轮询事件通知I/O效率低一般高内存占用高适中低时间复杂度O(n)O(n)O(1)可移植性跨平台跨平台仅在Linux、BSD等少数平台支持文件描述符生命周期控制子进程不继承fd子进程不继承fd子进程自动继承fd 总结
select适用于连接数较少的情况可移植性好但效率较低poll效率高于select但仍然存在遍历fd集合的缺点epoll适用于连接数较多的场景且能够避免无用的遍历效率最高但只能运行在Linux系统上。