各位大佬,epoll能异步write吗,多个epoll

文章 3年前 (2021) admin
0

Q1:epoll并发量最大能达到多少

根据主题的含义,根据内存计算最大并发连接数。首先,找出单个连接消耗内存的地方。第一个是套接字缓冲区。读和写分别有一个。默认大小复制如下:/proc/sys/net/IP v4/TCP _ rmem(for read)/proc/sys/net/IP v4/TCP _ wmem(for write)。默认大小为87K和16K,最小为4K和4K,最大为2m和2m。在实际使用中,默认值应保持至少8K。8K。然后是逻辑IO缓冲区,也就是说,比如你听recv事件,你需要有内存可用(一般在套接字建立时分配,断开时释放)。这个内存是你写socket程序的时候自己控制的,最低是4K,4K,实际用的是8k。至少,现在设置一个优化方案和使用场景。首先假设4G内存全部空闲(系统和其他进程也需要内存……。如果网络数据包的大小可以控制在4K以下,则假设所有连接的网络都不会拥塞,或者拥塞总量低于4K。一个连接的内存消耗为444=164G/16K=262000,如果网络包的大小可以控制在8K以下,假设所有连接的网络都不会拥塞,或者拥塞总量在8K以下,那么一个套接字的内存占用在24k到32k之间,4g/32K=131000并发按照32K保守计算。这种在生产环境中作为纯网络级别的内存消耗可以作为参考。如果使用默认配置,如果所有连接的网络都严重拥塞,不考虑逻辑发送队列的占用情况,默认配置为2M 2M 8 8 ~=4M4G/4M=1024并发(……如果考虑到发送队列也拥塞,那就自己拿主意。如果只是针对并发进行了优化,没有常驻的逻辑缓冲区,套接字的网络吞吐量小,负载平滑,则将套接字缓冲区大小设置为系统中最低。那么就是4g/8k=52.4万并发,应该是极限值。

Q2:socket write方法 是一个异步的还是同步的

同步通信原理同步通信是一种连续串行传输数据的通信方式,一次通信只传输一帧信息。

Q3:WriteFile怎么实现异步写入

使用fs.writeFile方法的说明:以异步方式将数据写入文件。如果文件已经存在,原始内容将被替换。语法:复制代码如下:fs.writefile (filename,data,[options],[callback (err)])由于此方法属于fs模块,所以在使用前需要引入fs模块(var fs=)。

Q4:用半同步/半异步模式,加多线程(线程池)和epoll调用,实现一个简单的时间服务器?

用半同步半岛不准是加多线程,是来完成这件事情应该是有一定难度的。

Q5:epoll为什么这么快,epoll的实现原理

生活中的例子来解释。

假设你在大学读书,要等朋友来拜访,但这个朋友只知道你在A栋,却不知道你住在哪里,于是约好了在A栋门口见面.

如果你用阻塞IO模型来处理这个问题,那么你只能在A栋门口等,等你的朋友来。在这段时间里,你不能做其他任何事情。不难知道这种方法效率低下。

进一步解释select和epoll模型之间的区别。

阿姨选择做以下事情:比如同学A的一个朋友来了,阿姨选的就比较傻。她带她的朋友去打听谁是同学A,你的朋友在这里。所以在实际代码中,阿姨select做了以下事情:

int n=select(read set;空,空,100);for(int I=0;n 0;i) { if (FD_ISSET(fdarray[i],readset)){ do _某物(FD array[I]);-n;} } epoll阿姨更高级。她记下了同学A的信息,比如他的房间号。同学A的朋友到了,只需要告诉朋友同学A在哪个房间,而不是自己带人去楼里找人。所以Epoll阿姨做的事情可以用下面的代码来表示:

n=epoll_wait(epfd,events,20,500);for(I=0;我

ll自己的内核高速cache区,用于安置每一个我们想监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。这个内核高速cache区,就是建立连续的物理内存页,然后在之上建立slab层,简单的说,就是物理上分配好你想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象。static int __init eventpoll_init(void) { ... ... /* Allocates slab cache used to allocate "struct epitem" items */epi_cache = kmem_cache_create("eventpoll_epi", sizeof(struct epitem), 0, SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL); /* Allocates slab cache used to allocate "struct eppoll_entry" */pwq_cache = kmem_cache_create("eventpoll_pwq", sizeof(struct eppoll_entry), 0, EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL); ... ... epoll的高效就在于,当我们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然可以飞快的返回,并有效的将发生事件的句柄给我们用户。这是由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。
那么,这个准备就绪list链表是怎么维护的呢?当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据到内核中后就来把socket插入到准备就绪链表里了。
如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。
最后看看epoll独有的两种模式LT和ET。无论是LT和ET模式,都适用于以上所说的流程。区别是,LT模式下,只要一个句柄上的事件一次没有处理完,会在以后调用epoll_wait时次次返回这个句柄,而ET模式仅在第一次返回。
这件事怎么做到的呢?当一个socket句柄上有事件时,内核会把该句柄插入上面所说的准备就绪list链表,这时我们调用epoll_wait,会把准备就绪的socket拷贝到用户态内存,然后清空准备就绪list链表,最后,epoll_wait干了件事,就是检查这些socket,如果不是ET模式(就是LT模式的句柄了),并且这些socket上确实有未处理的事件时,又把该句柄放回到刚刚清空的准备就绪链表了。所以,非ET的句柄,只要它上面还有事件,epoll_wait每次都会返回。而ET模式的句柄,除非有新中断到,即使socket上的事件没有处理完,也是不会次次从epoll_wait返回的。三、扩展阅读(epoll与之前其他相关技术的比较): Linux提供了select、poll、epoll接口来实现IO复用,三者的原型如下所示,本文从参数、实现、性能等方面对三者进行对比。 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int poll(struct pollfd *fds, nfds_t nfds, int timeout); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); select、poll、epoll_wait参数及实现对比 1. select的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),位数组的每一位代表其对应的描述符是否需要被检查。 select的第二三四个参数表示需要关注读、写、错误事件的文件描述符位数组,这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件。所以每次调用select前都需要重新初始化fdset。 timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。 select对应于内核中的sys_select调用,sys_select首先将第二三四个参数指向的fd_set拷贝到内核,然后对每个被SET的描述符调用进行poll,并记录在临时结果中(fdset),如果有事件发生,select会将临时结果写到用户空间并返回;当轮询一遍后没有任何事件发生时,如果指定了超时时间,则select会睡眠到超时,睡眠结束后再进行一次轮询,并将临时结果写到用户空间,然后返回。 select返回后,需要逐一检查关注的描述符是否被SET(事件是否发生)。 2. poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。 poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。 poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。 3. epoll通过epoll_create创建一个用于epoll轮询的描述符,通过epoll_ctl添加/修改/删除事件,通过epoll_wait检查事件,epoll_wait的第二个参数用于存放结果。 epoll与select、poll不同,首先,其不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的epoll描述符关联起来。另外epoll不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。

Q6:select的效率一定比epoll低吗

第一,问题导致问题导致差异。当读取两个以上的I/O时,如果使用阻塞I/O,可能会在一个描述符上长时间阻塞,另一个描述符虽然有数据但无法读取,实时性达不到要求。近似解如下:1 .使用多进程或多线程,但这种方法会导致程序的复杂性,并且还会创建和维护进程和线程。(Apache服务器是子进程模式,具有隔离用户的优势。) 2.使用进程,但使用非阻塞I/O读取数据,当一个I/O不可读时立即返回,并检查下一个I/O是否可读。这种形式的循环是轮询,这浪费了CPU时间,因为大部分时间都是不可读的,但重复执行读取系统调用仍然需要时间。3.异步I/O,当一个描述符准备好了,就用一个信号告诉进程,但是由于信号数量有限,在有多个描述符的情况下就不适用了。4.更好的方法是I/O复用(看起来也是翻译复用)。首先,构建一个描述符列表(epoll中的队列),然后调用一个函数,直到其中一个描述符准备好,该函数才会返回。当它返回时,它告诉进程哪个输入/输出准备好了。Select和epoll是多通道I/O机制的解决方案,select在POSIX标准中,epoll是Linux独有的。主要有三个区别(epoll相对于select的优势):1 .select的句柄数量有限,在linux/posix_types.h头文件中有这样一条语句:#define __fd_SETSIZE 1024意味着select最多可以同时监听1024个fds。Epoll没有,但它的限制是打开文件句柄的最大数量。2.epoll最大的优点是不会随着FD的增加而降低效率。selec采用轮询,数据结构类似数组,而EPOLL维护一个队列,直接看队列是否为空就可以了。Epoll将只在“活动”套接字上运行——这是因为在内核实现中,epoll是根据每个fd上的回调函数实现的。然后,只有“活动”套接字会主动调用回调函数(将此句柄添加到队列中),但其他空闲状态句柄不会。此时,epoll实现了一个“伪”AIO。但是,如果大部分I/O都是“活动”的,并且每个I/O端口的利用率都很高,那么epoll的效率不一定比select高(维护队列可能比较复杂)。3.利用mmap加速内核和用户空间之间的消息传输。无论是select、poll还是epoll,内核都需要将FD消息告知用户空间,因此如何避免不必要的内存复制非常重要。在这方面,epoll是由内核在用户空间mmap的相同内存中实现的。第二,界面1)选择1。int select (int maxfdp1,FD _ set *受限读fds,FD _ set *受限写,FD _ set *受限除fds,struct time val *受限TV ptr);struct timeval { long tv _ seclong tv _ usec}有三种情况:tvptr==NULL总是等待;电视ptr-TV _ sec==0电视ptr-TV _ usec==0根本不要等待;当它不等于0时,就是等待时间。select的三个指针都可以为空,因此select提供了比sleep更精确的计时器。请注意,select的第一个参数maxfdp1不是描述符的数量,而是最大描述符加1。首先,它起到了防止错误的限制作用;其次,它可以为内核轮询提供前一个会话,以提高效率。选择返回-1表示错误,0表示超时,正值是所有准备好的描述符的数量(如果相同的描述符准备好进行读写,对结果的影响是2)。
2.int FD _ ISSET(int FD,FD _ set * FD set);描述符集中的fd不是0,否则返回03.int FD _ CLR(int FD,FD _ set * FD _ set);int FD_SET(int fd,FD _ SET * FD SET);int FD _ ZERO(FD _ set * FD set);使用单词“FD _ zero()清除集合。FD _ set()和FD _ clr()分别从集合中添加和移除给定的文件描述符。测试文件描述符是否是集合的一部分;这在select()返回后很有用。这些函数与描述符的0和1无关,只是添加和删除来检查描述符是否在集合中。2)epoll1.int epoll _ create(int size);创建一个epoll句柄,大小用来告诉内核有多少监听器。该参数不同于select()中的第一个参数,它给出了最大监听值fd 1。应该注意的是,创建epoll句柄后,它将占用一个fd值。如果你看看linux下的/proc/process id/fd/就可以看到这个fd。因此,使用epoll后,必须调用close()来关闭它,否则可能会导致fd耗尽。2.int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */};events可以是以下几个宏的集合:EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);EPOLLOUT:表示对应的文件描述符可以写;EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);EPOLLERR:表示对应的文件描述符发生错误;EPOLLHUP:表示对应的文件描述符被挂断;EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里关于epoll工作模式ET,LTLT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了,但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

版权声明:admin 发表于 2021年10月24日 上午9:52。
转载请注明:各位大佬,epoll能异步write吗,多个epoll | 热豆腐网址之家

相关文章