異步通知
異步通知的意思是:一旦設備就緒,則主動通知應用程序進行訪問。這樣,使用無阻塞IO的應用程序無需輪詢的查詢設備是否可訪問,達到減小CPU消耗的目的。類似於硬件上的“中斷”的概念,比較準確的稱謂是“信號驅動的異步IO”。
信號:是在軟件層次上對中斷機制的模擬。
在原理上,進程接收到一個信號(軟件層)<==>(硬件層)處理器接收到一箇中斷。
假設假設recvfrom函數是一個系統調用:
阻塞IO,結合poll()的非阻塞IO及異步通知的區別
Linux異步通知編程
Linux應用層
Linux中可用的信號及含義
信號被捕獲時,有相應的信號處理函數進行處理<==>當中斷產生時,有相應的中斷處理函數進行處理。
信號的接收
在用戶程序中,使用signal()函數來設置對應信號的處理函數
原型:
void (*signal(int signum, void (*handler))(int)))(int);
可以分解爲
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
含義:
當接收到signum信號時,執行handler函數指針指向的信號處理函數
爲了在用戶空間中處理一個設備釋放的信號,需完成一下幾項工作:
(1)通過IO控制命令(F_SETOWN)設置設備文件的擁有者爲本進程,這樣從設備驅動發出的信號才能被本進程接收
(2)通過IO控制命令(F_SETFL)設置設備文件支持FASYNC(異步通知),即調用驅動層的xxx_fasync()
(3)通過signal()函數連接信號和信號處理函數
Linux驅動層
爲了使設備支持異步通知,驅動程序需要做以下幾項工作:
(1) 支持F_SETOWN命令,能在這個控制命令處理中設置filp->fowner爲對應進程ID。
(2) 支持F_SETFL命令的處理,每當FASYNC標誌改變時,驅動程序中的fasync()函數將得到執行。
(3) 在設備資源可獲得時,調用kill_fasync()函數激發相應的信號
處理FASYNC標誌變更的函數
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fapp)
{
if(!on)
{
/* 將文件從異步通知中移除 */
return fasync_remove_entry(filp, fapp);
}
return fasync_add_entry(fd, filp, fapp);
--> fasync_insert_entry(fd, filp, fapp, new);
/* fa_fd指向應層打開此驅動時得到的FD.這也就是後面發送信號時能找到程號的原因。*/
--> new->fa_fd = fd;
rcu_assign_pointer(*fapp, new);
--> smp_store_release (&p, RCU_INITIALZER(V))
--> WRITE_OWCE(*p, v)
*p = v
}
通知應用程序資源可用的函數==>其實就是內核給應用程序發信號
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
...
kill_fasync_rcu(rcu_dereference(*fp), sig, band);
--> fown = &fa->fa_file->f_owner;
/* 內核想應用程序發送信號,通知應用程序資源可用 */
send_sigio(fown, fa->fa_fd, band);
}
支持異步通知的設備驅動模板
struct xxx_dev {
struct cdev cdev;
...
struct fasync_struct *async_queue;
}
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);
}
static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct xxx_dev *dev = filp->private_data;
...
if(dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
static int xxx_release(struct inode *inode, struct file *filp)
{
/* 將文件從異步通知列表中刪除 */
xxx_fasync(-1, filp, 0);
...
}
static const struct file_operation xxx_fops = {
...
.fasync = xxx_fasync,
};
從內核態分析異步通知
應用程序直接調用fcntl
SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
--> do_fcntl(fd, cmd, arg, f.file)
--> switch(cmd)
--> case F_SETOWN:
--> f_setown(filp, arg, 1)
--> __f_setown(filp, pid, type, force)
--> f_modown(filp, pid, type, force)
-->filp->f_owner.pid = get_pid(pid);//綁定進程號
--> case F_SETFL:
--> setl(fd, filp, arg)
--> filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0)