文獻標識碼: A
文章編號: 0258-7998(2010)07-0137-03
Linux以其功能強大、源代碼開放,、支持多種硬件平臺,、模塊化設(shè)計方案以及豐富的開發(fā)工具支持等特點廣泛應(yīng)用在嵌入式系統(tǒng)領(lǐng)域。作為嵌入式產(chǎn)品的操作系統(tǒng)平臺,,具有較好的實時性,、系統(tǒng)可靠性、任務(wù)處理隨機性是系統(tǒng)追求的目標,目前商業(yè)嵌入式操作系統(tǒng)實時性能可以滿足嵌入式領(lǐng)域的需求,,但由于其價格昂貴,,應(yīng)用受到了限制[1]。而嵌入式Linux以其非常低廉的價格,,可以大大地降低成本,,逐漸成為嵌入式操作系統(tǒng)的首選。但由于其在實時應(yīng)用領(lǐng)域的技術(shù)障礙,,要應(yīng)用在嵌入式領(lǐng)域,還必須對Linux內(nèi)核作必要的改進,。本文以S3C2410+Linux作為移動機器人操作平臺,為了提高機器人任務(wù)處理的實時性,針對影響Linux OS實時性能的若干方面進行研究,,并利用相應(yīng)的解決方法基于標準Linux2.6內(nèi)核加以實現(xiàn),,最后通過測試,驗證了此改進方法的效果,。
1 Linux內(nèi)核實時性分析
1.1 Linux內(nèi)核制約實時性的因素
衡量操作系統(tǒng)實時性的指標主要有中斷延遲和搶占延遲,。嵌入式系統(tǒng)中很多實時任務(wù)是靠中斷驅(qū)動的,中斷事件必須在限定的時限內(nèi)處理,,否則將產(chǎn)生災(zāi)難性的后果,。大多數(shù)實時系統(tǒng)都是處理一些周期性的或非周期性的重復(fù)事件,事件產(chǎn)生的頻度就確定了任務(wù)的執(zhí)行時限,,因此每次事件發(fā)生時,,相應(yīng)的處理任務(wù)必須及時響應(yīng)處理,否則將無法滿足時限[2],。搶占延遲就反映了系統(tǒng)的響應(yīng)及時程度,。針對Linux內(nèi)核,中斷關(guān)閉及中斷優(yōu)先級執(zhí)行機制,、內(nèi)核不可搶占性,、自旋鎖(spinlock)及大內(nèi)核鎖及一些O(n)的任務(wù)調(diào)度算法影響了系統(tǒng)的實時性能。
1.2 現(xiàn)存增強Linux內(nèi)核實時性的技術(shù)
多年來,Linux實時性改進技術(shù)的發(fā)展主要有兩種技術(shù)方案:(1)直接修改Linux內(nèi)核,。針對內(nèi)核數(shù)據(jù)結(jié)構(gòu),、調(diào)度函數(shù)、中斷方式進行改動,,重新設(shè)計一個由優(yōu)先級驅(qū)動的實時調(diào)度器,替換原有Linux內(nèi)核中的進程調(diào)度器sched.c,。這一方案主要是針對中斷機制、任務(wù)調(diào)度算法進行改進的,,較為成功的案例為Kansas大學開發(fā)的Kurt-Linux,。Kurt提高了Linux系統(tǒng)中的實時精度,,將時鐘芯片設(shè)置為單觸發(fā)狀態(tài),。對于實時任務(wù)的調(diào)度,Kurt-Linux采用基于時間的靜態(tài)實時CPU調(diào)度算法,。實時任務(wù)在設(shè)計階段就需要明確地說明其實時事件要發(fā)生的時間,。這種調(diào)度算法對于那些循環(huán)執(zhí)行的任務(wù)能夠取得較好的調(diào)度效果;(2)在Linux內(nèi)核之外進行實時性擴展,,添加一個實時內(nèi)核。實時內(nèi)核接管硬件所有中斷,,并依據(jù)是否為實時任務(wù)給予響應(yīng),。Fsm Labs公司開發(fā)的RTLinux就是依據(jù)這種策略開發(fā)設(shè)計的[3]。以上論述的兩種技術(shù)方案有其可借鑒之處,,但如果綜合考慮任務(wù)響應(yīng),、內(nèi)核可搶占性、實時調(diào)度策略等都將影響操作系統(tǒng)的實時性能,,因此,,這兩種技術(shù)還不能很好地滿足實時性要求。為了增強嵌入式Linux實時性能,,下面將介紹中斷機制,、內(nèi)核的搶占性以及大內(nèi)核鎖等相關(guān)問題。
2 Linux實時性改進方法
Linux2.4及以前版本內(nèi)核是不可搶占的,在Linux2.6中,,內(nèi)核已經(jīng)可以搶占,,實時性有所增強。但是內(nèi)核中仍然有不可搶占的區(qū)域,,如自旋鎖spinlock保護的臨界區(qū)等,。另外,影響內(nèi)核實時性能的因素還有中斷運行機制,、大內(nèi)核鎖機制以及調(diào)度算法等,。
2.1 中斷運行機制改進
在Linux標準內(nèi)核中,中斷是最高優(yōu)先級的執(zhí)行單元,硬件架構(gòu)決定了硬件中斷到來的時候在該中斷沒有被屏蔽的條件下必須處理,。不管內(nèi)核當時處理什么,,即便是Linux中最高優(yōu)先級的實時進程,只要有中斷發(fā)生,,系統(tǒng)將立即響應(yīng)該事件并執(zhí)行相應(yīng)的中斷處理程序,,這就大大削弱了Linux的實時性能。特別是系統(tǒng)有嚴重的網(wǎng)絡(luò)或I/O負載時,,中斷將非常頻繁,,實時任務(wù)將很難有機會運行,這對于Linux的實時應(yīng)用來說是不可接受的,。Linux采用的關(guān)中斷技術(shù)在關(guān)中斷區(qū)域使相應(yīng)實時任務(wù)得不到響應(yīng),,增加了實時任務(wù)的中斷延遲。Linux實時化后自旋鎖變?yōu)榛コ怄i的技術(shù),,但由于自旋鎖的中斷處理不能及時響應(yīng),,降低了系統(tǒng)的實時性能。因此,借鑒Ingo Molnar實時補丁的實時化方法,,采用中斷線程化技術(shù)改進中斷運行機制,,中斷將作為內(nèi)核線程運行而且賦予不同的實時優(yōu)先級,實時任務(wù)可以有比中斷線程更高的優(yōu)先級,,這樣,,實時任務(wù)就可以作為最高優(yōu)先級的執(zhí)行單元來運行了,即使在嚴重負載下仍有實時性保證,。另一方面,中斷處理線程也可以因為在內(nèi)核同步中得不到鎖而掛載到鎖的等待隊列中,很多關(guān)中斷就不必真正的禁止硬件中斷了,,而是禁止內(nèi)核進程搶占,從而減小了中斷延遲[4],。
在初始化階段,,常規(guī)中斷初始化和中斷線程化的初始化在start_kernel( )函數(shù)中都調(diào)用trap_init( )和init_IRQ( )兩個函數(shù)來初始化irq_desc_t結(jié)構(gòu)體,區(qū)別主要體現(xiàn)在內(nèi)核初始化創(chuàng)建init線程時,,中斷線程化的中斷在init( )函數(shù)中還將調(diào)用init_hardirqs(kernel/irq/manage.c)來為每一個IRQ創(chuàng)建一個內(nèi)核線程,,最高實時優(yōu)先級為50,依次類推直到25,。因此,,任何IRQ線程的最低實時優(yōu)先級為25,具體實現(xiàn)是通過kthread_create函數(shù)創(chuàng)建的,。功能實現(xiàn)等同于如下代碼:
void __init init_hardirqs(void)
{ ……
for (i = 0; i < NR_IRQS; i++) {
//對于每一個中斷建立一個中斷線程
irq_desc_t *desc = irq_desc + i;
if(desc->action && !(desc->status & IRQ_NODELAY))
//有IRQ_NODELAY標志的中斷不允許線程化
desc->thread = kthread_create(do_irqd,
desc, "IRQ %d", irq); //建立線程
……
}
}
static int do_irqd(void * __desc)
//分配中斷線程優(yōu)先級50~25
{ ……
/*Scale irq thread priorities from prio 50 to prio 25 */
param.sched_priority = curr_irq_prio;
if (param.sched_priority > 25)
curr_irq_prio = param.sched_priority - 1;
……
}
在中斷處理階段當中斷發(fā)生時,CPU調(diào)用do_IRQ( )函數(shù)來處理中斷,,do_IRQ( )在做了必要的相關(guān)處理之后調(diào)用_do_IRQ( )。_do_IRQ( )主要功能為判斷該中斷是否已經(jīng)被線程化(核對終端描述符的狀態(tài)字段是否包含IRQ_NODELAY標志),,對于沒有線程化的中斷,,將直接調(diào)用handle_IRQ_event( )函數(shù)來處理。功能實現(xiàn)等同于如下代碼:
fastcall notrace unsigned int __do_IRQ(unsigned int irq,
struct pt_regs *regs)
{ ……
if (redirect_hardirq(desc))
//檢測是否為線程化中斷,,若是則喚醒中斷線程
goto out_no_end;
……
action_ret = handle_IRQ_event(irq, regs, action);
//處理非線程化中斷
……
}
int redirect_hardirq(struct irq_desc *desc)
//檢測irq_desc結(jié)構(gòu)體,,判斷是否線程化
{ ……
if (!hardirq_preemption || (desc->status & IRQ_
NODELAY) || !desc->thread)
return 0;
……
if (desc->thread && desc->thread->state != TASK_
RUNNING)
wake_up_process(desc->thread);
……
}
針對已線程化的情況,調(diào)用wake_up_process( )函數(shù)喚醒中斷處理線程執(zhí)行,,內(nèi)核線程將調(diào)用do_hardirq( )來處理相應(yīng)的中斷,。具體實現(xiàn)是通過handle_IRQ_event( )函數(shù)直接調(diào)用相應(yīng)的中斷處理函數(shù)完成的。對于緊急的中斷(如時鐘中斷),內(nèi)核保持原來的中斷處理方式,,而不為其創(chuàng)建中斷線程,,這樣就保證了緊急中斷的快速響應(yīng)。
2.2 內(nèi)核可搶占性設(shè)計
在Linux標準內(nèi)核中,,因不具有可搶占性和導(dǎo)致較大的延遲,,增加內(nèi)核的可搶占性能,可提高系統(tǒng)的實時任務(wù)處理能力,。當前修改Linux內(nèi)核提高實時性的方法主要有增加搶占點和改造成搶占式內(nèi)核兩種方法,。增加搶占點方法是在內(nèi)核中插入搶占點,,通過檢測搶占點調(diào)度標志來決定是否進行實時任務(wù)的調(diào)度。采用這種方法,,在檢測搶占點標志時大大增加了系統(tǒng)開銷,因此本方案采用直接改造Linux內(nèi)核的方法,,通過修改自旋鎖為互斥鎖來提高內(nèi)核的可搶占性[5],。即借鑒Ingo Molnar的實時補丁的實時化方法,使用mutex互斥鎖來替換spinlock自旋鎖,。使用mutex替換spinlock,,可以讓spinlock可搶占。起初spinlock不可搶占性設(shè)計目的是避免死鎖,,可搶占性設(shè)計可能導(dǎo)致競爭者與保持者的死鎖局面,。中斷處理函數(shù)中也可以使用spinlock,如果spinlock已經(jīng)被某一進程保持,,則中斷處理函數(shù)無法進行,,從而形成死鎖。中斷線程化以后,,中斷線程將掛在等待隊列上并放棄CPU讓別的線程或進程來運行,讓每個spinlock都有一個等待隊列,,該等待隊列按進程或線程優(yōu)先級排隊,如果一個進程或線程競爭的spinlock已經(jīng)被另一個線程保持,它將把自己掛在該spinlock的優(yōu)先級化的等待隊列上,然后發(fā)生調(diào)度把CPU讓給別的進程或線程,。mutex替換spinlock后,,spinlock結(jié)構(gòu)定義如下代碼:
typedef struct {
struct rt_mutex lock; //新的實時互斥鎖
unsigned int break_lock;
} spinlock_t;
其中struct rt_mutex結(jié)構(gòu)如下:
struct rt_mutex {
raw_spinlock_t wait_lock;
struct plist wait_list; //優(yōu)先級等待隊列
struct task_struct *owner; //擁有該鎖進程的信息
int owner_prio;
… …
};
在如上代碼中,類型raw_spinlock_t就是原來的spinlock_t。即代碼中的spinlock_t就是新設(shè)計的自旋鎖,。rt_mutex結(jié)構(gòu)中,wait_list字段為優(yōu)先級等待隊列,。在mutex使用中,當遇到鎖住的臨界資源時,,任務(wù)被掛起到wait_list中,,臨界資源解鎖時等待任務(wù)被激活。臨界資源被保護的同時可以搶占,。
由于Linux內(nèi)核底層的臨界資源是不可搶占的,,使用mutex替換spinlock的過程中,這部分可以保留,,仍由不可搶占的spinlock保護,,如:保護硬件寄存器的鎖、調(diào)度器的運行隊列鎖等,。不可搶占的spinlock被重新命名為raw_spinlock_t,。spin_lock被宏定義為:
#define spin_lock(lock) PICK_OP(raw_spinlock_t,spin,_lock,lock)
函數(shù)PICK_OP支持兩種鎖共存機制,PICK_OP在編譯階段將鎖操作轉(zhuǎn)化為mutex或者spinlock:
#define PICK_OP(type, optype, op, lock) \
do { \
if (TYPE_EQUAL((lock), type)) \
_raw_##optype##op((type *)(lock)); \
else if (TYPE_EQUAL(lock, spinlock_t)) \
//調(diào)用gcc的內(nèi)嵌函數(shù)__builtin_types_compatible_p()
_spin##op((spinlock_t *)(lock)); \
else __bad_spinlock_type(); \
} while (0)
#define TYPE_EQUAL(lock, type) \
__builtin_types_compatible_p(typeof(lock), type *)
gcc的內(nèi)嵌函數(shù)__builtin_types_compatible_p用于判斷一個變量的類型是否為某指定的類型,,如果類型為spinlock_t,,將運行函數(shù)_spin_lock;類型為raw_spinlock_t,,將運行函數(shù)_raw_spin_lock。
實時rt_mutex在具體應(yīng)用中,,一個高優(yōu)先級任務(wù)搶占該鎖的同時,,把先前的鎖擁有者添加到互斥鎖等待隊列中,并在當前擁有該鎖的任務(wù)task_struct中標記等待該鎖的所有任務(wù);反之,,不能得到該鎖就把當前任務(wù)添加到鎖的優(yōu)先級等待隊列中,,直到喚醒執(zhí)行。為了防止優(yōu)先級逆轉(zhuǎn),,可以改變鎖的當前擁有者的優(yōu)先級為鎖的等待隊列中任務(wù)的最高優(yōu)先級,。
rt_mutex可以使高優(yōu)先級任務(wù)利用搶占鎖進入臨界區(qū),這樣內(nèi)核不可搶占區(qū)的數(shù)量和范圍大大縮小,,內(nèi)核可搶占性有了很大的提高,,且降低了實時高優(yōu)先級任務(wù)的搶占延遲,改善了系統(tǒng)的實時性能,。
2.3 可搶占大內(nèi)核鎖設(shè)計
大內(nèi)核鎖BKL(Big Kernel Lock)實質(zhì)上也是spinlock,,它用于保護整個內(nèi)核,該鎖保持時間較長,,對系統(tǒng)的實時性能影響很大[6],。采用Ingo Molnar的實時化方法,BKL使用semaphore實現(xiàn),,結(jié)構(gòu)定義如下代碼:
struct semaphore {
atomic_t count;
struct rt_mutex lock; //實時互斥鎖的使用
};
由結(jié)構(gòu)體發(fā)現(xiàn),,在BKL實現(xiàn)中利用了實時互斥鎖rt_mutex,在改進后的spinlock結(jié)構(gòu)體spinlock_t中也利用了實時互斥鎖rt_mutex,因此可搶占大內(nèi)核鎖和新的spinlock共用了低層的處理代碼,。使用semaphore之后,,大內(nèi)核鎖就可搶占了。
3 內(nèi)核實時性測試
針對Linux2.6內(nèi)核,,本文并沒有作出對內(nèi)核調(diào)度算法的修正,,只是探討了中斷運行機制、自旋鎖及大內(nèi)核鎖技術(shù)在系統(tǒng)實時性能上的局限性,,所以實驗測試主要測試中斷延遲時間和任務(wù)響應(yīng)時間,。實驗環(huán)境: Intel 2 GHz CPU,256 DDR內(nèi)存,,Kernel 2.6.22版本,。測試結(jié)果如表1所示。
由表可知,,在中斷服務(wù)程序中寫入標記,,測試中斷觸發(fā)至中斷服務(wù)程序執(zhí)行平均響應(yīng)時間,標準Linux2.6內(nèi)核平均中斷響應(yīng)時間為182 μs,改進后Linux2.6內(nèi)核為14 μs,。采用開源軟件LMbench3.0 測試系統(tǒng)任務(wù)調(diào)度延遲時間,標準Linux2.6內(nèi)核平均任務(wù)響應(yīng)時間為1 260 μs,,改進后Linux2.6內(nèi)核為162μs,。由此可見,改進策略在一定程度上大大減小了中斷延遲和任務(wù)調(diào)度時間,有利于改善移動機器人任務(wù)處理的實時性能,。
本文基于Linux2.6內(nèi)核的關(guān)中斷,、中斷優(yōu)先級、內(nèi)核的不可搶占性以及大內(nèi)核鎖保持時間過長等問題進行了實時性分析,,提出了相應(yīng)的改進方法,。利用中斷線程化、互斥鎖的應(yīng)用及大內(nèi)核鎖的改進等技術(shù)提高了系統(tǒng)的實時性能,,降低了內(nèi)核中斷延遲和調(diào)度延遲。改進后的內(nèi)核在移動機器人控制器平臺中有很好的應(yīng)用價值,,提高了機器人控制的實時性能,。
參考文獻
[1] 吳軍,周轉(zhuǎn)運. 嵌入式Linux系統(tǒng)應(yīng)用基礎(chǔ)與開發(fā)范例[M].北京:人民郵電出版社,2007.
[2] LOV R.Linux kernel development[M].陳莉君,等譯.北京: 機械工業(yè)出版社,2005.
[3] 鄒勇,王青,李明樹.Linux內(nèi)核的實時支持的研究與實現(xiàn)[J].計算機研究與發(fā)展,2002,39(4):466-472.
[4] 張玉芳,熊忠陽,王銀輝,等.Linux實時化設(shè)計方法研究[J].小型微型計算機系統(tǒng),2009,,30(3):421-424.
[5] 吳嬌梅,李紅艷,吳保榮,等.改善嵌入式Linux 實時性能的方法研究[J].微計算機信息,2006(1-2):46-47.
[6] 厲海燕,李新明.Linux搶占式內(nèi)核的研究與實現(xiàn)[J].計算機工程與設(shè)計,2005,26(9):2395-2399.