1.同步
筆者在本文只分析poll同步機制,首先看poll函數的用法:
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
struct pollfd {
int fd; /*文件描述符*/
short events; /* 需要測試的事件 */
short revents; /* 實際發生的事件,也就是返回結果 */
};
功能:
查詢數組fds中的fd是否發生了events事件,若發生了events事件,poll立即返回,不阻塞;若沒有fd發生events事件,則poll阻塞,經過timeout時間後被喚醒。
參數:
nfds指示fds數組的項數,timeout指示poll函數的超時時間(超過時間,則調用poll函數的進程被喚醒),timeout=0,進程不阻塞;timeout<0,進程阻塞到事件發生爲止。例子如下:
ret = poll(fds, 1, 5000);
if (ret == 0) //fds中沒有可讀寫的設備文件
{
do something;
}
else //fds中有可讀寫的設備文件
{
do otherthing;
}
返回值:
>0,發生了指定events的fd的數量;=0,沒有fd發生了events;-1,調用poll失敗
原理:
poll函數最終會調用每個fd對應的xxx_poll函數(屬於驅動程序),xxx_poll調用poll_wait()將進程掛到等待隊列(不會阻塞),xxx_poll()函數返回一個mask給poll(),該mask裏面包含了特定的events,xxx_poll()決定哪種情況下使得mask裏面含有POLLIN、POLLRDNORM等events 。poll()函數調用schedule_timeout()進行休眠。例子如下:
static unsigned xxx_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不休眠
if (condition)
mask |= POLLIN | POLLRDNORM;//條件成立,則設備文件可讀,返回表示可讀的mask
return mask;
}
2.異步
原理:
由文件(驅動程序)嚮應用程序發送信號,應用程序再調用信號函數。
應用層:
首先需要應用程序綁定一個信號函數,當收到某個信號後,執行該函數;然後需要將該進程和某個設備文件綁定,這樣設備文件可讀寫時,才知道給那個進程發信號。例子如下:
signal(SIGIO, my_signal);.//綁定信號函數
fcntl(fd, F_SETOWN, getpid());//綁定進程和設備文件
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);//給設備文件添加異步通知標識(調用驅動程序裏的xxx_fasync()函數)
三個步驟執行後,設備文件就可以向進程發送信號了。
驅動層:
在設備驅動程序裏,實現向進程發送信號的功能。
xxx_fasync(),調用fasync_helper()函數,初始化一個結構體
kill_fasync(),向進程發送信號。
static int xxx_fasync (int fd, struct file *filp, int on)
{
return fasync_helper (fd, filp, on, &button_async);//初始化fasync_struct結構體,該結構體在向進程發送信號時用到
}
kill_fasync (&button_async, SIGIO, POLL_IN); //設備文件可讀寫時,調用該函數,向進程發送信號
3.總結
我們在讀寫設備文件時,可以採用同步或異步機制,同步機制下,需要我們的應用程序自己不斷去查詢設備文件是否可讀寫,屬於主動出擊;而採用異步機制時,應用程序是被動接受信號,當設備文件可讀寫時,由設備驅動程序給應用程序發信號,應用程序然後纔去讀寫設備文件,屬於被動接受。
同步和異步實現,都是靠對應的設備驅動程序。