epoll IO多路复用技术

合集下载

epoll 用法

epoll 用法

epoll 用法
epoll是一种事件通知机制,用于在 Linux 上实现高效的 I/O 多路复用。

它可以同时监视多个文件描述符,当其中任意一个描述符就绪时,就会通知应用程序进行相应的处理。

epoll 的使用方法主要分为以下几步:
1. 创建 epoll 实例:调用 epoll_create 函数创建一个 epoll 实例,它返回一个文件描述符,用于后续的操作。

2. 添加事件监听:调用 epoll_ctl 函数将需要监听的文件描述符添加到 epoll 实例中,同时指定需要监听的事件类型,如可读、可写等。

3. 等待事件发生:调用 epoll_wait 函数等待事件的发生。

它会阻塞应用程序,直到有文件描述符就绪或者超时。

4. 处理事件:当 epoll_wait 返回时,应用程序需要根据返回的就绪事件列表进行相应的处理,如读取数据、写数据等。

需要注意的是,epoll 使用时需要结合非阻塞 I/O 来使用,以充分发挥其高效的特性。

epoll 的使用相对于传统的 select 和 poll 等方式更加高效,可以大大提高应用程序的性能和并发能力。

因此,在设计高并发网络应用程序时,epoll 是一个非常重要的工具。

- 1 -。

epoll 原理

epoll 原理

epoll 原理
epoll是Linux系统提供的一种高效I/O多路复用方式。

它采用事件驱动的方式实现I/O多路复用,可以同时监控多个文件描述符的状态,并在文件描述符就绪时通知应用程序进行读写操作。

与传统的select 和 poll 系统调用相比,epoll 具有更高的性能和更好的可扩展性。

epoll 基于内核中的事件驱动机制,通过注册回调函数实现对事件的监听和处理。

应用程序可以将一个或多个文件描述符注册到epoll 对象中,当所关注的文件描述符就绪时,内核会通知 epoll 对象,epoll 对象再调用应用程序注册的回调函数进行处理。

epoll 主要包含三个系统调用:epoll_create、epoll_ctl 和epoll_wait。

其中,epoll_create 用于创建 epoll 对象,epoll_ctl 用于向 epoll 对象中添加、修改或删除文件描述符,epoll_wait 则是阻塞等待 epoll 对象中的文件描述符就绪。

与 select 和 poll 不同的是,epoll 不需要在每次调用
epoll_wait 时重新向内核传递文件描述符集合,而是在注册文件描述符时将其添加到内核中的事件表中,这样每次调用 epoll_wait 时只需要从事件表中取出就绪的文件描述符即可,大大减少了内核与用户空间的数据交换次数,提高了系统的效率。

总之,epoll 是 Linux 平台上一种高效的 I/O 多路复用机制,采用事件驱动的方式实现对文件描述符的监控和处理。

它相对于传统的 select 和 poll 有更好的性能和可扩展性,是实现高并发网络编
程的重要工具之一。

非阻塞io多路复用机制

非阻塞io多路复用机制

非阻塞io多路复用机制非阻塞IO多路复用机制一、介绍在计算机网络中,I/O即输入输出操作,是计算机与外部设备进行交互的方式之一。

传统的I/O操作常常是阻塞式的,即当一个I/O操作进行时,CPU不能同时执行其它操作,需等待I/O操作完成后才能继续执行。

这种阻塞式的I/O操作,对于服务器等高性能应用是十分低效和浪费资源的。

为了提高I/O操作的效率,非阻塞式的I/O操作越来越被广泛应用于计算机网络。

非阻塞式的I/O操作,可以在读取或写入操作时立即返回,避免资源浪费。

二、多路复用机制多路复用是一种I/O操作的机制,可以同时监测多个I/O操作的状态,从而提高I/O操作的效率。

多路复用分为两种方式:select和epoll。

1. select多路复用select是一种多路复用机制,主要实现步骤是:将要监听的一组I/O操作,通过select函数注册在一个fd_set变量集合中,然后调用select函数等待I/O操作发生,当I/O操作发生时,再通过遍历fd_set集合,判断哪些I/O操作发生了变化,从而进行相应的操作。

select的缺点是效率低下和可扩展性差,因为每次调用select函数,都要遍历I/O操作的集合,当I/O操作很大时,效率会很低,甚至会出现错误。

