linux多进程通过中断实现,Linux驱动中断上下文中会发生什么结果实验测试
一、前言
每一個(gè)Linux驅(qū)動(dòng)工程師都知道這樣一個(gè)準(zhǔn)則:在中斷上下文中不能睡眠。但是為什么interrupt context中不能調(diào)用導(dǎo)致睡眠的kernel API呢?如果驅(qū)動(dòng)這么做會(huì)導(dǎo)致什么樣的后果呢?這就是本文探討的主題。為了理解這個(gè)主題,我們?cè)O(shè)計(jì)了一些非常簡(jiǎn)單的驅(qū)動(dòng)程序和用戶空間的程序,實(shí)際做實(shí)驗(yàn)觀察實(shí)驗(yàn)效果,最后給出了結(jié)果和分析。
BTW,本文的實(shí)驗(yàn)在X86 64bit + 標(biāo)準(zhǔn)4.4內(nèi)核上完成。
二、測(cè)試程序
1、cst驅(qū)動(dòng)模塊
我們首先準(zhǔn)備一個(gè)能夠在中斷上下文中睡眠的驅(qū)動(dòng)程序,在這里我稱之Context schedule test module(后文簡(jiǎn)稱cst模塊)。這個(gè)驅(qū)動(dòng)程序類似潛伏在內(nèi)核中的“搗蛋鬼”,每隔1秒隨機(jī)命中一個(gè)進(jìn)程,然后引發(fā)調(diào)度。首先準(zhǔn)備一個(gè)Makefile,代碼如下:
按理說(shuō)代碼中的xxxx應(yīng)該是我的名字,如果你愿意在你的環(huán)境中測(cè)試,可以修改成你的名字,當(dāng)然,最重要的是需要某個(gè)版本的內(nèi)核代碼。在內(nèi)核升級(jí)文檔中,我已經(jīng)編譯了/home/xxxx/work/linux-4.4.6目錄下的內(nèi)核,并把我的計(jì)算機(jī)升級(jí)到4.4.6的內(nèi)核上,如果你愿意可以按照那份文檔進(jìn)行升級(jí),否則可能會(huì)有一些版本問(wèn)題需要處理。除了Makefile之外,還需要一個(gè)Kbuild文件:
obj-m := cst.o
當(dāng)然,最重要的是cst模塊的源代碼:
代碼非常的簡(jiǎn)單,無(wú)需多說(shuō),直接make就可以編譯得到cst.ko的內(nèi)核模塊了。
2、用戶空間測(cè)試程序
為了更方便的測(cè)試,我們需要準(zhǔn)備一個(gè)“受害者”,代碼如下:
這段代碼也很簡(jiǎn)單:不斷的產(chǎn)生一個(gè)隨機(jī)數(shù),并運(yùn)算其平方根,在使得的時(shí)候,向用戶輸出一些字符,表明自己的狀態(tài)。當(dāng)程序執(zhí)行起來(lái)的時(shí)候,大部分時(shí)間在用戶態(tài)(運(yùn)算),偶爾進(jìn)入內(nèi)核態(tài)(printf)。這個(gè)進(jìn)程并不知道在內(nèi)核態(tài)有一個(gè)cst的模塊,每隔一秒就發(fā)射一只休眠之箭,可能命中用戶態(tài),也可能命中內(nèi)核態(tài),看運(yùn)氣吧,但是無(wú)論怎樣,該進(jìn)程被射中之后都會(huì)進(jìn)入睡眠。
三、執(zhí)行測(cè)試并觀察結(jié)果
1、先把用戶空間的測(cè)試程序跑起來(lái)
要想測(cè)試導(dǎo)彈(呵呵~~我們的cst模塊就是一個(gè)搗蛋) 的性能,必須要有靶機(jī)或者靶艦。當(dāng)然也可以不用“靶機(jī)”程序,只不過(guò)搗蛋鬼cst總是命中swapper進(jìn)程,有點(diǎn)無(wú)趣,因此這里需要把我們用戶空間的那個(gè)測(cè)試程序跑起來(lái),讓CPU先活躍起來(lái)。
需要注意的是,在多核cpu上,我們需要多跑幾個(gè)“靶機(jī)”進(jìn)程才能讓系統(tǒng)不會(huì)always進(jìn)入idle狀態(tài)。例如我的T450是4核cpu,因此我需要運(yùn)行4個(gè)靶機(jī)程序才能讓系統(tǒng)中的4個(gè)cpu core都燥起來(lái)。可以通過(guò)下面的命令確認(rèn):
ps –eo comm,psr | grep cst
BTW,靶機(jī)程序是cst_test。通過(guò)上面的命令,可以看到系統(tǒng)中運(yùn)行了四個(gè)cst_test進(jìn)程,分別在4個(gè)cpu上。
2、插入內(nèi)核模塊
靶機(jī)已經(jīng)就緒,是時(shí)候發(fā)射搗蛋了,命令如下:
sudo insmod ./cst.ko
一旦插入了cst內(nèi)核模塊,搗蛋鬼就開(kāi)始運(yùn)作了,每隔1秒鐘發(fā)射一次,總有一個(gè)倒霉蛋被命中,被調(diào)度。當(dāng)然,在我們的測(cè)試中,一般總是cst_test這個(gè)進(jìn)程被命中。
3、觀察結(jié)果
一切準(zhǔn)備就緒,是時(shí)候搬個(gè)小板凳坐下來(lái)看好戲了。當(dāng)然,我們需要一個(gè)觀察的工具,輸入如下命令:
sudo tail –f /var/log/messages
在上面的cst模塊中,輸出并沒(méi)有直接到控制臺(tái),因此我們需要通過(guò)內(nèi)核日志來(lái)看看cst的運(yùn)行情況。
四、結(jié)果和分析
1、結(jié)果
很奇怪,一切都是正常的,系統(tǒng)沒(méi)有死,cst模塊也運(yùn)行正常,cst_test進(jìn)程也始終保持alive狀態(tài),不斷的運(yùn)行在無(wú)聊的平方根、打印著無(wú)聊的字符串。唯一異常的是日志,每隔1秒鐘就會(huì)dump stack一次。
2、分析
當(dāng)cst模塊命中cst_test進(jìn)程,無(wú)論是userspace還是kernel space,都會(huì)在內(nèi)核棧上保存中斷發(fā)生那一點(diǎn)的上下文,唯一不同是如果發(fā)生在userspace,那么發(fā)生中斷那一刻,內(nèi)核棧是空的,而如果在kernel space,內(nèi)核棧上已經(jīng)有了cst_test通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核的現(xiàn)場(chǎng)以及該系統(tǒng)調(diào)用各個(gè)函數(shù)的stack frame,當(dāng)中斷發(fā)生的時(shí)候,在當(dāng)前內(nèi)核棧的棧頂上繼續(xù)壓入了中斷現(xiàn)場(chǎng),之后就是中斷處理的各個(gè)函數(shù)的stack frame,最后是cst_timer_handler的stack frame,由于調(diào)用了schedule函數(shù),cst_test進(jìn)程的現(xiàn)場(chǎng)被繼續(xù)壓入內(nèi)核棧,調(diào)度器決定下一個(gè)調(diào)度的進(jìn)程。
cst_test進(jìn)程雖然被調(diào)度了,但是仍然在runqueue中,調(diào)度器一定會(huì)在適當(dāng)?shù)臅r(shí)機(jī)調(diào)度cst_test進(jìn)程重新進(jìn)入執(zhí)行態(tài),這時(shí)候恢復(fù)其執(zhí)行就OK了,cpu執(zhí)行cst_TImer_handler函數(shù)schedule之后的代碼,繼續(xù)未完的中斷上下文的執(zhí)行,然后從內(nèi)核?;謴?fù)中斷現(xiàn)場(chǎng),一切又按照原路返回了。
當(dāng)然,這里的測(cè)試看起來(lái)一切OK,但這并不是說(shuō)可以自由的在中斷上下文中調(diào)用導(dǎo)致睡眠的內(nèi)核API,因?yàn)槲覀冞@里給出了一個(gè)簡(jiǎn)單的例子,實(shí)際上也有可能導(dǎo)致系統(tǒng)死鎖。例如在內(nèi)核態(tài)持有鎖的時(shí)候被中斷,然后發(fā)生調(diào)度。有興趣的同學(xué)可以自己修改上面的代碼實(shí)驗(yàn)這種情況。
3、why
最后還是回到這個(gè)具體的技術(shù)問(wèn)題:為什么interrupt context中不能調(diào)用導(dǎo)致睡眠的kernel API?
我的看法是這樣的:調(diào)度器是每一個(gè)OS的必備組件,在編碼階段之前,我們往往要制定下我們的設(shè)計(jì)概念。對(duì)于Linux 調(diào)度器,它的目標(biāo)就是調(diào)度一個(gè)線程,一個(gè)線程就是調(diào)度實(shí)體(暫不考慮group sched)。中斷上下文是不是調(diào)度實(shí)體呢?當(dāng)然不是,它沒(méi)有專屬的task struct,內(nèi)核無(wú)從調(diào)度。這是調(diào)度器設(shè)計(jì)者的決定,這樣的決定讓調(diào)度器設(shè)計(jì)起來(lái)簡(jiǎn)潔而美麗。
基于上面的設(shè)計(jì)概念,中斷上下文(hard irq和sofTIrq context)并不參與調(diào)度(暫不考慮中斷線程化),它們是異步事件的處理機(jī)制,目標(biāo)就是盡快完成處理,返回現(xiàn)場(chǎng)。因此,所有中斷上下文的優(yōu)先級(jí)都是高于進(jìn)程上下文的,也就是說(shuō),對(duì)于用戶進(jìn)程(無(wú)論內(nèi)核態(tài)還是用戶態(tài))或者內(nèi)核線程,除非disable了CPU的本地中斷,否則一旦中斷發(fā)生,它們是沒(méi)有任何能力阻擋中斷上下文搶占當(dāng)前進(jìn)程上下文的執(zhí)行的。
因此,Linux kernel的設(shè)計(jì)者制定了規(guī)則:
1、中斷上下文不是調(diào)度實(shí)體
2、中斷上下文的優(yōu)先級(jí)高于進(jìn)程上下文
而在中斷上下文中調(diào)度毫無(wú)疑問(wèn)會(huì)打破規(guī)則,因此不能在硬中斷、軟中斷環(huán)境中調(diào)用阻塞函數(shù)。不過(guò),在linux調(diào)度器的具體實(shí)現(xiàn)的時(shí)候,檢測(cè)到了在中斷上下文中調(diào)度schedule函數(shù)也并沒(méi)有強(qiáng)制linux進(jìn)入panic,可能是linux的開(kāi)發(fā)者認(rèn)為一個(gè)好的內(nèi)核調(diào)度器無(wú)論如何也盡自己最大的努力讓系統(tǒng)運(yùn)行下去吧。但是,在廠商自己提供的內(nèi)核中,往往修改調(diào)度器行為,在中斷上下文中檢測(cè)到調(diào)度就直接panic了,對(duì)于內(nèi)核開(kāi)發(fā)者而言,這樣更好,可以盡早的發(fā)現(xiàn)問(wèn)題。
總結(jié)
以上是生活随笔為你收集整理的linux多进程通过中断实现,Linux驱动中断上下文中会发生什么结果实验测试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 摩尔庄园苹果种子怎么获得
- 下一篇: 田园犬多少钱一只啊?