使用非阻塞IO的應用程序經常使用select,poll,epoll系統調用;它們的功能本質上是一樣的:都允許進程決定是否可以對一個或者多個打開的文件做非阻塞的讀取或者寫入;這些電泳也會阻塞進程,直到給定的文件描述符中的任何一個可讀取或者寫入;因此,它們常常用於那些需要使用多個輸入或者輸出流而又不會阻塞於其中任何一個流的應用程序中;同一功能之所以要由多個獨立的函數提供,是因為其中兩個幾乎是由兩個不同的Unix團體分別實現的:select在BSD中引入,而poll由SystemV引入;epoll系統調用則用於將poll函數擴展到能夠處理數千個文件描述符;
poll在file_operations結構中的定義如下:
1 unsigned int (*poll) (struct file *, struct poll_table_struct *);
|
1
|
unsigned int (*poll) (struct file *, struct poll_table_struct *);
|
當用戶空間程序在驅動程序關聯的文件描述符上執行select,poll,epoll系統調用時,該驅動程序的方法將被調用;該poll函數的功能分為兩步:
1. 在一個或者多個可指示poll狀態變化的等待隊列上調用poll_wait;如果當前沒有文件描述符可用來執行IO,則內核將進程在傳遞到該系統調用的所有文件描述符對應的等待隊列上等待;
1 static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
2. 返回一個用來描述操作是否可以立即無阻塞執行的位掩碼;
POLLIN-如果設備可以無阻塞的讀取,就設置該位;
POLLRDNORM-如果通常的數據已經就緒,就可以讀取,就應該設置該位;一個可讀設備返回POLLIN|POLLRDNORM;
POLLRDBAND-指示可以從設備讀取out-of-band帶外數據;
POLLPRI-可以無阻塞的讀取高優先級(即out-of-band)的數據;設置該位會導致select報告文件發生一個異常,這是由於select把out-of-band數據作為異常對待;
POLLHUP-當讀取設備的進程到達文件尾時,驅動程序必須設置POLLHUP位,調用select的進程會被告知設備是可讀的;
POLLERR-設備發生了錯誤;如果調用poll,就會報告設備既可以讀也可以寫,因為讀寫都會無阻塞的返回一個錯誤碼;
POLLOUT-如果設備可以無阻塞的寫入,就在返回值中設置該位;
POLLWRNORM-該位和POLLOUT的意義一樣,有時其實就是同一個數字;一個可寫的設備將返回POLLOUT|POLLWRNORM;
POLLWRBAND-與POLLRDBAND類似,這一位標識具有非零優先級的數據可以被寫入設備;只有數據報的poll實現中使用了這一位,因為數據報可以傳輸out-of-band數據;
POLLRDBAND和POLLWRBAND只在於套接字相關的文件描述符中才有意義,設備驅動程序通常用不到這兩個標記;
隨意從內核摘取了一段代碼,對上面描述的兩個步驟體現的很明確;
1 static unsigned int evdev_poll(struct file *file, poll_table *wait) 2 { 3 struct evdev_client *client = file->private_data; 4 struct evdev *evdev = client->evdev; 5 unsigned int mask; 6 7 poll_wait(file, &evdev->wait, wait); 8 9 if (evdev->exist && !client->revoked) 10 mask = POLLOUT | POLLWRNORM; 11 else 12 mask = POLLHUP | POLLERR; 13 14 if (client->packet_head != client->tail) 15 mask |= POLLIN | POLLRDNORM; 16 17 return mask; 18 }
