2016.8.25--I/O复用函数(select,poll,epoll)

来源:互联网 发布:x8软件下载 编辑:程序博客网 时间:2024/05/05 22:24

I/O复用

  • IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

      (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。

      (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。

      (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。

      (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。

      (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
      


3个I/O复用函数

int select(int nfds, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);//返回值 就绪描述符的数目,超时返回0,出错返回-1//第一个参数nfds 指定待测试的描述字的个数,值是待测试的最大描述字+1//中间三个参数(readset,writeset,exceptset)指定让内核测试readwrite,except的描述字,可以通过4个宏设置/*    void FD_ZERO(fd_set *fdset);        //清空集合    void FD_SET (int fd,fd_set *fdset);  //将一个给定的文件描述符加入集合中    void FD_CLR (int fd,fd_set *fdset);  //将一个给定的文件描述符从集合中删除    int FD_ISSET(int fd,fd_set *fdset); //检查集合中指定的文件描述符是否可以读写*///最后一个参数tiemout 告知内核等待所指定描述字中的任何一个就绪可以花费的时间。/* timeval结构用于指定这段时间的秒数和微秒数。  struct timeval  {       long tv_sec;   //秒       long tv_usec;  //微妙  };  这个参数有三种可能:    (1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针。    (2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。    (3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0*/

poll

int poll(struct pollfd *fds,unsigned int nfds, int timeout);/*    struct pollfd     {        int   fd;       //文件描述符        short events;   //等待的事件        short revents;  //实际发生了的事件         };*///nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量//timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。 //timeout指定为负数值表示无限超时,使poll()一直挂起直到一个指定事件发生 //timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。//每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域/*POLLIN        有数据可读。POLLRDNORM      有普通数据可读。POLLRDBAND     有优先数据可读。POLLPRI       有紧迫数据可读。POLLOUT        写数据不会导致阻塞。POLLWRNORM      写普通数据不会导致阻塞。POLLWRBAND      写优先数据不会导致阻塞。POLLMSGSIGPOLL    消息可用。*/

epoll

//使用一个文件描述符管理多个描述符,将用户关心的事件存放在内核的一个事件表中,只需要copy一次(用户空间<->内核空间)int epoll_create(int size);//创建一个epolll对象//size是最大fd监听+1int epoll_ctl(int epfd ,int op ,int fd ,struct epoll_event *event);//op的取值有:EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL,表示你要从监听集中添加、去除或修改某个文件描述符。int epoll_wait(int epfd ,struct epoll_event *events ,int maxevent);//成功返回就绪的文件描述符的个数,失败-1,并设置errno//等待 I/O 事件的发生//epfd: 由 epoll_create() 生成的 Epoll 专用的文件描述符//epoll_event: 用于回传代处理事件的数组//maxevents: 每次能处理的事件数//timeout: 等待 I/O 事件发生的超时值 返回发生事件数。//epoll数据结构/*struct epoll_event {       __uint32_t events;      // Epoll events       epoll_data_t data;      // User data variable   };   typedef union epoll_data {       void *ptr;       int fd;       __uint32_t u32;       __uint64_t u64;   } epoll_data_t;   *///工作方式 LT:会重复通告,直到被处理//        ET:必须立即处理,不会重复通告

3个函数特点

//select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替//poll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程//select/*一个进程可监视的fd数量被限制,最大是FD_SIZE,默认是1024需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大对socket进行扫描时是线性扫描*///poll/*poll优点1)poll() 在应付大数目的文件描述符的时候相比于select速度更快 2)它没有最大连接数的限制,原因是它是基于链表来存储的。poll缺点1)大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。 2)与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符*///epoll/*epoll优点1)支持一个进程打开大数目的socket描述符(FD)2)IO效率不随FD数目增加而线性下降3)使用mmap加速内核与用户空间的消息传递。*/

关于epoll不一定比select高效的场景

并发量低,socket都比较活跃的情况下,两者效率近似


个人理解

  • select对每个感兴趣的事件,每次调用时都需要把FD集合从用户态拷贝到内核态

  • poll和select很相似,只是一个是struct pollfd,另一个是fd_set

  • epoll是在每次增加fd的时候,在epoll_ctl()就将fd拷贝进内核,减少了很多重复拷贝,保证每个fd只会被拷贝1次

  • epoll使用了callback(回调函数)机制,效率远高于轮询,所以提升了性能


推荐阅读

http://blog.csdn.net/lizhiguo0532/article/details/6568964#comments

http://blog.csdn.net/lizhiguo0532/article/details/6568968

http://blog.csdn.net/lizhiguo0532/article/details/6568969

http://m.blog.csdn.net/article/details?id=52226501 (!!!有面试需要的可以看这篇)

http://www.cnblogs.com/Anker/p/3265058.html (!!!这篇很不错)

http://blog.csdn.net/turkeyzhou/article/details/8504554

0 0
原创粉丝点击