2. epoll多路复用epoll是linux内核中提供的多路复用机制,实现方式与select相似,但是其效率更高,可扩展性更好。

epoll的实现原理是:将要监听的I/O 事件添加到内核中的事件表中,然后通过epoll_wait函数等待I/O事件发生。

当I/O事件发生后,内核会在自己的事件表中查找相应的I/O事件,从而可以快速地找到I/O事件的注册信息。

三、非阻塞IO与多路复用机制的结合非阻塞IO和多路复用机制的结合,可以实现高效的I/O操作。

首先将要操作的I/O设置为非阻塞式的,然后再通过多路复用机制对多个I/O 事件进行监听,当I/O事件发生时,操作系统会负责将I/O操作的结果返还给应用程序。

select poll epoll原理

select poll epoll原理

select poll epoll原理一、概述select、poll、epoll 都是常见的 I/O 多路复用机制,实现高效的 I/O 操作,提高程序的性能。

这三种机制都是通过在一个线程中处理多个文件描述符的 I/O 事件实现高并发。

本文将介绍 select、poll、epoll 的原理。

二、selectselect 是最早的 I/O 多路复用机制之一,提出于1983 年。

select 的基本原理是将一些文件描述符添加到一个 fd_set 集合中,通过 select 函数等待这些 fd_set 集合中的文件描述符中有一个或多个就绪,然后在这些文件描述符上执行 I/O 操作。

select 函数可以设置一个超时时间,当超时时间到达后若 fd_set 集合中没有就绪的文件描述符,则返回 0。

select 的缺点是效率低下。

在处理大量文件描述符时会采用轮询的方式,每次遍历所有文件描述符查看是否有就绪的 I/O 事件,需要大量的 CPU 资源,并且 select 的 fd_set 集合最大长度受到系统宏 FD_SETSIZE 的限制,通常不超过 1024。

三、pollpoll 函数是对 select 函数的一次改进,也是 I/O 多路复用机制中比较常见的一种。

poll 的基本原理是将一些文件描述符添加到一个 pollfd 数组中,通过 poll 函数等待这些数组中的文件描述符中有一个或多个就绪,然后在这些文件描述符上执行 I/O 操作。

与 select 不同的是,poll 数组不受数量限制,我们可以根据需要动态调整数组长度。

poll 函数也可以设置超时时间,当超时时间到达后若 pollfd 数组中没有就绪的文件描述符,则返回0。

poll 的缺点是仍然存在效率问题。

poll 在处理大量文件描述符时,每次都需要遍历整个 pollfd 数组,查看是否有就绪的 I/O 事件。

四、epollepoll 是 Linux 下的一种 I/O 多路复用机制,也是效率最高的一种。

epoll select

epoll select

