总结一下三个io复用函数

1.select  

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

1)nfds为被监听文件描述符的总数,通常为个数+1

2)struct fd_set由定义来看__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];包含一个整形数组,其中每个位代表着一个文件描述符,这代表了读文件描述符的集合。需要注意的是,监听文件描述符的最大数由fd_setsize决定,其最大值是1024。

3)代表写文件描述符的集合。

4)异常文件描述符的集合。

5)struct timeout结构

struct timeval
  {
    __time_t tv_sec;        /* Seconds.  */
    __suseconds_t tv_usec;    /* Microseconds.  */
  };

第一个成员为秒,第二个成员为微妙,可见提供到微妙的支持。如果该结构指定为null,则一直阻塞此函数直到有事件发生。

6)return:成功返回0,失败返回-1并置errno。

7)提供以下宏函数代替位的繁琐操作

#define    FD_SET(fd, fdsetp)    __FD_SET (fd, fdsetp)  //将文件描述符置位,相当于加入到该集合当中。
#define    FD_CLR(fd, fdsetp)    __FD_CLR (fd, fdsetp)  //清除该文件描述符上的位,相当于从集合中取出。
#define    FD_ISSET(fd, fdsetp)    __FD_ISSET (fd, fdsetp)  //判断文件描述符是否在其中
#define    FD_ZERO(fdsetp)        __FD_ZERO (fdsetp)  //将该文件描述符清零。

8)更要注意的是,在向内核注册事件,通知完该事件之后,一定要重新注册一遍,即fd_set需要重新FD_SET进文件描述符。因为只能监听3个事件,读写异常,所以限制较多。

2.poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

1)struct pollfd结构

               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

  1.1)events是需要在该文件描述符上监听的事件,通常是下列宏的位掩码:

              POLLIN               //该文件描述符有数据可读
             There is data to read. POLLPRI        //紧急数据(oob) There is urgent data to read (e.g., out-of-band data on TCP socket; pseudoterminal master in packet mode has seen state change in slave). POLLOUT        //写的缓冲区现在不阻塞了,可以该描述符写东西 Writing now will not block. POLLRDHUP (since Linux 2.6.17)    //仅linux支持,文件描述符对方关闭了写,或者关闭了连接,这个很有必要知道 Stream socket peer closed connection, or shut down writ‐ ing half of connection. The _GNU_SOURCE feature test macro must be defined (before including any header files) in order to obtain this definition. POLLERR        //错误事件 Error condition (output only). POLLHUP        //挂起 Hang up (output only). POLLNVAL      //文件描述符不有效 Invalid request: fd not open (output only).
  如果定义了_XOPEN_SOURCE,那么还有如下的宏:

          POLLRDNORM       //normal,正常的可读数据,相当与pollin
              Equivalent to POLLIN.

          POLLRDBAND    //linux中不可用
                       Priority band data can be read (generally unused onLinux).

          POLLWRNORM    //相当于pollout
                       Equivalent to POLLOUT.

          POLLWRBAND    //更高优先级的数据可写
              Priority data may be written.

  1.2)内核返回的事件。

2)文件描述符的总数,typeof unsigned long int nfds_t

3)监听时间,指定为0立即返回,指定为-1则一直阻塞直到所监听描述符上有事件发生。

4)return:正常返回0,异常返回-1并置errno

3.epoll

  //需要如下的一组函数来完成     
    * epoll_create(2) creates an epoll instance and returns a file descriptor referring to that instance. (The more recent epoll_cre‐ ate1(2) extends the functionality of epoll_create(2).) * Interest in particular file descriptors is then registered via     epoll_ctl(2). The set of file descriptors currently registered on an epoll instance is sometimes called an epoll set. * epoll_wait(2) waits for I/O events, blocking the calling thread if no events are currently available.

1)向内核注册事件表,参数为需要监听的文件描述符的个数。

