Linux 異步通知(信號)原理及架構

異步通知

異步通知的意思是:一旦設備就緒,則主動通知應用程序進行訪問。這樣,使用無阻塞IO的應用程序無需輪詢的查詢設備是否可訪問,達到減小CPU消耗的目的。類似於硬件上的“中斷”的概念,比較準確的稱謂是“信號驅動的異步IO”。
信號:是在軟件層次上對中斷機制的模擬。
在原理上,進程接收到一個信號(軟件層)<==>(硬件層)處理器接收到一箇中斷。
假設假設recvfrom函數是一個系統調用:信號驅動的異步IO
阻塞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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章