歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> 《Linux4.0設備驅動開發詳解》筆記--第九章:Linux設備驅動中的異步通知與同步I/O

《Linux4.0設備驅動開發詳解》筆記--第九章:Linux設備驅動中的異步通知與同步I/O

日期:2017/3/1 11:50:00   编辑:關於Linux

在設備驅動中使用異步通知可以使得對設備的訪問可進行時,由驅動主動通知應用程序進行訪問。因此,使用無阻塞I/O的應用程序無需輪詢設備是否可訪問,而阻塞訪問也可以被類似“中斷”的異步通知所取代。異步通知類似於硬件上的“中斷”概念,比較准確的稱謂是“信號驅動的異步I/O”。

9.1 異步通知的概念和作用

異步通知:一旦設備就緒,則主動通知應用程序,該應用程序無需查詢設備狀態 幾種通知方式比較:
阻塞I/O :一直等待設備可訪問後開始訪問 非阻塞I/O:使用poll()查詢設備是否訪問 異步通知 :設備主動通知用戶應用程序 -

9.2 linux異步通知編程

9.2.1 linux信號

作用:linux系統中,異步通知使用信號來實現
SIGHUP     終止進程     終端線路掛斷
SIGINT     終止進程     中斷進程
SIGQUIT   建立CORE文件終止進程,並且生成core文件
SIGILL   建立CORE文件       非法指令
SIGTRAP   建立CORE文件       跟蹤自陷
SIGBUS   建立CORE文件       總線錯誤
SIGSEGV   建立CORE文件       段非法錯誤
SIGFPE   建立CORE文件       浮點異常
SIGIOT   建立CORE文件       執行I/O自陷
SIGKILL   終止進程     殺死進程
SIGPIPE   終止進程     向一個沒有讀進程的管道寫數據
SIGALARM   終止進程     計時器到時
SIGTERM   終止進程     軟件終止信號
SIGSTOP   停止進程     非終端來的停止信號
SIGTSTP   停止進程     終端來的停止信號
SIGCONT   忽略信號     繼續執行一個停止的進程
SIGURG   忽略信號     I/O緊急信號
SIGIO     忽略信號     描述符上可以進行I/O
SIGCHLD   忽略信號     當子進程停止或退出時通知父進程
SIGTTOU   停止進程     後台進程寫終端
SIGTTIN   停止進程     後台進程讀終端
SIGXGPU   終止進程     CPU時限超時
SIGXFSZ   終止進程     文件長度過長
SIGWINCH   忽略信號     窗口大小發生變化
SIGPROF   終止進程     統計分布圖用計時器到時
SIGUSR1   終止進程     用戶定義信號1
SIGUSR2   終止進程     用戶定義信號2
SIGVTALRM 終止進程     虛擬計時器到時

9.2.2 信號的接收

信號捕獲函數signal()
參數:
signum:信號值 handler:針對signum的處理函數
若為SIG_IGN:忽略該信號 若為SIG_DFL:系統默認方式處理 若為用戶自定義函數:信號被捕獲,該函數被執行 返回值
成功:最後一次為信號signum綁定的處理函數的handler值 失敗:返回SIG_ERR sigaction()
作用:改變進程接收到特定信號後的行為 參數
signum:信號值
除SIG_KILL及SIG_STOP以外的一個特定有效的信號 act:指向結構體sigaction的一個實例的指針
在結構體sigaction中,指定了處理信號的函數,若為空則進程會以缺省值的方式處理信號 oldact:保存原來對應的信號的處理函數,可設為NULL
int sigaction(int signo,const struct sigaction *restrict act, struct sigaction *restrict oact);
實例:使用信號實現異步通知
在用戶空間處理設備釋放信號的准備工作
通過F_SETOWN IO控制命令設置設備文件的擁有者為本進程,以使信號被本進程捕獲 通過F_SETFL IO控制命令設置設備文件以支持FASYNC,及異步通知模式 通過signal()函數連接信號和信號處理函數
//啟動信號機制

void sigterm_handler(int sigo)
{

char data[MAX_LEN];
int len;
len = read(STDIN_FILENO,&data,MAX_LEN);
data[len] = 0;
printf("Input available:%s\n",data);
exit(0);

}

