Linux異步通知

Linux異步通知

-v0.1 2019.5.2 Sherlock init

本文是Linux異步通知的一個學習筆記,讀者可以參看此文快速獲得相關的知識。

Linux系統中有很多內核和用戶態程序通知的機制,比如event fd, netlink和異步通知。
通過這些機制內核可以主動給用戶態程序發送消息。本文討論異步通知的基本用法。

利用異步通知機制可以實現從內核中向設備文件綁定的進程發送特定信號。把異步通知用
起來需要內核中設備驅動和用戶態程序的配合。在這裏有一個實例程序可以直接下載,
然後在虛擬機裏運行: https://github.com/wangzhou/tests/tree/master/fasync_test

如這個示例中的驅動代碼,爲了支持異步通知,我們需要實現設備文件操作中的.fasync
回調,實現的方式也很簡單,就是調用下標準的fasync_helper函數向內核註冊一個
fasync_struct, 其中最後一個參數是一個fasync_struct結構的二維指針,一般設備驅動裏
應該定義一個特定file相關的fasync_struct的指針,用於保存內核分配的fasync_struct
的地址, 其實前面所謂註冊一個fasync_struct, 就是請求內核分配一個fasync_struct的
結構,然後返回該結構的地址給設備驅動。

int test_fasync(int fd, struct file *file, int mode)
{
	return fasync_helper(fd, file, mode, &async_queue);
}

當內核要發送信號給用戶態進程的時候需要調用下kill_fasync函數, 該函數會向
fasync_struct對應的fd所綁定的進程發送一個SIGIO信號。這裏的SIGIO也可以換成其他的
信號,但是一般用SIGIO信號發異步通知。

void test_trigger_sigio(struct timer_list *tm)
{
	kill_fasync(&async_queue, SIGIO, POLL_IN);
}

可以看到在測試程序裏,我們爲了方便測試,其實是起了一個內核定時器,在測試內核模塊
加載10s後,向fd綁定的用戶態進程發送一個SIGIO信號。(注意,這個測試程序是在主線
內核5.1上調試的,主線內核在4.15更新了內核定時器的API,這裏用的是新內核定時器API)

在用戶態程序中,需要設置設備fd,使其和當前進程綁定,還要使其可以接受fasync信號。

ret = fcntl(fd, F_SETOWN, getpid());
if (ret == -1) {
	printf("u fasync: fail to bind process\n");
	return -2;
}

fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
if (ret == -1) {
	printf("u fasync: fail to set fasync\n");
	return -3;
}

這樣設置後在內核調用kill_fasync,內核就知道把信號發給哪個進程。不同shell可能對
於信號設定有所不同,這裏在測試之前先把SIGIO信號unmask,避免SIGIO被shell默認mask
掉,然後子進程繼承父進程的信號設定,也mask住SIGIO的情況。

Linux信號的實現基本邏輯是在進程的signal pending表裏標記其他進程或者內核給自己發
的信號,然後在進程從內核態切回用戶態的時候再去掃描signal pending表以響應信號。
如果用戶態進程一直沒有系統調用,那麼內核態發的SIGIO會不會得不到即使的響應? 另外
是否其他的內核活動也會引起測試進程發生內核態向用戶態切換的過程,其中一個最可能
的情況就是內核週期性的調度。測試程序中做了一個簡單的測試,用戶態程序在設置好
fd後就進入死循環,而內核設備驅動在加載10s後會給用戶態進程發一個SIGIO信號。測試
的結果是用戶態進程可以在10s左右收到內核發的SIGIO信號。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章