天天看點

linux阻塞後的信号通知,關于Linux裝置通路機制——阻塞,非阻塞io,異步通知

對于字元型驅動中經常會碰到應用程式和底層硬體資料交換的問題,常用的做法有:

1.阻塞:

通過讀寫函數中内嵌阻塞代碼(信号量,等待隊列)來實作不滿足條件時的睡眠,等到滿足條件了應用程式從睡眠中喚醒,繼續下面的操作。

關于信号量和等待隊列可以參考以前的ppt和之前的文章。

2.非阻塞:

非阻塞就是應用程式即使得不到硬體資料也不會睡眠,而是直接傳回。當然真實的操作不會就這樣傳回推出了,而是先通過輪詢的方式,也就是上一篇文章的select,(雖然這裡說是非阻塞,但是select本身就有點阻塞的味道,如果加入的檔案描述符都不滿足,select會休眠,一旦下層驅動有變化則會通知select再次調用poll函數,這其實是一種改良的輪詢,非常好的一種機制),通過select來實作輪詢的方式,隻要select能通過則表明可以進行讀或者寫了。

3.異步IO

一旦實作異步IO,底層的一旦有資料變化,就會像産生一個中斷一樣通知上層應用.異步通知的意思是:一旦裝置就緒,則主動通知應用程式,這樣應用程式就根本不需要查詢裝置狀态(非常像中斷吧!)

1.2之前都有提到怎麼寫,是以這裡不多說了,談一下怎麼用異步io。

(1)首先在結構體中添加異步結構體指針:

struct fifodev

{

unsigned char buf[MAX_FIFO_BUF];   //按鍵緩沖區

unsigned int current_len;

wait_queue_head_t r_wait;            //等待隊列

wait_queue_head_t w_wait;

struct cdev cdev;

……

struct fasync_struct *async_queue;

} ;

(2)驅動裝置的fasync()函數

static int sep4020_fifo_fasync(int fd, struct file *flip, int mode)

{

return fasync_helper(fd, flip, mode, &fifo_dev->async_queue);

}

(3)在相應的資源可以獲得的地方添加釋放sigio信号,比如在中斷,讀函數,寫函數中,這裡舉例寫函數中:

static ssize_t sep4020_fifo_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)

{……

wake_up_interruptible(&fifo_dev->r_wait);

if(fifo_dev->async_queue)

kill_fasync(&fifo_dev->async_queu,SIGIO, POLL_IN);//這裡是寫函數釋放,當然是表示可以讀了,同理在讀中釋放就要用poll_out了

out:

return ret;

}

(4)在檔案關閉時,将檔案從異步通知隊列中删除。

static int sep4020_fifo_release(struct inode *inode, struct file *filp)

{

//調用之前寫的fasync函數

sep4020_fifo_fasync(-1,flip, 0);

return 0;

}

下面是應用程式需要做的工作:

#include ……

void input_handler(int signum)

{

相應的處理,比如說程式中異步讀通知的話,這裡就可以實作讀的操作

}

main()

{

int fd, oflags;

fd = open("dev/fifo, O_RDWR, S_IRUSR | S_IWUSR");

if(fd == -1)

{

printf("wrong\r\n");

exit(-1);

}

signal(SIGIO,input_handler);//讓input_handler()處理SIGIO信号

fcntl(fd, F_SETOWN, getpid());//第二個參數的定義是設定異步io所有權,是以這句話的意思是設定本程序為fd檔案的所有者

oflags = fcntl(fd, F_GETFL);//獲得檔案狀态标志

fcntl(fd, F_SETFL, oflags | FASYNC);//使用者程式必須對通路的裝置檔案設定FASYNC标志。F_SETFL指令表示設定檔案狀态标志位.

while(1)

{

sleep(100);

}

}

以下是網上摘錄的:

驅動程式的實作需要:

當使用者程式操作時,從核心驅動的角度來看:

(1)當使用者程式調用F_SETOWN指令時(通過fnctl系統調用),所設定的值儲存在了驅動程式中的filp->f_owner結構體。

(2)當使用者程式調用F_SETFL指令設定FASYNC标志時,驅動中的fasync方法相應的被調用。fasync方法的實作樣例如下:

static int scull_p_fasync(int fd, struct file *filp, int mode)

{

struct scull_pipe *dev = filp->private_data;

return fasync_helper(fd, filp, mode, &dev->async_queue);//

}

(3)當裝置驅動準備好通路資料後,向所有注冊異步通知的程序發送SIGIO信号。它的實作樣例如下:

if (dev->async_queue)

kill_fasync(&dev->async_queue, SIGIO, POLL_IN);//POLL_IN指裝置此時準備好供使用者可讀的資料;如果要對裝置可寫,這裡應該用POLL_OUT

(4)當裝置檔案被關閉時,應當将裝置檔案從核心維護的活動異步讀清單中删掉。它的實作樣例如下:

scull_p_fasync(-1, filp, 0);