int main(void)
{

int oflags;
//啟動信號驅動機制

signal(SIGIO,sigterm_handler);
fcntl(STDIN_FILENO,F_SETOWN,getpid());
oflags = fcntl(STDIN_FILENO,F_GETFL);
fctcl(STDIN_FILENO,F_SETFL,oflags | FASYNC);
//建立一個死循環,防止程序結束

whlie(1);

return 0;

}

9.2.3 信號的釋放 (在設備驅動端釋放信號)

為了是設備支持異步通知機制,驅動程序中涉及以下3項工作
支持F_SETOWN命令,能在這個控制命令處理中設置filp->f_owner為對應的進程ID。不過此項工作已由內核完成,設備驅動無須處理 支持F_SETFL命令處理,每當FASYNC標志改變時,驅動函數中的fasync()函數得以執行。因此,驅動中應該實現fasync()函數 在設備資源中可獲得,調用kill_fasync()函數激發相應的信號 - 設備驅動中異步通知編程:
處理FASYNC標志變更函數:fasync_helper() 釋放信號的函數:kill_fasync()
int fasync_helper(int fd,struct file *filp,int mode,struct fasync_struct **fa);

 void kill_fasync(struct fasync_struct **fa,int sig,int band);
將fasync_struct結構體指針放到設備結構體中是最佳的選擇
//異步通知的設備結構體模板
struct xxx_dev{
    struct cdev cdev;
    ...
    struct fasync_struct *async_queue;//異步結構體指針
};
在設備驅動中的fasync()函數中,只需簡單地將該函數的3個參數以及fasync_struct結構體指針的指針作為第四個參數傳入fasync_helper()函數就可以了,模板如下
static int xxx_fasync(int fd,struct file *filp, int mode)
{
  struct xxx_dev *dev = filp->private_data;
  return fasync_helper(fd, filp, mode, &dev->async_queue);
}
在設備資源可獲得時應該調用kill_fasync()函數釋放SIGIO信號,可讀時第三個參數為POLL_IN,可寫時第三個參數為POLL_OUT,模板如下
static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos)

{
    struct xxx_dev *dev = filp->private_data;
    ...
    //產生異步讀信息
    if(dev->async_queue)
    kill_fasync(&dev->async_queue,GIGIO,POLL_IN);
    ...
}
最後在文件關閉時,要將文件從異步通知列表中刪除
int xxx_release(struct inode *inode,struct file *filp)

{
    //將文件從異步通知列表中刪除
    xxx_fasync(-1,filp,0);
    ...
    return 0;
}

9.4 linux異步I/O

9.4.1 AIO概念與GNU C庫 AIO

9.4.1.1 AIO概念

同步I/O:linux系統中最常用的輸入輸出(I/O)模型是同步I/O,在這個模型中,當請求發出後,應用程序就會阻塞,知道請求滿足

異步I/O:I/O請求可能需要與其它進程產生交疊

Linux 系統中最常用的輸入/輸出(I/O)模型是同步 I/O

在這個模型中,當請求發出之後,應用程序就會阻塞,直到請求滿足為止 調用應用程序在等待 I/O 請求完成時不需要使用任何中央處理單元(CPU) 在某些情況下,I/O 請求可能需要與其他進程產生交疊,可移植操作系統接口(POSIX)異步 I/O(AIO)應用程序接口(API)就提供了這種功能

9.4.1.1 AIO系列API:

