Linux驅動中的異步函數(aio_read和aio_write)
我們可以在signal_handler使用了read和write函數處理設備文件的讀寫操作。然而這兩個函數可以分別用aio_read和aio_write代替。在本節將重新改造signal驅動,使用aio_read和aio_write函數來處理設備文件的讀寫操作,新的Linux驅動源代碼文件是aio_signal.c。這個文件和13.1.3節編寫的signal.c文件的內容基本相同,只是添加了如下兩個函數。
// 設備文件的aio_read函數
static ssize_t signal_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long niov, loff_t pos)
{
return signal_read(iocb->ki_filp, (char*)iov->iov_base,iocb->ki_nbytes, &pos);;
}
// 設備文件的aio_write函數
static ssize_t signal_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long niov, loff_t pos)
{
return signal_write(iocb->ki_filp, (char*)iov->iov_base,iocb->ki_nbytes, &pos);;
}
這兩個函數的參數完全相同。其中kiocb結構體類似於aiocb結構體,aiocb結構體中很多數據(如aio_nbytes、aio_offset等)在kiocb中都可以通過對應的成員變量獲取。而iov可以獲取用於讀寫數據的緩存區,也就是aiocb.aio_buf指向的內存區域。除此之外,還可以通過iovec.iov_len獲取緩存區的字節長度。pos參數和kiocb.ki_pos都可以獲取讀寫數據的偏移量,使用哪個都可以。只是pos參數和read、write函數不同。在aio_read和aio_write函數中pos參數變成了實際的值,而不是loff_t指針類型參數。也就是不能在aio_read和aio_write函數中修改pos參數的值(修改了也沒用)。
從上面實現的aio_read和aio_write函數可以看出,在這兩個函數中並未實現具體的代碼,而是調用了已經實現的signal_read和signal_write函數。這就會引出一個問題,如果file_operations結構體同時初始化異步(aio_read和aio_write)和同步(read和write)函數,那么系統會使用同步還是異步讀寫方式處理對設備文件的讀寫操作呢?
如果同時指定同步和異步讀寫函數,系統會使用read和write函數來處理設備文件的讀寫操作。如果想使用aio_read和aio_write函數來處理設備文件的讀寫操作,就不能指定read和write函數。因此,本節的示例需要按如下方式修改file_operations結構體初始化的代碼。
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.poll = signal_poll,
.fasync = signal_fasync,
.aio_read=signal_aio_read,
.aio_write=signal_aio_write
};
現在Linux驅動程序已經修改完畢,最后執行build.sh腳本文件編譯和安裝本節實現的Linux驅動。為了和13.1.3節實現的signal驅動區分開。本節實現的Linux驅動建立的設備文件名是aio_signal。
注意:file_operations結構體的aio_read、aio_write和read、write都能處理同步和異步I/O操作。只是aio_read和aio_write函數可以獲取更多的異步I/O的數據,並可以更好地控制異步I/O。雖然大多數字符設備都不使用aio_read和aio_write函數(只有一些塊設備,如磁帶機,為了提高讀寫效率會使用它們),但如果想對AIO有更多的控制,也可以考慮使用aio_read和aio_write函數,盡管這兩個函數看起來比read和write函數復雜一些。