对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。
总结:
1、select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
wait_queue_func_t func)
{
q->flags = 0;
q->private = NULL;
q->func = func;
}
可以看到,总体和select的实现是类似的,只不过它是创建了一个eppoll_entry结构pwq,只不过pwq->wait的func成员被设置成了回调函数ep_poll_callback(而不是default_wake_function,所以这里并不会有唤醒操作,而只是执行回调函数),private成员被设置成了NULL。最后吧pwq->wait链入到whead中(也就是设备等待队列中)。这样,当设备等待队列中的进程被唤醒时,就会调用ep_poll_callback了。
再看一下epoll:
/*

io的多路复用机制

io的多路复用机制

io的多路复用机制IO的多路复用机制是一种高效的网络编程技术,它能够在单线程下同时处理多个IO任务,提高系统的并发性能。

下面将详细介绍IO 的多路复用机制及其应用。

一、什么是IO的多路复用机制IO的多路复用机制是指通过一个线程来监听多个IO事件,并且在有IO事件发生时进行响应的机制。

它的主要原理是利用操作系统提供的系统调用,如select、poll、epoll等,来监听多个IO事件的状态变化。

当有IO事件就绪时,通过系统调用返回的事件集合来确定是哪些IO事件就绪,并进行相应的处理。

二、为什么需要IO的多路复用机制在传统的IO模型中,每个IO任务通常需要一个线程来处理,当IO 任务数量较大时,会导致线程数量激增,造成系统资源的浪费和线程调度的开销。

而采用IO的多路复用机制后,只需要一个线程来监听多个IO事件,大大减少了线程的数量和系统开销,提高了系统的并发性能。

三、IO的多路复用机制的应用1. 服务器编程:在服务器编程中,经常需要处理多个客户端的连接请求。

使用IO的多路复用机制可以通过一个线程监听多个客户端的连接请求,并在有连接请求到来时进行处理。

2. 并发网络编程:在并发网络编程中,通常需要同时处理多个网络请求。

使用IO的多路复用机制可以在单线程下同时处理多个网络请求,提高系统的并发性能。

3. 文件传输:在文件传输中,通常需要同时读写多个文件。

使用IO 的多路复用机制可以在单线程下同时处理多个文件的读写操作,提高文件传输的效率。

四、IO的多路复用机制的优点1. 提高系统的并发性能:通过一个线程同时监听多个IO事件,减少了线程的数量和系统开销,提高了系统的并发性能。

2. 减少了线程切换的开销:由于采用了单线程的方式来处理多个IO 事件,减少了线程的切换开销,提高了系统的响应速度。

3. 简化了程序的设计:采用IO的多路复用机制可以将多个IO任务放在一个线程中处理,简化了程序的设计和维护。

五、IO的多路复用机制的局限性1. 对于大量的IO任务,单个线程可能无法处理,需要采用多线程或线程池来进行处理。

linux中select、poll、epoll原理

linux中select、poll、epoll原理

linux中select、poll、epoll原理select、poll和epoll是Linux下常用的I/O多路复用技术,都用于实现高效的事件驱动型的网络编程。

1. select(选择)select是最古老的I/O多路复用机制,它通过在套接字上设置阻塞(阻塞方式)进行等待,一旦有文件描述符准备就绪(可读、可写等),则返回。

select使用fd_set集合来保存要监听的文件描述符,因此其监听的文件描述符数量受到系统给定的FD_SETSIZE限制。

select的实现原理是:在内核中创建一个称为“等待队列”的数据结构(fd_set),该队列保存了需要等待的文件描述符,当某个文件描述符就绪时,会通过和用户进程的映射表通知用户进程。

select通过轮询所有注册的文件描述符,检查哪些文件描述符已经准备好,并将准备好的文件描述符从用户态拷贝到内核态。

select的缺点是每次调用都需要轮询全部的注册文件描述符,效率较低。

2. poll(轮询)poll是在select的基础上进行改进的多路复用技术。

poll与select的最大区别在于,它没有限制文件描述符的数量,并且使用了一个pollfd结构体数组来保存每个文件描述符及其关注的事件。

poll在内核中创建一个称为“等待队列”的数据结构,该队列保存了需要等待的文件描述符,当某个文件描述符就绪时,会通过和用户进程的映射表通知用户进程。

poll的实现原理是:将用户进程注册要监听的文件描述符及其关注的事件存储在内核中的一个事件表中,当发生事件时,内核会将该事件存储在内核态的事件表中,并通知用户进程。

与select不同的是,poll只需在事件发生时拷贝某些信息到内核态,而不需要拷贝全部的文件描述符。

poll的缺点是,当注册的文件描述符数量较大时,每次调用poll都需要遍历整个事件表,效率较低。

3. epoll(事件通知)epoll是Linux特有的一种I/O多路复用机制,通过内核与用户空间的共享内存来实现高效的事件通知。

多路io复用的原理

多路io复用的原理

多路io复用的原理
多路IO复用是一种提高IO效率的技术。

它通过在一个线程
内监听多个IO事件,使得程序能够有效地处理多个读写操作。

多路IO复用的原理主要分为两个方面:
1. 就绪通知机制
多路复用技术利用操作系统的就绪通知机制来监听IO事件。

它将多个IO事件注册到同一个select或epoll的文件描述符上,并通过内核提供的函数将文件描述符传递给操作系统,让操作系统来进行监听。

当有事件发生时,操作系统会通知进程,进程就可以进行相应的处理。

2. 非阻塞IO机制
多路复用技术还需要采用非阻塞IO机制。

当程序阻塞在某个
IO操作时,如果事件没有发生,程序就无法继续执行,这会
影响整个应用程序的运行。

非阻塞IO机制可以避免这种情况
的发生,它会立即返回一个错误码而不是让程序一直等待。

这样程序就可以进行其他任务或者重新发起IO操作,等待IO
事件发生,从而提高程序的效率。

综上所述,多路IO复用的原理就是利用操作系统的就绪通知
机制和非阻塞IO机制,来提高程序的IO效率,使得程序能
够同时处理多个IO事件。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一、编目11. Epoll 是何方神圣?Epoll 可是当前在Linux 下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6 内核中正式引入,和select 相似,其实都I/O 多路复用技术而已,并没有什么神秘的。

其实在Linux 下设计并发网络程序,向来不缺少方法,比如典型的Apache 模型(Process Per Connection ,简称PPC ),TPC (Thread Per Connection )模型,以及select 模型和poll 模型,那为何还要再引入Epoll 这个东东呢?那还是有得说说的…2. 常用模型的缺点如果不摆出来其他模型的缺点,怎么能对比出Epoll 的优点呢。

2.1 PPC/TPC 模型这两种模型思想类似,就是让每一个到来的连接一边自己做事去,别再来烦我。

只是PPC 是为它开了一个进程,而TPC 开了一个线程。

可是别烦我是有代价的,它要时间和空间啊,连接多了之后,那么多的进程/ 线程切换,这开销就上来了;因此这类模型能接受的最大连接数都不会高,一般在几百个左右。

2.2 select 模型1. 最大并发数限制,因为一个进程所打开的FD (文件描述符)是有限制的,由FD_SETSIZE 设置,默认值是1024/2048 ,因此Select 模型的最大并发数就被相应限制了。

自己改改这个FD_SETSIZE ?想法虽好,可是先看看下面吧…2. 效率问题,select 每次调用都会线性扫描全部的FD 集合,这样效率就会呈现线性下降,把FD_SETSIZE 改大的后果就是,大家都慢慢来,什么?都超时了??!!3. 内核/ 用户空间内存拷贝问题,如何让内核把FD 消息通知给用户空间呢?在这个问题上select 采取了内存拷贝方法。

2.3 poll 模型基本上效率和select 是相同的,select 缺点的 2 和 3 它都没有改掉。

3. Epoll 的提升把其他模型逐个批判了一下,再来看看Epoll 的改进之处吧,其实把select 的缺点反过来那就是Epoll 的优点了。

3.1. Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048, 一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max 察看。

3.2. 效率提升,Epoll 最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll 的效率就会远远高于select 和poll 。

3.3. 内存拷贝,Epoll 在这点上使用了“共享内存”,这个内存拷贝也省略了。

4. Epoll 为什么高效Epoll 的高效和其数据结构的设计是密不可分的,这个下面就会提到。

首先回忆一下select 模型,当有I/O 事件到来时,select 通知应用程序有事件到了快去处理,而应用程序必须轮询所有的FD 集合,测试每个FD 是否有事件发生,并处理事件;代码像下面这样:int res = select(maxfd+1, &readfds, NULL, NULL, 120);if (res > 0){for (int i = 0; i < MAX_CONNECTION; i++){if (FD_ISSET(allConnection[i], &readfds)){handleEvent(allConnection[i]);}}}// if(res == 0) handle timeout, res < 0 handle errorEpoll 不仅会告诉应用程序有I/0 事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些信息应用程序就能直接定位到事件,而不必遍历整个FD 集合。

int res = epoll_wait(epfd, events, 20, 120);for (int i = 0; i < res;i++){handleEvent(events[n]);}5. Epoll 关键数据结构前面提到Epoll 速度快和其数据结构密不可分,其关键数据结构就是:struct epoll_event {__uint32_t events; // Epoll eventsepoll_data_t data; // User data variable};typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;} epoll_data_t;可见epoll_data 是一个union 结构体, 借助于它应用程序可以保存很多类型的信息:fd 、指针等等。

有了它,应用程序就可以直接定位目标了。

6. 使用Epoll既然Epoll 相比select 这么好,那么用起来如何呢?会不会很繁琐啊… 先看看下面的三个函数吧,就知道Epoll 的易用了。

int epoll_create(int size);生成一个Epoll 专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的socket fd 上是否发生以及发生了什么事件。

size 就是你在这个Epoll fd 上能关注的最大socket fd 数,大小自定,只要内存足够。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );控制某个Epoll 文件描述符上的事件:注册、修改、删除。

其中参数epfd 是epoll_create() 创建Epoll 专用的文件描述符。

相对于select 模型中的FD_SET 和FD_CLR 宏。

int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);等待I/O 事件的发生;参数说明:epfd: 由epoll_create() 生成的Epoll 专用的文件描述符;epoll_event: 用于回传代处理事件的数组;maxevents: 每次能处理的事件数;timeout: 等待I/O 事件发生的超时值;返回发生事件数。

相对于select 模型中的select 函数。

7. 例子程序下面是一个简单Echo Server 的例子程序,麻雀虽小,五脏俱全,还包含了一个简单的超时检查机制,简洁起见没有做错误处理。

view plain1.//2.// a simple echo server using epoll in linux3.//4.// 2009-11-055.// by sparkling6.//7.#include <sys/socket.h>8.#include <sys/epoll.h>9.#include <netinet/in.h>10.#include <arpa/inet.h>11.#include <fcntl.h>12.#include <unistd.h>13.#include <stdio.h>14.#include <errno.h>15.#include <iostream>ing namespace std;17.#define MAX_EVENTS 50018.struct myevent_s19.{20.int fd;21.void (*call_back)(int fd, int events, void *arg);22.int events;23.void *arg;24.int status; // 1: in epoll wait list, 0 not in25.char buff[128]; // recv data buffer26.int len;27.long last_active; // last active time28.};29.// set event30.void EventSet(myevent_s *ev, int fd, void (*call_back)(int, int, void*), void *arg)31.{32. ev->fd = fd;33. ev->call_back = call_back;34. ev->events = 0;35. ev->arg = arg;36. ev->status = 0;37. ev->last_active = time(NULL);38.}39.// add/mod an event to epoll40.void EventAdd(int epollFd, int events, myevent_s *ev)41.{42.struct epoll_event epv = {0, {0}};43.int op;44. epv.data.ptr = ev;45. epv.events = ev->events = events;46.if(ev->status == 1){47. op = EPOLL_CTL_MOD;48. }49.else{50. op = EPOLL_CTL_ADD;51. ev->status = 1;52. }53.if(epoll_ctl(epollFd, op, ev->fd, &epv) < 0)54. printf("Event Add failed[fd=%d]/n", ev->fd);55.else56. printf("Event Add OK[fd=%d]/n", ev->fd);57.}58.// delete an event from epoll59.void EventDel(int epollFd, myevent_s *ev)60.{61.struct epoll_event epv = {0, {0}};62.if(ev->status != 1) return;63. epv.data.ptr = ev;64. ev->status = 0;65. epoll_ctl(epollFd, EPOLL_CTL_DEL, ev->fd, &epv);66.}67.int g_epollFd;68.myevent_s g_Events[MAX_EVENTS+1]; // g_Events[MAX_EVENTS] is used by listenfd69.void RecvData(int fd, int events, void *arg);70.void SendData(int fd, int events, void *arg);71.// accept new connections from clients72.void AcceptConn(int fd, int events, void *arg)73.{74.struct sockaddr_in sin;75. socklen_t len = sizeof(struct sockaddr_in);76.int nfd, i;77.// accept78.if((nfd = accept(fd, (struct sockaddr*)&sin, &len)) == -1)79. {80.if(errno != EAGAIN && errno != EINTR)81. {82. printf("%s: bad accept", __func__);83. }84.return;85. }86.do87. {88.for(i = 0; i < MAX_EVENTS; i++)89. {90.if(g_Events[i].status == 0)91. {92.break;93. }94. }95.if(i == MAX_EVENTS)96. {97. printf("%s:max connection limit[%d].", __func__, MAX_EVENTS);98.break;99. }100.// set nonblocking101.if(fcntl(nfd, F_SETFL, O_NONBLOCK) < 0) break;102.// add a read event for receive data103. EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]);104. EventAdd(g_epollFd, EPOLLIN|EPOLLET, &g_Events[i]);105. printf("new conn[%s:%d][time:%d]/n", inet_ntoa(sin.sin_addr), ntohs (sin.sin_port), g_Events[i].last_active);106. }while(0);107.}108.// receive data109.void RecvData(int fd, int events, void *arg)110.{111.struct myevent_s *ev = (struct myevent_s*)arg;112.int len;113.// receive data114. len = recv(fd, ev->buff, sizeof(ev->buff)-1, 0);115. EventDel(g_epollFd, ev);116.if(len > 0)117. {118. ev->len = len;119. ev->buff[len] = '/0';120. printf("C[%d]:%s/n", fd, ev->buff);121.// change to send event122. EventSet(ev, fd, SendData, ev);123. EventAdd(g_epollFd, EPOLLOUT|EPOLLET, ev);124. }125.else if(len == 0)126. {127. close(ev->fd);128. printf("[fd=%d] closed gracefully./n", fd);129. }130.else131. {132. close(ev->fd);133. printf("recv[fd=%d] error[%d]:%s/n", fd, errno, strerror(errno));134. }135.}136.// send data137.void SendData(int fd, int events, void *arg)138.{139.struct myevent_s *ev = (struct myevent_s*)arg;140.int len;141.// send data142. len = send(fd, ev->buff, ev->len, 0);143. ev->len = 0;144. EventDel(g_epollFd, ev);145.if(len > 0)146. {147.// change to receive event148. EventSet(ev, fd, RecvData, ev);149. EventAdd(g_epollFd, EPOLLIN|EPOLLET, ev);150. }151.else152. {153. close(ev->fd);154. printf("recv[fd=%d] error[%d]/n", fd, errno);155. }156.}157.void InitListenSocket(int epollFd, short port)158.{159.int listenFd = socket(AF_INET, SOCK_STREAM, 0);160. fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking161. printf("server listen fd=%d/n", listenFd);162. EventSet(&g_Events[MAX_EVENTS], listenFd, AcceptConn, &g_Events[MAX_EVE NTS]);163.// add listen socket164. EventAdd(epollFd, EPOLLIN|EPOLLET, &g_Events[MAX_EVENTS]);165.// bind & listen166. sockaddr_in sin;167. bzero(&sin, sizeof(sin));168. sin.sin_family = AF_INET;169. sin.sin_addr.s_addr = INADDR_ANY;170. sin.sin_port = htons(port);171. bind(listenFd, (const sockaddr*)&sin, sizeof(sin));172. listen(listenFd, 5);173.}174.int main(int argc, char **argv)175.{176.short port = 12345; // default port177.if(argc == 2){178. port = atoi(argv[1]);179. }180.// create epoll181. g_epollFd = epoll_create(MAX_EVENTS);182.if(g_epollFd <= 0) printf("create epoll failed.%d/n", g_epollFd); 183.// create & bind listen socket, and add to epoll, set non-blocking 184. InitListenSocket(g_epollFd, port);185.// event loop186.struct epoll_event events[MAX_EVENTS];187. printf("server running:port[%d]/n", port);188.int checkPos = 0;189.while(1){190.// a simple timeout check here, every time 100, better to use a min i-heap, and add timer event191.long now = time(NULL);192.for(int i = 0; i < 100; i++, checkPos++) // doesn't check listen fd193. {194.if(checkPos == MAX_EVENTS) checkPos = 0; // recycle195.if(g_Events[checkPos].status != 1) continue;196.long duration = now - g_Events[checkPos].last_active;197.if(duration >= 60) // 60s timeout198. {199. close(g_Events[checkPos].fd);200. printf("[fd=%d] timeout[%d--%d]./n", g_Events[checkPos].fd, g_Events[checkPos].last_active, now);201. EventDel(g_epollFd, &g_Events[checkPos]);202. }203. }204.// wait for events to happen205.int fds = epoll_wait(g_epollFd, events, MAX_EVENTS, 1000); 206.if(fds < 0){207. printf("epoll_wait error, exit/n");208.break;209. }210.for(int i = 0; i < fds; i++){211. myevent_s *ev = (struct myevent_s*)events[i].data.ptr; 212.if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN)) // read ev ent213. {214. ev->call_back(ev->fd, events[i].events, ev->arg);215. }216.if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) // write event217. {218. ev->call_back(ev->fd, events[i].events, ev->arg);219. }220. }221. }222.// free resource223.return 0;224.}二、编目2Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select 函数。

相关文档
最新文档