2)extern int epoll_ctl (int __epfd, int __op, int __fd,struct epoll_event *__event) __THROW;  //类似于fcntl函数

  2.1)指定的epollfd事件表的描述符

  2.2)op操作的类型,有如下的宏可选

#define EPOLL_CTL_ADD 1    /* Add a file descriptor to the interface.  */       //添加
#define EPOLL_CTL_DEL 2    /* Remove a file descriptor from the interface.  */   //删除
#define EPOLL_CTL_MOD 3    /* Change file descriptor epoll_event structure.  */  //修改

  2.3)需要监听的文件描述符

  2.4)struct epoll_event结构

struct epoll_event
{
  uint32_t events;    /* Epoll events */
  epoll_data_t data;    /* User data variable */
} __EPOLL_PACKED;

    2.4.1)events为需要监听事件的位掩码,如下所示

enum EPOLL_EVENTS
  {
    EPOLLIN = 0x001,          //有数据可读
#define EPOLLIN EPOLLIN
    EPOLLPRI = 0x002,    //有紧急数据(oob)可读
#define EPOLLPRI EPOLLPRI
    EPOLLOUT = 0x004,    //该描述符有缓冲区可供写
#define EPOLLOUT EPOLLOUT
    EPOLLRDNORM = 0x040,      //normal
#define EPOLLRDNORM EPOLLRDNORM
    EPOLLRDBAND = 0x080,        //优先级数据可读
#define EPOLLRDBAND EPOLLRDBAND
    EPOLLWRNORM = 0x100,      //normal write
#define EPOLLWRNORM EPOLLWRNORM
    EPOLLWRBAND = 0x200,      //优先级数据可写
#define EPOLLWRBAND EPOLLWRBAND
    EPOLLMSG = 0x400,        //没了解过
#define EPOLLMSG EPOLLMSG
    EPOLLERR = 0x008,
#define EPOLLERR EPOLLERR       //错误消息
    EPOLLHUP = 0x010,
#define EPOLLHUP EPOLLHUP    //挂起
    EPOLLRDHUP = 0x2000,
#define EPOLLRDHUP EPOLLRDHUP    //描述符的对方关闭了写操作,或者关闭了连接
    EPOLLWAKEUP = 1u << 29,
#define EPOLLWAKEUP EPOLLWAKEUP    //没了解
    EPOLLONESHOT = 1u << 30,
#define EPOLLONESHOT EPOLLONESHOT  //在任意时刻仅可以触发一个可读可写异常事件,常用与多线程
    EPOLLET = 1u << 31
#define EPOLLET EPOLLET   //使用et方式工作,默认lt
  };

    2.4.2)

typedef union epoll_data
{
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;

  可以看出是一个union,ptr指的是数据地址,fd指的是文件描述符,通常我们使用文件描述符。

    2.4.3)关于et(边缘敏感)与lt(水平敏感):lt模式:通知之后可不处理。et:通知之后必须处理,且对该事件仅通知一次

  2.5)成功返回0,失败返回-1

3)

extern int epoll_wait (int __epfd, struct epoll_event *__events,
               int __maxevents, int __timeout);

  3.1)指定的epollfd

  3.2)触发事件的数组集合

  3.3)需要监听的最大描述符数量

  3.4)timeout,-1则一直阻塞直到有事件发生。

  3.5)返回有发生事件的文件描述符个数。

4)通常我们要往一个epollfd添加文件描述符的函数接口可以这么写:

void addfd(int epollfd,int fd)
{
    struct epoll_event event;
    event.data.fd=fd;
    event.events=EPOLLIN|EPOLLET;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
    setnonblocking(fd);
}

5)通常设置一个文件描述符为非阻塞可以这么写:

int setnonblocking(int fd)
{
    int old_option=fcntl(fd,F_GETFL);
    int new_option=old_option|O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);
    return old_option;
}

4.详情请看man或者源码

猜你喜欢

转载自www.cnblogs.com/manch1n/p/10555815.html