Linux下每個用戶空間進程(不是kernel thread)都有兩個堆棧,一個內(nèi)核棧,一個系統(tǒng)棧。
其中內(nèi)核棧在創(chuàng)建進程或者線程(do_fork)是創(chuàng)建,在2.6內(nèi)核中,他的內(nèi)容如下:
union thread_union {
structthread_info thread_info;
unsignedlong stack[THREAD_SIZE/sizeof(long)];
};
由此可見,此內(nèi)核棧的高端用于作為堆棧,底端用于存放thread_info(不是task_struct)。此堆棧用于存放進程在內(nèi)核時的callframes或者接收到中斷時的現(xiàn)場狀態(tài)(pt_regs),在有些體系結構下,也存放同步上下文切換時的狀態(tài)(switch_stack)。內(nèi)核棧不能動態(tài)增長。
用戶棧在進程調(diào)用execve的時候(參見fs/binfmt_*.c文件中的load_binary函數(shù),大概是這個函數(shù))創(chuàng)建,對于用clone創(chuàng)建的線程來說,它可以由用戶來指定,用戶棧和內(nèi)核沒有任何關系,它用于存放進程在用戶空間時的callframes。用戶堆??梢詣討B(tài)增長。
線程(英語:thread)是操作系統(tǒng)能夠進行運算調(diào)度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發(fā)多個線程,每條線程并行執(zhí)行不同的任務。在Unix System V及SunOS中也被稱為輕量進程(lightweight processes),但輕量進程更多指內(nèi)核線程(kernel thread),而把用戶線程(user thread)稱為線程。
線程是獨立調(diào)度和分派的基本單位。線程可以操作系統(tǒng)內(nèi)核調(diào)度的內(nèi)核線程,如Win32線程;由用戶進程自行調(diào)度的用戶線程,如Linux Portable Thread;或者由內(nèi)核與用戶進程,如Windows 7的線程,進行混合調(diào)度。
同一進程中的多條線程將共享該進程中的全部系統(tǒng)資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進程中的多個線程有各自的調(diào)用棧(call stack),自己的寄存器環(huán)境(register context),自己的線程本地存儲(thread-local storage)。
一個進程可以有很多線程,每條線程并行執(zhí)行不同的任務。
每個進程都有自己的 3 G 用戶空間,它們共享1GB的內(nèi)核空間。當一個進程從用戶空間進入內(nèi)核空間時,它就不再有自己的進程空間了.
對內(nèi)核線程的虛擬空間總結一下:
1、創(chuàng)建的時候:
父進程是用戶進程,則mm和active_mm均共享父進程的,然后內(nèi)核線程一般調(diào)用daemonize
父進程是內(nèi)核線程,則mm和active_mm均為NULL
總之,內(nèi)核線程的mm = NULL;進程調(diào)度的時候以此為依據(jù)判斷是用戶進程還是內(nèi)核線程。
2、進程調(diào)度的時候
如果切換進來的是內(nèi)核線程,則置active_mm為切換出去的進程的active_mm;
如果切換出去的是內(nèi)核線程,則置active_mm為NULL。
linux在創(chuàng)建用戶任務的時候,給每個任務都分配了一個kernel modestack。一個運行在用戶態(tài)的任務如果被一個IRQ打斷,中斷處理要做一次堆棧切換。這時linux好像使用了任務的kernel modestack,也就是說linux系統(tǒng)中沒有一個唯一的系統(tǒng)堆棧,而是每一個任務都有一個系統(tǒng)堆棧,中斷處理的棧使用的就是被打斷任務的系統(tǒng)堆棧。內(nèi)核線程也是進程,只不過沒有自己的用戶空間,但task_struct和內(nèi)核堆棧還是得有的,要不怎么運行呢?
[1.內(nèi)核在主動進行進程調(diào)度時,可以自己設置將要投入運行進程的sp0為TSS段中的sp0,則該用戶進程在進入內(nèi)核后使用的是它自身的系統(tǒng)堆棧,但如果cpu運行在某一用戶進程時,而為另一用戶進程服務的外部中斷發(fā)生了,在進入內(nèi)核后使用的是當前用戶進程的系統(tǒng)堆棧,還是中斷服務的另一用戶進程的系統(tǒng)堆棧呢?
2操作系統(tǒng)映象是否擁有自己的堆??臻g?還是利用用戶進程的系統(tǒng)堆棧?
回答: 1。外部中斷不是為某個用戶進程服務的,是為整個操作系統(tǒng)服務的,它始終用當前進程的核心堆棧。??????中斷棧
從《深入Linux內(nèi)核構架》中可以知道:內(nèi)核在IA-32平臺上,早期(2.6.36及之前)內(nèi)核如果配置了4K內(nèi)核棧(CONFIG_4KSTACKS)(默認是8K),對于常規(guī)的內(nèi)核工作以及IRQ處理例程共用這個棧來說似乎有點不夠用,所有引入了兩個棧:硬件IRQ棧和軟件IRQ棧。在這種情況下,當內(nèi)核進入中斷之后,檢測自己所在的棧是內(nèi)核棧還是中斷棧。如果是中斷棧(中斷嵌套情況)就去執(zhí)行中斷例程;如果是內(nèi)核棧就切換到中斷棧,同時復制當前內(nèi)核棧中的部分thread_info數(shù)據(jù)到中斷棧。
但是2.6.36之后的內(nèi)核就不再有4K內(nèi)核棧的配置,對于IA-32統(tǒng)一使用8K內(nèi)核棧,并總是使用兩個獨立的8K中斷棧。這樣的改變應該是由于計算機性能的提高、內(nèi)存的擴大(4G內(nèi)存已經(jīng)很平常,16G、32G內(nèi)存也已不新鮮)以及軟件的復雜度提高(對棧的需求增加)。
跟蹤了ARM構架的內(nèi)核代碼發(fā)現(xiàn)其中(arch/arm/kernel/irq.c)并沒有和x86類似的棧轉(zhuǎn)換設計。也就是說:ARM并沒有獨立的中斷棧,中斷共用當前進程的內(nèi)核棧。
Linux 0.11 系統(tǒng)中共使用了四種堆棧。
一種是系統(tǒng)初始化時臨時使用的堆棧;
一種是供內(nèi)核程序自己使用的堆棧(內(nèi)核堆棧),只有一個,位于系統(tǒng)地址空間固定的位置,也是后來任務0 的用戶態(tài)堆棧;
另一種是每個任務通過系統(tǒng)調(diào)用,執(zhí)行內(nèi)核程序時使用的堆棧,我們稱之為任務的內(nèi)核態(tài)堆棧,每個任務都有自己獨立的內(nèi)核態(tài)堆棧;
最后一種是任務在用戶態(tài)執(zhí)行的堆棧,位于任務(進程)地址空間的末端。
也就是說,schedule()的時候發(fā)生上下文切換,何時調(diào)用schedule()?
1.程序員顯示調(diào)用schedule().
2.內(nèi)核提供need_reshed標志表明是否需要重新執(zhí)行一次調(diào)度,每個進程都包含一個need_reshed標志(2.6中移入thread_info某個特殊變量的一位)。
1)當某個進程耗盡時間片時,scheduler_tick()會設置這個標志。
2)當一個優(yōu)先級高的程序進入可執(zhí)行狀態(tài)的時候,try_to_wake_up()也會設置這個標志。3)再返回用戶空間以的時候,內(nèi)核會檢查need_reshed。4)從中斷返回的時候,內(nèi)核會檢查need_reshed
實時調(diào)度策略
Linux提供了兩種實時調(diào)度策略:SCHED_FIFO和SCHED_RR。普通、非實時的調(diào)度策略是SCHED_NORMAL。
SCHED_FIFO級的進程會比任何SCHED_NORMAL級的進程都先得到調(diào)度,一旦一個SCHED_FIFO級進程處于可執(zhí)行狀態(tài),就會一直執(zhí)行,直到它自己受阻塞或顯式地釋放處理器。它不基于時間片,可以一直執(zhí)行下去,只有較高優(yōu)先級的SCHED_FIFO或者SCHED_RR任務才能搶占SCHED_FIFO任務。
SCHED_RR是帶時間片的SCHED_FIFO,是一種實時輪流調(diào)度算法。
這兩種實時算法實現(xiàn)的都是靜態(tài)優(yōu)先級,內(nèi)核不為之計算動態(tài)優(yōu)先級。
Linux的實時調(diào)度算法提供一種軟實時的工作方式,內(nèi)核調(diào)度進程,盡力使進程在它的限定時間到來前運行,但內(nèi)核不保證總能滿足這些進程的要求。