目錄
2.4 進(jìn)程管理相關(guān)的系統(tǒng)調(diào)用
2.4.1 進(jìn)程復(fù)制
2.4.2 內(nèi)核線程
2.4.3 啟動(dòng)新程序
2.4.4 退出進(jìn)程
本專欄文章將有70篇左右,歡迎+關(guān)注,訂閱后續(xù)文章。
2.4 進(jìn)程管理相關(guān)的系統(tǒng)調(diào)用
2.4.1 進(jìn)程復(fù)制
1. _do_fork函數(shù)
????????fork vfork clone都最終調(diào)用_do_fork
????????????????clone:通過(guò)CLONE_XX標(biāo)志精確控制父子進(jìn)程共享哪些資源。
????????????????vfork:由于fork使用了COW技術(shù),vfork優(yōu)勢(shì)不再,使用少。
COW:copy-on-write,寫時(shí)復(fù)制。
????????
fork子進(jìn)程時(shí),使用COW機(jī)制,原理:
? ? ? ? 1. 不復(fù)制父進(jìn)程的地址空間。而是將父進(jìn)程的地址空間標(biāo)記為只讀,并與子進(jìn)程共享相同的物理內(nèi)存頁(yè)。
? ? ? ? 2. 當(dāng)父進(jìn)程或子進(jìn)程有寫內(nèi)存時(shí),發(fā)生缺頁(yè)異常。
? ? ? ?3. 缺頁(yè)異常處理中檢查該頁(yè)是否可以寫。
????????????????若可以,寫數(shù)據(jù)到內(nèi)存頁(yè),再修改子進(jìn)程頁(yè)表項(xiàng)。
????????????????若不可以,段錯(cuò)誤。
COW頁(yè):減少不必要的拷貝,提高性能。
2. 執(zhí)行系統(tǒng)調(diào)用
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
stack_start:用戶棧
parent_tidptr,child_tidptr:
用于返回線程ID給用戶空間,因?yàn)閜thread_create函數(shù)需要tid值
系統(tǒng)調(diào)用在用戶空間和內(nèi)核空間傳遞參數(shù)的方法因體系結(jié)構(gòu)而異。
方法有:
????????寄存器傳遞:速度快,但寄存器數(shù)量有限。
????????棧傳遞:可傳遞內(nèi)容多。
3. do_fork的實(shí)現(xiàn)
copy_process:見下節(jié)
wake_up_new_task:將該新進(jìn)程加入調(diào)度器隊(duì)列。
4. copy_process 復(fù)制進(jìn)程
dup_task_struct函數(shù):
????????復(fù)制父進(jìn)程的task_struct和thread_info結(jié)構(gòu)體。
task_struct:存儲(chǔ)體系架構(gòu)無(wú)關(guān)的通用信息。
thread_info:存儲(chǔ)線程的重要信息,不同體系架構(gòu)定義不一樣。從task_struct中獨(dú)立出來(lái)。
????????通常包含:內(nèi)核棧棧頂,指向當(dāng)前線程的task_struct等。
????????task_struct:存儲(chǔ)體系架構(gòu)無(wú)關(guān)的通用信息。
創(chuàng)建新進(jìn)程時(shí)分配了新的內(nèi)核棧,即task_struct->stack
復(fù)制后,父子進(jìn)程兩個(gè)的task_struct結(jié)構(gòu)體只有一個(gè)成員不同:
????????新進(jìn)程分配了一個(gè)自己的內(nèi)核棧,即task_struct->stack
union thread_union {
struct thread_info thread_info; 定義在不同體系中
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
每個(gè)進(jìn)程有一個(gè)內(nèi)核棧,大小為8K。如下:
THREAD_SIZE=8K,即上圖內(nèi)核棧最大為8K,惡意操作內(nèi)核??赡芨采wthread_info
struct thread_info { //以arch/arm為例
unsigned long flags;
int preempt_count; 搶占計(jì)數(shù),表示當(dāng)前線程是否可被搶占。
struct task_struct *task; 代表當(dāng)前線程
__u32 cpu; 當(dāng)前線程所在CPU
struct cpu_context_save cpu_context; 保存著CPU寄存器(如PC,SP等)
};
其中thread_info中flag有:
TIF_SIGPENDING 當(dāng)前進(jìn)程是否有待決信號(hào)
TIF_NEED_RESCHED 當(dāng)前進(jìn)程想讓出CPU,調(diào)度器選擇其他進(jìn)程執(zhí)行。
? TIF = Thread Info Flag
如何訪問(wèn)指定線程的thread_info?
????????(struct thread_info *) (task)->stack
如何根據(jù)當(dāng)前線程thread_info找到當(dāng)前線程的task_struct?
????????task_struct *current = current_thread_info()->task
如何訪問(wèn)當(dāng)前線程的thread_info?
struct thread_info *current_thread_info(void) ARM為例
{
register unsigned long sp asm ("sp"); //sp寄存器:保存了當(dāng)前線程的內(nèi)核棧頂部
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}
??
如何根據(jù)thread_info找到對(duì)應(yīng)task_struct?
????????task_struct *current = current_thread_info()->task
task_struct->stack和CPU sp寄存器,如上圖,兩者不指向同一地址:
????????task_struct->stack:
????????????????指向創(chuàng)建該線程時(shí)分配8K內(nèi)核棧的起始地址。也就是thread_info處
????????CPU sp寄存器:
????????????????當(dāng)前CPU運(yùn)行線程的內(nèi)核棧棧頂。
當(dāng)前進(jìn)程正在運(yùn)行時(shí):
????????通過(guò)ARM sp寄存器值,得到當(dāng)前線程的thread_info,再得到current的task_struct。
進(jìn)程切換到一個(gè)新進(jìn)程時(shí):
????????通過(guò)task_strcut -> stack,得到該線程的thread_info,再通過(guò)thread_info得到cpu_context,即可得到該進(jìn)程上次執(zhí)行時(shí)的寄存器信息,如pc,sp,r0-r12等。
進(jìn)程切換時(shí),關(guān)于進(jìn)程的task_struct的stack成員,sp寄存器,變化過(guò)程?
1. 保存當(dāng)前進(jìn)程的上下文:
????????保存當(dāng)前進(jìn)程上下文到內(nèi)核棧中:包括CPU的通用寄存器、程序計(jì)數(shù)器PC、棧指SP等。
2. 切換新進(jìn)程的:
?????切換到新進(jìn)程的task_struct結(jié)構(gòu)體,再通過(guò)task_struct->stack得到thread_info。
3. 恢復(fù)新進(jìn)程上下文
????????從thread_info中cpu_context得到該進(jìn)程上次執(zhí)行時(shí)的上下文信息。如pc,sp,r0-r12等。從而恢復(fù)新進(jìn)程上下文值。此時(shí)可正確得到新進(jìn)程的內(nèi)核棧棧頂sp。
struct pt_regs 和 thread_info中struct cpu_context_save 是用于保存 CPU 寄存器狀態(tài)
區(qū)別:
????????struct pt_regs:用于處理異?;蛳到y(tǒng)調(diào)用返回時(shí)將其恢復(fù)到原始狀態(tài),還可傳參。
????????struct cpu_context_save:用于進(jìn)程切換時(shí)主動(dòng)保存CPU上下文。
kstack_end(void *addr)函數(shù):
????????返回當(dāng)前線程的內(nèi)核棧的結(jié)束地址。
????????????????這樣就可判斷某個(gè)地址是否在內(nèi)核棧區(qū)間。
繼續(xù)回到copy_process
sched_fork函數(shù):
????????1. 初始化子進(jìn)程調(diào)度參數(shù):優(yōu)先級(jí)和調(diào)度策略等。
????????2. 復(fù)制父進(jìn)程的調(diào)度器相關(guān)數(shù)據(jù)(調(diào)度器類別,時(shí)間片)。
????????3. 將子進(jìn)程加入調(diào)度隊(duì)列。
copy_process會(huì)檢測(cè)如下標(biāo)志:
????????CLONE_FS 共享父進(jìn)程的文件系統(tǒng)
????????CLONE_NEWXX 不共享的資源
????????CLONE_FILES 共享父進(jìn)程的文件描述符
????????CLONE_SIGHAND 共享父進(jìn)程的信號(hào)處理函數(shù)
????????CLONE_MM COW,只復(fù)制頁(yè)表
struct pt_regs { 如上圖,存儲(chǔ)在當(dāng)前線程的內(nèi)核棧最底部中。
????????long uregs[18];
};
struct pt_regs作用:
????????從用戶態(tài)陷入內(nèi)核態(tài)時(shí)候,用戶態(tài)的上下文信息保存在pt_regs數(shù)據(jù)結(jié)構(gòu)中。還可傳遞系統(tǒng)調(diào)用參數(shù)和返回值。
存儲(chǔ)的寄存器信息有:
????????#define ARM_cpsr uregs[16] 程序狀態(tài)寄存器
????????#define ARM_pc uregs[15]
????????#define ARM_lr uregs[14]
????????#define ARM_sp uregs[13] 當(dāng)前線程內(nèi)核棧的棧頂
????????#define ARM_ip uregs[12]
????????#define ARM_fp uregs[11]
????????#define ARM_r10 uregs[10] //通用寄存器 r0-r10
struct pt_regs這18個(gè)寄存器,保存在當(dāng)前線程的內(nèi)核棧的底部,如上圖。
????????即 :struct pt_regs *regs = task_struct->stack + THREAD_START_SP - 1
copy_process還調(diào)用copy_thread。
????????copy_thread重要內(nèi)容:
????????????????填充thread_info和pt_regs。
父子進(jìn)程可共享信號(hào)處理函數(shù),但不共享掛起待處理信號(hào)。
unsigned long put_user(void __user *dst, const void *src, unsigned long size);
????????向用戶空間傳遞單個(gè)數(shù)據(jù)。如char,short,int大小的數(shù)據(jù),比copy_to_user快。
copy_to_user優(yōu)點(diǎn):可復(fù)制任意類型和長(zhǎng)度數(shù)據(jù)。
每個(gè)體系的虛擬地址0到4KB的區(qū)域,沒有任何意義。可重用該地址范圍來(lái)編碼錯(cuò)誤碼。
如果返回值指向0-4KB地址范圍內(nèi)部,表示該調(diào)用失敗,其原因由指針值判斷。
宏ERR_PTR:將數(shù)值常數(shù)編碼為指針。
使用方法:return ERR_PTR(-EINVAL);
2.4.2 內(nèi)核線程
內(nèi)核線程父進(jìn)程是:init進(jìn)程
內(nèi)核線程的任務(wù)通常是周期任務(wù),如:
????????pdflush:刷新臟頁(yè)到磁盤。
????????kswapd:回寫內(nèi)存頁(yè)到交換區(qū)。
????????ksoftirqd:處理軟中斷。
創(chuàng)建內(nèi)核線程:
????????pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
????????最終也調(diào)用_do_fork(CLONE_VM)
創(chuàng)建的內(nèi)核線程在指定CPU上運(yùn)行:
????????kthread_create_on_cpu()
????????????????-> p->sched_class->set_cpus_allowed(p, new_mask);
kthread_run() = kthread_create() + wake_up_process()
內(nèi)核線程不需要用戶空間,所以內(nèi)核線程task_struct的mm_struct=NULL。
當(dāng)內(nèi)核線程運(yùn)行,可不置換掉之前進(jìn)程的用戶空間地址,因?yàn)閮?nèi)核線程不使用用戶空間。所以用active_mm保存用戶空間mm_struct,因?yàn)閮?nèi)核線程運(yùn)行后調(diào)度的進(jìn)程通常還是之前那個(gè)用戶進(jìn)程,通過(guò)active_mm直接恢復(fù),不用修改映射表,TLB中緩存的映射表仍然有效。這叫惰性TLB。
惰性TLB:一種優(yōu)化策略,延遲或避免不必要TLB的更新,提高性能。
TASK_SIZE:即用戶態(tài)虛擬地址大小(32位,0-3G)。
????????內(nèi)核線程地址空間大于TASK_SIZE。
2.4.3 啟動(dòng)新程序
execve系統(tǒng)調(diào)用
int do_execve(struct filename *filename, const char __user *const __user *__argv, const char __user *const __user *__envp)
會(huì)__user定義的指針進(jìn)行參數(shù)檢查。
linux_binfmt存儲(chǔ)了所有注冊(cè)的可執(zhí)行程序的加載函數(shù)和執(zhí)行函數(shù)。
struct linux_binprm:保存可執(zhí)行文件的信息,包括可執(zhí)行程序的路徑,參數(shù)和環(huán)境變量的信息,vma
struct linux_binfmt {
????????struct list_head lh; 連接所有二進(jìn)制的執(zhí)行函數(shù)
????????int (*load_binary)(struct linux_binprm *); 加載二進(jìn)制文件
????????int (*load_shlib)(struct file *); 加載動(dòng)態(tài)庫(kù)
????????int (*core_dump)(struct coredump_params *cprm); 用于crash時(shí)核心轉(zhuǎn)儲(chǔ)文件
}
Linux文件特殊權(quán)限SUID、SGID、Sticky總結(jié):
SUID文件所屬主:Set User ID
????????當(dāng)一個(gè)可執(zhí)行文件具有SUID權(quán)限時(shí),它執(zhí)行時(shí)臨時(shí)具有文件所有者的權(quán)限,而不是執(zhí)行者的權(quán)限。
????????作用:暫時(shí)提升用戶權(quán)限。允許普通用戶執(zhí)行root用戶的程序。
????????缺點(diǎn):潛在安全性威脅。謹(jǐn)慎使用。
????????使用舉例:
????????????????/usr/bin/passwd:允許用戶更改自己的密碼而無(wú)需root權(quán)限。
????????設(shè)置方法:
????????????????增加suid權(quán)限:chmod u+s ,或chmod 4755
????????????????移除suid權(quán)限:chmod u-s ,或chmod 0755。
SGID文件屬組: Set Group ID
????????當(dāng)一個(gè)文件或目錄設(shè)置SGID權(quán)限后,任何用戶執(zhí)行該文件或訪問(wèn)該目錄時(shí),都以該文件或目錄所屬的組身份執(zhí)行,而不是該用戶的組權(quán)限。
????????使用場(chǎng)景:當(dāng)不同組的用戶在一個(gè)共享目錄下創(chuàng)建新文件,新文件是該目錄所屬組的權(quán)限,而不是創(chuàng)建文件的用戶的組權(quán)限??纱_保所有用戶以相同的組權(quán)限執(zhí)行該目錄下新文件。
????????設(shè)置方法:
????????????????增加suid權(quán)限:chmod g+s ,或chmod 2755。
????????????????移除sgid權(quán)限:chmod g-s ,或chmod 0755。
Sticky權(quán)限:
????????作用:一般用于目錄,只允該目錄下的文件的創(chuàng)建者刪除自己的創(chuàng)建的文件,不允許其他人刪除文件。
二進(jìn)制文件起始處的magic值可標(biāo)識(shí)該文件類型。
????????如:ELF可執(zhí)行文件:Magic number: 0x7F ELF
????????????????JPEG圖像文件:Magic number:0xFFD8FF
search_binary_hander:
????????根據(jù)文件起始處的magic值來(lái)查找對(duì)應(yīng)二進(jìn)制文件的加載,執(zhí)行函數(shù)。
二進(jìn)制加載函數(shù): 將文件段映射到虛擬地址空間。
????????最終給變量start_code,end_code,start_data,end_data,start_brk brk,start_stack,arg_start,arg_end賦值。
每種二進(jìn)制格式通過(guò)register_binfmt注冊(cè):
????????如script_format,elf_format,aout_format等
2.4.4 退出進(jìn)程
exit文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-847433.html
各種引用計(jì)數(shù)減1。減1后若等于0,釋放資源。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-847433.html
到了這里,關(guān)于《深入Linux內(nèi)核架構(gòu)》第2章 進(jìn)程管理和調(diào)度 (2)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!