I/O复用:select、poll、epoll

1、select

2、poll

3、epoll

4、关于ET和LT两种工作模式

 5、epoll函数底层实现过程

6、epoll通用框架:

7、完整的服务器端例子


Linux支持3中I/O复用,分别是select、poll、epoll,下面分别进行介绍。

1、select

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • nfds:定被监听的文件描述符的总数,设置为监听的所有文件描述符中最大值加1,因为文件描述符是从0开始计数的。
  • readfds、writefds、exceptfds:分别指向可读、可写、异常事件对应的文件描述符集合。程序员通过这3个参数向该调用传入自己感兴趣的文件描述符。函数返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪。原型如下:
#define XFD_SETSIZE     256
#define FD_SETSIZE      XFD_SETSIZE

typedef long fd_mask;
#define NBBY    8
#define NFDBITS (sizeof(fd_mask) * NBBY)

#define howmany(x,y)    (((x)+((y)-1))/(y))

#if defined(BSD) && BSD < 198911
typedef struct fd_set 
{
    fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
} fd_set;
#endif

    fds_bit共占据32字节,即256位。该数组的每个元素的每一位(bit)标记一个文件描述符。fd_set能容纳的文件描述符由FD_SETSIZE指定,显然这限制了select()能同时处理的文件描述符的总数。该系统调用还提供了一些列宏方便程序员实现位操作:

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);       //设置fd中的fd位
void FD_ZERO(fd_set *set);              //清零set中所有位
  •  timeout: 设置函数的超时时间。它是一个timeval的普通(非const)指针,内核可以修改此参数以告诉应用程序函数阻塞等待了多久。不过内核返回的该值不能完全信任,比如调用失败时timeout的值是不确定的。timeout有三种可能:1)永远等下去,此时timeout设置为NULL;2)等待一段时间;3)根本不等待,值为0.
  • 返回值:执行成功时返回就绪的文件描述符的总数;若在超时时间内没有任何文件描述符就绪,select()将返回0;失败将返回-1并设置errno。若在select()阻塞等待期间程序收到信号,将立即返回-1并设置errno为EINTR

    select的优缺点:

  • 所能监听的最大描述符数为1024
  • 函数返回后只能进行轮询哪个描述符已准备好
  • 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

2、poll

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • fds:该参数是一个pollfd结构体类型的指针,它指定所有程序员感兴趣的文件描述符上发生的可读、可写、异常等事件。既然它是一个结构体指针,就可以指向该类型的数组。pollfd结构体的原型为:
struct pollfd {
   int   fd;         /* 文件描述符 */
   short events;     /* 注册的事件 */
   short revents;    /* 实际发生的事件,由内核填充 */
};

    fd成员指定文件描述符。event成员告诉内核要监听fd上的哪些事件,它可以是一系列事件的按位或。revents成员由内核修改,以通知应用程序fd上实际发生了哪些事件。event的取值为:

    

  • nfds:fds数组成员的的个数由参数nfds指定。显然,这个比select()的设计要灵活一点: 用户可以监测任意多数目文件描述符。
  • timeout:该参数指定函数的超时事件,单位为毫秒。当timeout为-1时,poll调用将一直阻塞直到监听的目标事件发生;当timeout为0时,poll()调用立即返回。
  • 返回值:执行成功时返回就绪的文件描述符的总数;若在超时时间内没有任何文件描述符就绪,poll()将返回0;失败将返回-1并设置errno。若在select()阻塞等待期间程序收到信号,将立即返回-1并设置errno为EINTR。

    poll的优缺点:

  • 相比select,无最大监听描述符的限制,但是仍然需要轮询哪个描述符是否准备好。
  • 大量的fd的数组被整体复制于用户态和内核地址空间之间。
  • poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

3、epoll:

(1)创建一个epoll的句柄

int epoll_create(int size):

    成功则返回 epoll 专用的文件描述符epfd,失败返回 -1。size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

(2)epoll的事件注册

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值