aio_read–異步讀
作用:請求對一個有效的文件描述符進行異步讀寫操作
請求進行排隊之後會立即返回 這個文件描述符可以表示一個文件、套接字,甚至管道 參數aiocb:結構體包含了傳輸的所有信息,以及為AIO操作准備的用戶空間緩存區 返回值
成功:返回0 失敗:返回-1,並設置errno的值
int aio_read( struct aiocb *aiocbp );
aio_write–異步寫
作用:請求一個異步寫操作
請求進行排隊之後會立即返回 這個文件描述符可以表示一個文件、套接字,甚至管道 參數aiocb:結構體包含了傳輸的所有信息,以及為AIO操作准備的用戶空間緩存區 返回值
成功:返回0 失敗:返回-1,並設置errno的值
int aio_write( struct aiocb *aiocbp );
aio_error
作用:確定請求的狀態 參數aiocb:結構體包含了傳輸的所有信息,以及為AIO操作准備的用戶空間緩存區 返回值
EINPROGRESS:說明請求尚未完成 ECANCELED:說明請求被應用程序取消 失敗:返回-1,並設置errno的值
int aio_error( struct aiocb *aiocbp );
aio_return–獲得異步操作的返回值
異步 I/O 和標准塊 I/O 之間的另外一個區別是不能立即訪問這個函數的返回狀態,因為並沒有阻塞在 read()調用上 在標准的 read()調用中,返回狀態是在該函數返回時提供的。但是在異步 I/O 中,我們要使用 aio_return()函數 只有在 aio_error()調用確定請求已經完成(可能成功,也可能發生了錯誤)之後,才會調用這個函數 參數aiocb:結構體包含了傳輸的所有信息,以及為AIO操作准備的用戶空間緩存區 返回值
成功:返回所傳輸的字節數 失敗:返回-1
ssize_t aio_return( struct aiocb *aiocbp );
aio_suspend–掛起異步操作,直到異步請求完成為止
作用:掛起(或阻塞)調用進程,直到異步請求完成為止,調用者提供了一個 aiocb 引用列表,其中任何一個完成都會導致 aio_suspend()返回
int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout );
aio_cancel–取消異步請求
作用:允許用戶取消對某個文件描述符執行的一個或所有 I/O 請求 要求:
如果要取消一個請求,用戶需提供文件描述符和 aiocb 引用
函數返回AIO_CANCELED:請求被成功取消 函數返回AIO_NOTCANCELED:請求完成 如果要取消對某個給定文件描述符的所有請求,用戶需要提供這個文件的描述符以及一個對 aiocbp 的 NULL 引用
函數返回AIO_CANCELED:表明所有的請求都取消了 函數返回AIO_NOTCANCELED:表明至少有一個請求沒有被取消 函數返回AIO_ALLDONE:表明沒有一個請求可以被取消 使用 aio_error()來驗證每個 AIO 請求
aio_error()返回-1並且設置了errno被設置為ECANCELED:表明某個請求已經被取消了
int aio_cancel( int fd, struct aiocb *aiocbp );
lio_listio–同時發起多個傳輸(一次系統調用可以啟動大量的I/O操作)
作用:這個函數非常重要,它使得用戶可以在一個系統調用(一次內核上下文切換)中啟動大量的 I/O 操作 參數
mode:可以是 LIO_WAIT 或 LIO_NOWAIT
LIO_WAIT 會阻塞這個調用,直到所有的 I/O 都完成為止 在操作進行排隊之後,LIO_NOWAIT 就會返回 list :是一個 aiocb 引用的列表,最大元素的個數是由 nent 定義的
如果 list 的元素為 NULL,lio_listio()會將其忽略。
int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );

9.4.2 Linux內核AIO與libaio

linux AIO也可以由內核空間實現,異步I/O是linux2.6以後版本內核的標准特性 對於塊設備,AIO可以一次性發出大量的read/write調用並且通過通用塊層的I/O調度來獲得更好的性能,用戶也可以減少過多的同步負載 對網絡設備而言,在socket層面上,也可以使用AIO,讓CPU和網卡的收發充分交疊以改善吞吐性能 用戶空間中一般要結合libaio來進行內核AIO的系統調用
io_setup( )

//Initializes an asynchronous context for the current process

io_submit( )

//Submits one or more asynchronous I/O operations

io_getevents( )

//Gets the completion status of some outstanding asynchronous I/O operations

io_cancel( )

//Cancels an outstanding I/O operation

io_destroy( )

//Removes an asynchronous context for the current process

9.4.3 AIO與設備驅動

用戶空間調用io_submit()之後,對應於用戶傳遞的每個iocb結構,內核會生成一個與之對應的kiocb結構

通過is_sync_kiocb判斷某kiocb是否為同步I/O請求

如果是返回真,表示為異步I/O請求

字符設備:必須明確應支持AIO(極少數是異步I/O操作)

字符設備驅動程序中file_operations 包含 3 個與 AIO 相關的成員函數,
ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count, loff_t offset);

ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count, loff_t offset);

int (*aio_fsync) (struct kiocb *iocb, int datasync);
塊設備和網絡設備:本身是異步的
Copyright © Linux教程網 All Rights Reserved