Linux內(nèi)核學(xué)習(xí)006——進(jìn)程管理(二)
之前提到在Linux內(nèi)核中把進(jìn)程(process)叫做任務(wù)(task),因此我會交替使用這兩個術(shù)語怠惶,本質(zhì)上它們指的是同一個東西耀态。
進(jìn)程描述符及任務(wù)結(jié)構(gòu)
內(nèi)核中把進(jìn)程的列表存放在任務(wù)隊列(一個雙向循環(huán)鏈表)中轮傍,鏈表中的的每一項都是類型為task_struct的結(jié)構(gòu)體,叫做進(jìn)程描述符首装。該結(jié)構(gòu)體描述了一個進(jìn)程的所有信息创夜,其定義位于linux-2.6.34/include/linux/sched.h中的1170行∠陕撸可以在之前安裝的虛擬機CentOS6上下載對應(yīng)源碼查看驰吓,或者使用在線網(wǎng)站查看:https://elixir.bootlin.com/linux/v2.6.34/source/include/linux/sched.h#L1170。
如下所示:
task_struct結(jié)構(gòu)體比較大系奉,在32位機器上檬贰,大概1.7KB。
任務(wù)列表大致如下所示:
分配進(jìn)程描述符
內(nèi)核通過slab分配器分配task_struct結(jié)構(gòu)體缺亮,這樣可以達(dá)到對象復(fù)用和緩存著色的目的翁涤。
這里的slab分配器是內(nèi)核分配內(nèi)存的一種方法,內(nèi)核內(nèi)存的分配通常是從空閑的內(nèi)存池中獲取的,主要有兩種方式:Buddy系統(tǒng)和slab分配葵礼。
slab其實就是一個或多個物理上連續(xù)的內(nèi)存頁号阿。每個內(nèi)核數(shù)據(jù)結(jié)構(gòu)都有一個高速緩存(cache),比如進(jìn)程描述符鸳粉。每個cache都含有一個或多個slab扔涧。其關(guān)系如下所示:
在2.6以前的內(nèi)核中,每個進(jìn)程的task_struct存放在其內(nèi)核棧的尾端赁严,這樣可以方便類似于x86這樣的寄存器較少的硬件體系結(jié)構(gòu)可以通過棧指針直接計算出其位置扰柠,而避免使用額外的寄存器記錄。在使用slab分配器動態(tài)生成task_struct后疼约,只需要在棧底(向下增長的棧)或棧頂(向上增長的棧)創(chuàng)建一個struct thread_info記錄即可卤档。
對于x86體系,struct thread_info定義在linux-2.6.34/arch/x86/include/asm/thread_info.h:26行程剥。其具體定義如下:
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable,
<0 => BUG */
mm_segment_t addr_limit;
struct restart_block restart_block;
void __user *sysenter_return;
#ifdef CONFIG_X86_32
unsigned long previous_esp; /* ESP of the previous stack in
case of nested (IRQ) stacks
*/
__u8 supervisor_stack[0];
#endif
int uaccess_err;
};
這樣劝枣,每個任務(wù)的thread_info結(jié)構(gòu)在其內(nèi)核棧的尾端分配,thread_info中的task指針指向?qū)嶋Htask_struct织鲸。
進(jìn)程描述符的存放
內(nèi)核通過一個唯一的進(jìn)程標(biāo)識符PID來標(biāo)識每個進(jìn)程舔腾,PID是類型位pid_t類型的整數(shù)(對于x86而言,就是int類型的別名)搂擦。為了與老版本的系統(tǒng)兼容稳诚,PID最大值位short int的最大值32768(可以增加到最大400萬左右)。pid存放在task_struct結(jié)構(gòu)體中瀑踢。
這個最大值實際上就是系統(tǒng)中允許同時存在的進(jìn)程的最大數(shù)目扳还,如果需要更大的值可以修改/proc/sys/kernel/pid_max提高。
在內(nèi)核中橱夭,訪問任務(wù)通常使用指向其task_struct的指針完成的氨距。而實際上,大多數(shù)內(nèi)核代碼直接通過處理struct task_struct完成的棘劣。因此俏让,能夠快速查找當(dāng)前正在執(zhí)行的任務(wù)的進(jìn)程描述符是很有用的,這是通過current宏完成的茬暇。這個宏的實現(xiàn)必須獨立于硬件體系結(jié)構(gòu):
- 某些體系結(jié)構(gòu)在寄存器中保存指向當(dāng)前正在運行的進(jìn)程的task_struct結(jié)構(gòu)的指針首昔,從而實現(xiàn)高效訪問
- 其他體系結(jié)構(gòu),例如x86糙俗,利用了存儲在內(nèi)核堆棧上的struct thread_info以計算thread_info的位置以及隨后的task_struct沙廉。
在PowerPC上,當(dāng)前的task_struct是保存在一個寄存器上的臼节。PowerPC有足夠多的寄存器,可以這樣加快訪問速度。
在x86系統(tǒng)上网缝,current把棧指針的后13個有效位屏蔽巨税,以此來計算thread_info的偏移。 該操作通過current_thread_info()函數(shù)完成粉臊。該函數(shù)位于linux2.6.34//arch/x86/include/asm/thread_info.h#L184草添。
具體代碼如下:
/* how to get the thread information struct from C */
static inline struct thread_info *current_thread_info(void)
{
return (struct thread_info *)
(current_stack_pointer & ~(THREAD_SIZE - 1));
}
其匯編代碼為(這里假設(shè)棧大小為8KB,若為4KB扼仲,則應(yīng)用4096代替8192):
movl $-8192, %eax
andl %esp, %eax
之后通過thread_info的task指針就可以獲取task_struct的地址了,current_thread_info()->task。