對于字元型驅動中經常會碰到應用程式和底層硬體資料交換的問題,常用的做法有:
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);