Linux 對進程的描述
進程是操作系統(tǒng)種調(diào)度的實體,對進程擁有資源的描述稱為進程控制塊(PCB, Process Contrl Block)。
通過 task_struct 描述進程
內(nèi)核里,通過 task_struct 結(jié)構(gòu)體來描述一個進程,稱為進程描述符 (process descriptor),它保存著支撐一個進程正常運行的所有信息。task_struct 結(jié)構(gòu)體內(nèi)容太多,這里只列出部分成員變量,感興趣的讀者可以去源碼 include/linux/sched.h頭文件查看。
- struct task_struct {
 - #ifdef CONFIG_THREAD_INFO_IN_TASK
 - /*
 - * For reasons of header soup (see current_thread_info()), this
 - * must be the first element of task_struct.
 - */
 - struct thread_info thread_info;
 - #endif
 - volatile long state;
 - void *stack;
 - ......
 - struct mm_struct *mm;
 - ......
 - pid_t pid;
 - ......
 - struct task_struct *parent;
 - ......
 - char comm[TASK_COMM_LEN];
 - ......
 - struct files_struct *files;
 - ......
 - struct signal_struct *signal;
 - }
 
task_struct 中的主要信息分類:
1.標示符:描述本進程的唯一標識符 pid,用來區(qū)別其他進程。
2.狀態(tài):任務(wù)狀態(tài),退出代碼,退出信號等
3.優(yōu)先級:相對于其他進程的優(yōu)先級
4.程序計數(shù)器:程序中即將被執(zhí)行的下一條指令的地址
5.內(nèi)存指針:包括程序代碼和進程相關(guān)數(shù)據(jù)的指針,還有和其他進程共享的內(nèi)存塊的指針
6.上下文數(shù)據(jù):進程執(zhí)行時處理器的寄存器中的數(shù)據(jù)
7.I/O狀態(tài)信息:包括顯示的I/O請求,分配的進程I/O設(shè)備和進程使用的文件列表
8.記賬信息:可能包括處理器時間總和,使用的時鐘總和,時間限制,記帳號等
- struct thread_info thread_info: 進程被調(diào)度執(zhí)行的信息
 - volatile long state:-1是不運行的,=0是運行狀態(tài),>0是停止狀態(tài)。下面是幾個比較重要的進程狀態(tài)以及它們之間的轉(zhuǎn)換流程。
 
- void *stack:指向內(nèi)核棧的指針,內(nèi)核通過 dup_task_struct 為每個進程都分配內(nèi)核??臻g,并記錄在此。
 - struct mm_struct *mm: 與進程地址空間相關(guān)的信息。
 
- pid_t pid: 進程標識符
 - char comm[TASK_COMM_LEN]: 進程的名稱
 - struct files_struct *files: 打開的文件表
 - struct signal_struct *signal: 信號處理相關(guān)
 
task_struct, thread_info 和內(nèi)核棧 sp 的關(guān)系
接著看下 thread_info 結(jié)構(gòu):
- struct thread_info {
 - unsigned long flags; /* low level flags */
 - mm_segment_t addr_limit; /* address limit */
 - #ifdef CONFIG_ARM64_SW_TTBR0_PAN
 - u64 ttbr0; /* saved TTBR0_EL1 */
 - #endif
 - union {
 - u64 preempt_count; /* 0 => preemptible, <0 => bug */
 - struct {
 - #ifdef CONFIG_CPU_BIG_ENDIAN
 - u32 need_resched;
 - u32 count;
 - #else
 - u32 count;
 - u32 need_resched;
 - #endif
 - } preempt;
 - };
 - #ifdef CONFIG_SHADOW_CALL_STACK
 - void *scs_base;
 - void *scs_sp;
 - #endif
 - };
 
接著再來看下內(nèi)核棧的定義:
- union thread_union {
 - #ifndef CONFIG_ARCH_TASK_STRUCT_ON_STACK
 - struct task_struct task;
 - #endif
 - #ifndef CONFIG_THREAD_INFO_IN_TASK
 - struct thread_info thread_info;
 - #endif
 - unsigned long stack[THREAD_SIZE/sizeof(long)];
 - };
 
當 CONFIG_THREAD_INFO_IN_TASK 這個配置打開的時候,則 thread_union 結(jié)構(gòu)中只存在 stask 成員了。
內(nèi)核在啟動的時候會在 head.S 里通過 __primary_switched 來做內(nèi)核棧的初始化:
- SYM_FUNC_START_LOCAL(__primary_switched)
 - adrp x4, init_thread_union
 - add sp, x4, #THREAD_SIZE
 - adr_l x5, init_task
 - msr sp_el0, x5 // Save thread_info
 
將 init_thread_union 的地址保存到 x4,然后偏移 THREAD_SIZE 棧大小,用于初始化 sp。將 init_task 進程描述符地址賦值給 x5,并保存到 sp_el0。
下面再看下 init_thread_union 和 init_task 的定義:
- #include/linux/sched/task.h
 - extern union thread_union init_thread_union;
 - #init/init_task.c
 - struct task_struct init_task
 - __aligned(L1_CACHE_BYTES)
 - = {
 - #ifdef CONFIG_THREAD_INFO_IN_TASK
 - .thread_info = INIT_THREAD_INFO(init_task),
 - .stack_refcount = REFCOUNT_INIT(1),
 - #endif
 - .....
 - };
 
故這三者的關(guān)系可以通過下圖描述:
如何獲取當前進程
內(nèi)核中經(jīng)常通過 current 宏來獲得當前進程對應(yīng)的 struct task_sturct 結(jié)構(gòu),我們借助 current,結(jié)合上面介紹的內(nèi)容,看下具體的實現(xiàn)。
- static __always_inline struct task_struct *get_current(void)
 - {
 - unsigned long sp_el0;
 - asm ("mrs %0, sp_el0" : "=r" (sp_el0));
 - return (struct task_struct *)sp_el0;
 - }
 - #define current get_current()
 
代碼比較簡單,可以看出通過讀取用戶空間棧指針寄存器 sp_el0 的值,然后將此值強轉(zhuǎn)成 task_struct 結(jié)構(gòu)就可以獲得當前進程。(sp_el0 里存放的是 init_task,即 thread_info 地址,thread_info 又是在 task_sturct 的開始處,從而找到當前進程。)


















 
 
 

 
 
 
 