1. 程式人生 > >總結一下三個io復用函數

總結一下三個io復用函數

水平 sin any blocking 描述符 mask 了解 fun line

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或者源碼

總結一下三個io復用函數