目的:
初步了解進(jìn)程描述符 task_struct。
目錄:
Linux的進(jìn)程
Linux 的進(jìn)程描述符
task_struct
內(nèi)核如何找到 task_struct
task_struct 的分配和初始化
實(shí)驗(yàn):打印 task_struct / thread_info / kernel mode stack
環(huán)境:
Linux-4.14 + ARMv7
1. Linux 的進(jìn)程
進(jìn)程的術(shù)語(yǔ)是 process,是 Linux 最基礎(chǔ)的抽象,另一個(gè)基礎(chǔ)抽象是文件。
最簡(jiǎn)單的理解,進(jìn)程就是執(zhí)行中 (executing, 不等于running) 的程序。
更準(zhǔn)確一點(diǎn)的理解,進(jìn)程包括執(zhí)行中的程序以及相關(guān)的資源(包括cpu狀態(tài)、打開(kāi)的文件、掛起的信號(hào)、tty、內(nèi)存地址空間等)。
一種簡(jiǎn)潔的說(shuō)法:進(jìn)程 = n*執(zhí)行流 + 資源,n>=1。
Linux 進(jìn)程的特點(diǎn):
通過(guò)系統(tǒng)調(diào)用 fork() 創(chuàng)建進(jìn)程,fork() 會(huì)復(fù)制現(xiàn)有進(jìn)程來(lái)創(chuàng)建一個(gè)全新的進(jìn)程。
內(nèi)核里,并不嚴(yán)格區(qū)分進(jìn)程和線程。
從內(nèi)核的角度看,調(diào)度單位是線程 (即執(zhí)行流)??梢园丫€程看做是進(jìn)程里的一條執(zhí)行流,1個(gè)進(jìn)程里可以有1個(gè)或者多個(gè)線程。
內(nèi)核里,常把進(jìn)程稱為 task 或者 thread,這樣描述更準(zhǔn)確,因?yàn)樵S多進(jìn)程就只有1條執(zhí)行流。
內(nèi)核通過(guò)輕量級(jí)進(jìn)程 (lightweight process) 來(lái)支持多線程。1個(gè)輕量級(jí)進(jìn)程就對(duì)應(yīng)1個(gè)線程,輕量級(jí)進(jìn)程之間可以共享打開(kāi)的文件、地址空間等資源。
2. Linux 的進(jìn)程描述符
2.1 task_struct
內(nèi)核里,通過(guò) task_struct 結(jié)構(gòu)體來(lái)描述一個(gè)進(jìn)程,稱為進(jìn)程描述符 (process descriptor),它保存著支撐一個(gè)進(jìn)程正常運(yùn)行的所有信息。
每一個(gè)進(jìn)程,即便是輕量級(jí)進(jìn)程(即線程),都有1個(gè) task_struct。
sched.h(includelinux) structtask_struct{ structthread_infothread_info; volatilelongstate; void*stack; [...] structmm_struct*mm; [...] pid_tpid; [...] structtask_struct*parent; [...] charcomm[TASK_COMM_LEN]; [...] structfiles_struct*files; [...] structsignal_struct*signal; }
這是一個(gè)龐大的結(jié)構(gòu)體,不僅有許多進(jìn)程相關(guān)的基礎(chǔ)字段,還有許多指向其他數(shù)據(jù)結(jié)構(gòu)的指針。
它包含的字段能完整地描述一個(gè)正在執(zhí)行的程序,包括 cpu 狀態(tài)、打開(kāi)的文件、地址空間、掛起的信號(hào)、進(jìn)程狀態(tài)等。
點(diǎn)擊查看大圖
作為初學(xué)者,先簡(jiǎn)單地了解部分字段就好::
struct thread_info thread_info:進(jìn)程底層信息,平臺(tái)相關(guān),下面會(huì)詳細(xì)描述。
long state:進(jìn)程當(dāng)前的狀態(tài),下面是幾個(gè)比較重要的進(jìn)程狀態(tài)以及它們之間的轉(zhuǎn)換流程。
點(diǎn)擊查看大圖
void *stack:指向進(jìn)程內(nèi)核棧,下面會(huì)解釋。
struct mm_struct *mm:與進(jìn)程地址空間相關(guān)的信息都保存在一個(gè)叫內(nèi)存描述符 (memory descriptor) 的結(jié)構(gòu)體 (mm_struct) 中。
點(diǎn)擊查看大圖
pid_t pid:進(jìn)程標(biāo)識(shí)符,本質(zhì)就是一個(gè)數(shù)字,是用戶空間引用進(jìn)程的唯一標(biāo)識(shí)。
struct task_struct *parent:父進(jìn)程的 task_struct。
char comm[TASK_COMM_LEN]:進(jìn)程的名稱。
struct files_struct*files:打開(kāi)的文件表。
struct signal_struct *signal:信號(hào)處理相關(guān)。
其他字段,等到有需要的時(shí)候再回過(guò)頭來(lái)學(xué)習(xí)。
2.2 當(dāng)發(fā)生系統(tǒng)調(diào)用或者進(jìn)程切換時(shí),內(nèi)核如何找到 task_struct ?
對(duì)于 ARM 架構(gòu),答案是:通過(guò)內(nèi)核棧 (kernel mode stack)。
為什么要有內(nèi)核棧?
因?yàn)閮?nèi)核是可重入的,在內(nèi)核中會(huì)有多條與不同進(jìn)程相關(guān)聯(lián)的執(zhí)行路徑。因此不同的進(jìn)程處于內(nèi)核態(tài)時(shí),都需要有自己私有的進(jìn)程內(nèi)核棧 (process kernel stack)。
當(dāng)進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài)時(shí),所使用的棧會(huì)從用戶棧切換到內(nèi)核棧。
至于是如何切換的,關(guān)鍵詞是系統(tǒng)調(diào)用,這不是本文關(guān)注的重點(diǎn),先放一邊,學(xué)習(xí)內(nèi)核要懂得恰當(dāng)?shù)臅r(shí)候忽略細(xì)節(jié)。
當(dāng)發(fā)生進(jìn)程切換時(shí),也會(huì)切換到目標(biāo)進(jìn)程的內(nèi)核棧。
同上,關(guān)鍵詞是硬件上下文切換 (hardware context switch),忽略具體實(shí)現(xiàn)。
無(wú)論何時(shí),只要進(jìn)程處于內(nèi)核態(tài),就會(huì)有內(nèi)核棧可以使用,否則系統(tǒng)就離崩潰不遠(yuǎn)了。
ARM 架構(gòu)的內(nèi)核棧和 task_struct 的關(guān)系如下:
內(nèi)核棧的長(zhǎng)度是 THREAD_SIZE,對(duì)于 ARM 架構(gòu),一般是 2 個(gè)頁(yè)框的大小,即 8KB。
內(nèi)核將一個(gè)較小的數(shù)據(jù)結(jié)構(gòu) thread_info 放在內(nèi)核棧的底部,它負(fù)責(zé)將內(nèi)核棧和 task_struct 串聯(lián)起來(lái)。thread_info 是平臺(tái)相關(guān)的,在 ARM 架構(gòu)中的定義如下:
//thread_info.h(archarmincludeasm) structthread_info{ unsignedlongflags;/*lowlevelflags*/ intpreempt_count;/*0=>preemptable,<0?=>bug*/ mm_segment_taddr_limit;/*addresslimit*/ structtask_struct*task;/*maintaskstructure*/ [...] structcpu_context_savecpu_context;/*cpucontext*/ [...] };
thread_info 保存了一個(gè)進(jìn)程能被調(diào)度執(zhí)行的最底層信息(low level task data),例如struct cpu_context_save cpu_context 會(huì)在進(jìn)程切換時(shí)用來(lái)保存/恢復(fù)寄存器上下文。
內(nèi)核通過(guò)內(nèi)核棧的棧指針可以快速地拿到 thread_info:
//thread_info.h(includelinux) staticinlinestructthread_info*current_thread_info(void) { //current_stack_pointer是當(dāng)前進(jìn)程內(nèi)核棧的棧指針 return(structthread_info*) (current_stack_pointer&~(THREAD_SIZE-1)); }
然后通過(guò) thread_info 找到 task_struct:
//current.h(includeasm-generic) #definecurrent(current_thread_info()->task)
內(nèi)核里通過(guò) current 宏可以獲得當(dāng)前進(jìn)程的 task_struct。
2.3 task_struct 的分配和初始化
當(dāng)上層應(yīng)用使用 fork() 創(chuàng)建進(jìn)程時(shí),內(nèi)核會(huì)新建一個(gè) task_struct。
進(jìn)程的創(chuàng)建是個(gè)復(fù)雜的工作,可以延伸出無(wú)數(shù)的細(xì)節(jié)。這里我們只是簡(jiǎn)單地了解一下 task_struct 的分配和部分初始化的流程。
fork() 在內(nèi)核里的核心流程:
dup_task_struct() 做了什么?
至于設(shè)置內(nèi)核棧里做了什么,涉及到了進(jìn)程的創(chuàng)建與切換,不在本文的關(guān)注范圍內(nèi),以后再研究了。
3. 實(shí)驗(yàn):打印 task_struct / thread_info / kernel mode stack
實(shí)驗(yàn)?zāi)康模?/p>
梳理 task_struct / thread_info / kernel mode stack 的關(guān)系。
實(shí)驗(yàn)代碼:
#include
運(yùn)行效果:
taskmoduleinit insmod3123task_struct(edb42580)/stack(ed46c000~ed474000)/thread_info->task(edb42580) bash2393task_struct(eda13e80)/stack(c9dda000~c9de2000)/thread_info->task(eda13e80) sshd2255task_struct(ee5c9f40)/stack(c9d2e000~c9d36000)/thread_info->task(ee5c9f40) sshd543task_struct(ef15f080)/stack(ee554000~ee55c000)/thread_info->task(ef15f080) systemd1task_struct(ef058000)/stack(ef04c000~ef054000)/thread_info->task(ef058000)
在程序里,我們通過(guò) task_struct 找到 stack,然后通過(guò) stack 找到 thread_info,最后又通過(guò) thread_info->task 找到 task_struct。
4. 相關(guān)參考
Linux 內(nèi)核設(shè)計(jì)與實(shí)現(xiàn) / 第 3.1 章節(jié)
深入理解 Linux 內(nèi)核 / 3
Linux 內(nèi)核深度解析 / 2.5.1
深入Linux 內(nèi)核架構(gòu) / 2.3
責(zé)任編輯:lq
-
Linux
+關(guān)注
關(guān)注
87文章
11511瀏覽量
213887 -
多線程
+關(guān)注
關(guān)注
0文章
279瀏覽量
20452 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
207瀏覽量
14289
原文標(biāo)題:Linux 內(nèi)核 / 進(jìn)程管理 / 如何描述一個(gè)進(jìn)程?
文章出處:【微信號(hào):strongerHuang,微信公眾號(hào):strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
詳解Linux系統(tǒng)中的服務(wù)管理

Linux常用命令大全
Linux后臺(tái)進(jìn)程管理詳解

Linux系統(tǒng)進(jìn)程管理入門指南

Linux進(jìn)程狀態(tài)詳解

Linux計(jì)劃任務(wù)cron詳解

深入解析Linux程序與進(jìn)程

Linux之CPU調(diào)度策略和CPU親和性

深入Linux進(jìn)程管理:提升效率與穩(wěn)定性的關(guān)鍵方法

一文搞懂Linux進(jìn)程的睡眠和喚醒
Linux網(wǎng)絡(luò)基礎(chǔ)知識(shí)總結(jié)

Linux lsof命令的基本用法

Linux用戶身份與進(jìn)程權(quán)限詳解

深入探討Linux的進(jìn)程調(diào)度器

評(píng)論