沒有廢話,內(nèi)存管理暫時(shí)告一段落,正式進(jìn)入進(jìn)程管理的內(nèi)容。
內(nèi)核通過 task_struct 描述進(jìn)程
用命令 pstree 可以讓內(nèi)核以樹形的結(jié)構(gòu)把進(jìn)程之間的關(guān)系列出來,如下圖:
這是進(jìn)程在內(nèi)核中的結(jié)構(gòu)形式,那么內(nèi)核是如何來以樹形結(jié)構(gòu)管理描述這些進(jìn)程的呢?用來描述進(jìn)程的數(shù)據(jù)結(jié)構(gòu),可以理解為進(jìn)程的屬性。比如進(jìn)程的狀態(tài)、進(jìn)程的標(biāo)識(PID)等,都被封裝在了進(jìn)程描述符 task_struct 這個數(shù)據(jù)結(jié)構(gòu)中。
struct task_struct {
......
/* -1 unrunnable, 0 runnable, >0 stopped: */
//任務(wù)狀態(tài)。<0是不運(yùn)行狀態(tài),=0是運(yùn)行狀態(tài),>0是停止?fàn)顟B(tài)。
volatile long state;
......
//指向內(nèi)核棧的指針
void *stack;
......
/*進(jìn)程的調(diào)度策略,有6種。
*限期進(jìn)程調(diào)度策略:SCHED_DEADLINE。
*實(shí)時(shí)進(jìn)程調(diào)度策略:SCHED_FIFO,SCHED_RR。
*普通進(jìn)程調(diào)度策略:SCHED_NORMAL,SCHED_BATCH,SCHED_IDLE。
*/
unsigned int policy;
......
//進(jìn)程內(nèi)存管理信息
struct mm_struct *mm;
struct mm_struct *active_mm;
......
//進(jìn)程標(biāo)識符,用來代表一個進(jìn)程
pid_t pid;
......
//線程鏈表
struct list_head thread_group;
struct list_head thread_node;
struct completion *vfork_done;
......
/* Filesystem information: */
//文件系統(tǒng)信息
struct fs_struct *fs;
/* Open file information: */
//打開文件信息
struct files_struct *files;
......
/* CPU-specific state of this task: */
//進(jìn)程的CPU狀態(tài),切換時(shí),要保存到停止進(jìn)程的task_struct中
struct thread_struct thread;
......
};
內(nèi)核就是通過list_head鏈表把各個進(jìn)程關(guān)系以樹形結(jié)構(gòu)管理起來的。
task_struct 結(jié)構(gòu)體內(nèi)容太多,這里只列出部分成員變量,感興趣的讀者可以去源碼 include/linux/sched.h頭文件查看。
task_struct 中的主要信息分類:
- 標(biāo)示符:描述本進(jìn)程的唯一標(biāo)識符 pid,用來區(qū)別其他進(jìn)程。狀態(tài):任務(wù)狀態(tài),退出代碼,退出信號等優(yōu)先級:相對于其他進(jìn)程的優(yōu)先級程序計(jì)數(shù)器:程序中即將被執(zhí)行的下一條指令的地址內(nèi)存指針:包括程序代碼和進(jìn)程相關(guān)數(shù)據(jù)的指針,還有和其他進(jìn)程共享的內(nèi)存塊的指針上下文數(shù)據(jù):進(jìn)程執(zhí)行時(shí)處理器的寄存器中的數(shù)據(jù)I/O狀態(tài)信息:包括顯示的I/O請求,分配的進(jìn)程I/O設(shè)備和進(jìn)程使用的文件列表記賬信息:可能包括處理器時(shí)間總和,使用的時(shí)鐘總和,時(shí)間限制,記帳號等
ARM64不用通過struct thread_info thread_info獲取當(dāng)前task_struct
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 直接作為 task_struct 返回了。對于ARM64平臺,記錄當(dāng)前進(jìn)程的task_struct地址是利用sp0_el1寄存器,當(dāng)內(nèi)核執(zhí)行進(jìn)程切換時(shí)會把當(dāng)前要運(yùn)行的進(jìn)程task_struct地址記錄到該寄存器中。因此我們current查找task_struct時(shí)也是很簡單了,不再用通過sp和thread_info去定位了。
volatile long state
-1是不運(yùn)行的,=0是運(yùn)行狀態(tài),>0是停止?fàn)顟B(tài)
Linux中的 ready 和 running 對應(yīng)的都是TASK_RUNNING標(biāo)志位,ready 表示進(jìn)程正處在隊(duì)列中,尚未被調(diào)度;running 則表示進(jìn)程正在CPU上運(yùn)行;
void *stack
指向內(nèi)核棧的指針,內(nèi)核通過 dup_task_struct 為每個進(jìn)程都分配內(nèi)核??臻g,并記錄在此。
struct mm_struct *mm
與進(jìn)程地址空間相關(guān)的信息。
task_struct 的分配和初始化
圖中可知,上層應(yīng)用通過各種方式創(chuàng)建進(jìn)程時(shí),最終都會通過 _do_fork 新建一個 task_struct。