總結一下三個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) //將文件描述符置位,相當於加入到該集合當中。 #defineFD_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復用函數