提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
Linux(实际上是 Unix)的一个基本概念是 Unix/Linux 中的一切都是文件的规则。每个进程都有一个指向文件、套接字、设备和其他操作系统对象的文件描述符表。
与许多 IO 源一起工作的典型系统有一个初始化阶段,然后进入某种待机模式——等待任何客户端发送请求并响应它
我查看
使用场景:一个高性能的网络服务器,可以让多个客户端同时使用 (bs)并且可以获取这些客户端的请求 并处理
怎么处理这些请求的 高并发状况
如果使用多线程 可以做 但是 多线程的使用需要上下文的切换(需要处理一些句柄) 客户端比较多的时候 代价是非常高的
使用单线程的时候使用 多个客户端的请求,会通过DMA 专门处理iO, 保证了数据的不丢失
以 传入五个io来举例子
select/poll/epoll
都服务于相同的想法,创建一组文件描述符,告诉内核你想对每个文件描述符做什么(读,写,…),并使用一个线程阻塞一个函数调用,直到至少一个文件描述符请求的操作可用
系统调用提供了一种实现同步多路复用 I/O 的机制
要使用select,开发人员需要使用描述符和要监视的事件来初始化和填充几个fd_set结构,然后调用select ()。
epoll 是 Linux(并且只有 Linux)中最新、最好、最新的轮询方法。嗯,它实际上是在 2002 年添加到内核中的,所以它并不是那么新。它与poll和select的不同之处在于它将有关当前监视的描述符和相关事件的信息保存在内核中,并导出 API 以添加/删除/修改这些。
struct epoll_event events[5];
int epfd = epoll_create(10); 注意这个
...
...
for (i=0;i<5;i++)
{
static struct epoll_event ev;
memset(&client, 0, sizeof (client));
addrlen = sizeof(client);
ev.data.fd = accept(sockfd,(struct sockaddr*)&client, &addrlen);
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev);
}
while(1){
puts("round again");
nfds = epoll_wait(epfd, events, 5, 10000); 到这了
for(i=0;i<nfds;i++) {
memset(buffer,0,MAXBUF);
read(events[i].data.fd, buffer, MAXBUF);
puts(buffer);
}
}
),而在轮询中则需要一个循环pollfd结构。
Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。
libebent是一个库,它将本文(以及其他一些)中列出的轮询方法包装在一个统一的 API 中。它的主要优点是它允许您编写一次代码并在许多操作系统上编译和运行它而无需更改代码。重要的是要理解 libevent 它只是构建在现有轮询方法之上的一个包装器,因此它继承了那些轮询方法所具有的问题。它不会在 Linux 上使select支持超过 1024 个套接字,也不会允许epoll在没有系统调用/上下文切换的情况下修改轮询事件。因此,了解每种方法的优缺点仍然很重要。
I/O 多路复用模块封装了底层的 select、epoll、avport 以及 kqueue 这些 I/O 多路复用函数,为上层提供了相同的接口。
封装的 select 函数
int fd = /* file descriptor */
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds)
for ( ; ; ) {
select(fd+1, &rfds, NULL, NULL, NULL);
if (FD_ISSET(fd, &rfds)) {
/* file descriptor `fd` becomes readable */
}
}
初始化一个可读的 fd_set 集合,保存需要监控可读性的 FD;
使用 FD_SET 将 fd 加入 rfds;
调用 select 方法监控 rfds 中的 FD 是否可读;
当 select 返回时,检查 FD 的状态并完成对应的操作。
为了最大化执行的效率与性能,所以会根据编译平台的不同选择不同的 I/O 多路复用函数作为子模块,提供给上层统一的接口;在 Redis 中,我们通过宏定义的使用,合理的选择不同的子模块:
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
Redis 会优先选择时间复杂度为 O(1) 的 I/O 多路复用函数作为底层实现,包括 Solaries 10 中的 evport、Linux 中的 epoll 和 macOS/FreeBSD 中的 kqueue,上述的这些函数都使用了内核内部的结构,并且能够服务几十万的文件描述符。
但是如果当前编译环境没有上述函数,就会选择 select 作为备选方案,由于其在使用时会扫描全部监听的描述符,所以其时间复杂度较差 O(n),并且只能同时服务 1024 个文件描述符,所以一般并不会以 select 作为第一方案使用。
作者 free-coder 【并发】IO多路复用select/poll/epoll介绍
作者 不知道名字 LINUX – IO 多路复用 – SELECT VS POLL VS EPOLL
作者 待参考第 6 章 I/O 多路复用:函数select和poll函数
作者:乔治
作者: Redis 和 I/O 多路复用