標籤:

select和poll的區別

之前看代碼時,很討厭select,感覺使用起來一點都不優雅,看到陳碩的muduo中poll用的很美觀,於是新代碼也改用了poll。當然用epoll也可以,但是考慮到我們不需要那麼大的並發,poll更簡單。

本篇中的區別參考自What are the differences between poll and select?

select採用的是位掩碼的模型,參考NDK中sysroot/usr/include/sys/select.h,即

#define FD_SETSIZE 1024#define NFDBITS (8 * sizeof(unsigned long))#define __FDSET_LONGS (FD_SETSIZE/NFDBITS)typedef struct { unsigned long fds_bits[__FDSET_LONGS];} fd_set;#define __FDELT(fd) ((fd) / NFDBITS)#define __FDMASK(fd) (1UL << ((fd) % NFDBITS))#define __FDS_BITS(set) (((fd_set*)(set))->fds_bits)#define FD_ZERO(set) (memset(set, 0, sizeof(*(fd_set*)(set))))#if defined(__BIONIC_FORTIFY)extern void __FD_CLR_chk(int, fd_set*, size_t);extern void __FD_SET_chk(int, fd_set*, size_t);extern int __FD_ISSET_chk(int, fd_set*, size_t);#define FD_CLR(fd, set) __FD_CLR_chk(fd, set, __bos(set))#define FD_SET(fd, set) __FD_SET_chk(fd, set, __bos(set))#define FD_ISSET(fd, set) __FD_ISSET_chk(fd, set, __bos(set))#else#define FD_CLR(fd, set) (__FDS_BITS(set)[__FDELT(fd)] &= ~__FDMASK(fd))#define FD_SET(fd, set) (__FDS_BITS(set)[__FDELT(fd)] |= __FDMASK(fd))#define FD_ISSET(fd, set) ((__FDS_BITS(set)[__FDELT(fd)] & __FDMASK(fd)) != 0)#endif /* defined(__BIONIC_FORTIFY) */

不考慮__BIONIC_FORTIFY,即select是通過申請了一個unsigned long fds_bits[]的數組,數組中的每一位代表一個文件句柄的掩碼,即select最大隻支持fd < 1024的情況,如果fd >= 1024,就必須重新編譯內核了。

那麼總結一下,

區別1:select使用的是定長數組,而poll是通過用戶自定義數組長度的形式(pollfd[])。

區別2:select只支持最大fd < 1024,如果單個進程的文件句柄數超過1024,select就不能用了。poll在介面上無限制,考慮到每次都要拷貝到內核,一般文件句柄多的情況下建議用epoll。

區別3:select由於使用的是位運算,所以select需要分別設置read/write/error fds的掩碼。而poll是通過設置數據結構中fd和event參數來實現read/write,比如讀為POLLIN,寫為POLLOUT,出錯為POLLERR:

struct pollfd pfd;pfd.fd = fd;pfd.events = POLLIN;pfd.revents = 0;

區別4:select中fd_set是被內核和用戶共同修改的,所以要麼每次FD_CLR再FD_SET,要麼備份一份memcpy進去。而poll中用戶修改的是events,系統修改的是revents。所以參考muduo的代碼,都不需要自己去清除revents,從而使得代碼更加簡潔。

區別5:select的timeout使用的是struct timeval *timeout,poll的timeout單位是int。

區別6:select使用的是絕對時間,poll使用的是相對時間。

區別7:select的精度是微秒(timeval的分度),poll的精度是毫秒。

區別8:select的timeout為NULL時表示無限等待,否則是指定的超時目標時間;poll的timeout為-1表示無限等待。所以有用select來實現usleep的。

區別9:理論上poll可以監聽更多的事件,比如sysroot/usr/include/asm-generic/poll.h中有

#define POLLIN 0x0001#define POLLPRI 0x0002/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */#define POLLOUT 0x0004#define POLLERR 0x0008#define POLLHUP 0x0010#define POLLNVAL 0x0020/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */#define POLLRDNORM 0x0040#define POLLRDBAND 0x0080#ifndef POLLWRNORM#define POLLWRNORM 0x0100/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */#endif#ifndef POLLWRBAND#define POLLWRBAND 0x0200#endif/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */#ifndef POLLMSG#define POLLMSG 0x0400#endif#ifndef POLLREMOVE/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */#define POLLREMOVE 0x1000#endif#ifndef POLLRDHUP#define POLLRDHUP 0x2000/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */#endif#define POLLFREE 0x4000#define POLL_BUSY_LOOP 0x8000struct pollfd {/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ int fd; short events; short revents;};

至於epoll,其實可以理解為poll的add/del/modify的增強版,使得用戶不需要去關心最大的集合,而只需要去關心在需要add/delete的時候往內核註冊一下。

推薦閱讀:

systemd 是不是管的太多了?
SSH 基本用法
只使用 Linux 系統是怎樣一種體驗?
在Linux內核模塊中對空指針解引用,為什麼內核不掛?

TAG:Linux |