一. 概述
Linux是類Unix系統,借鑒了Unix的設計并實作相關接口,但并非Unix。Linux是由Linus Torvalds于1991年創造的開源免費系統,采用GNU GPL協定保護,下面列舉Linux的一些主要特點:
Linux系統中萬物皆為檔案,這種抽象友善操作資料或裝置,隻需一套統一的系統接口open, read, write, close即可完成對檔案的操作
Linux是單核心,支援動态加載核心子產品,可在運作時根據需求動态加載和解除安裝部分核心代碼;
Linux核心支援可搶占;
Linux核心建立程序,采用獨特的fork()系統調用,建立程序較高效;
Linux核心并不區分程序和線程,對于核心而言,程序與線程無非是共享資源的差別,對CPU排程來說并沒有顯著差異。
1.1 程序概念
程序與線程的發展演化的目标,是為了更快的建立程序/線程,更小的上下文切換開銷,更好的支援SMP以及HMP架構的CPU。
線程上下文(例如各個寄存器狀态,pc指針)的切換比程序開銷要小得多。
系統需要運轉起來,代碼都是靜态的,程序才具有生命力,程序是程式的動态執行過程
,程序正是作業系統的心髒所在。何為程序?程序是處于執行狀态的代碼以及相關資源的集合,不僅僅是代碼段(text section),還包括檔案,信号,CPU狀态,記憶體位址空間等。線程基本可以等同于程序般對待。
虛拟處理器:多個程序共享同一個處理器,但虛拟處理器給程序一種獨占的感覺;
虛拟記憶體:多程序分享整個記憶體,但虛拟記憶體給程序以獨占整個記憶體空間的感覺;
二. 程序
2.1 task_struct結構體
程序主要由以下幾部分組成:
代碼段:編譯後形成的一些指令
資料段:程式運作時需要的資料
隻讀資料段:常量
已初始化資料段:全局變量,靜态變量
未初始化資料段(bss):未初始化的全局變量和靜态變量
堆棧段:程式運作時動态配置設定的一些記憶體
PCB:程序資訊,狀态辨別等
Linux核心中程序用task_struct結構體表示,稱為程序描述符,該結構體相對比較複雜,有幾百行代碼,記載着該程序相關的所有資訊,比如程序位址空間,程序狀态,打開的檔案等。對核心而言,程序或者線程都稱為任務task。核心将所有程序放入一個雙向循環連結清單結構的任務清單(task list)。

struct task_struct {
volatile long state; //程序狀态
struct mm_struct *mm, *active_mm; //記憶體位址空間
pid_t pid;
pid_t tgid;
struct task_struct __rcu *real_parent; //真正的父程序,fork時記錄的
struct task_struct __rcu *parent; // ptrace後,設定為trace目前程序的程序
struct list_head children; //子程序
struct list_head sibling;//父程序的子程序,即兄弟程序
struct task_struct *group_leader; //線程組的領頭線程
char comm[TASK_COMM_LEN]; //程序名,長度上限為16字元
struct fs_struct *fs; //檔案系統資訊
struct files_struct *files; // 打開的檔案
struct signal_struct *signal;
struct sighand_struct *sighand;
struct sigpending pending;
void *stack; // 指向核心棧的指針
...
}
程序運作在核心态時,需要相應的堆棧資訊, 則linux kernel為每個程序都提供一個核心棧kernel stack.
2.1.1 thread_info
Linux通過slab動态生成task_struct,那麼在棧頂或棧底建立新的結構體thread_info即可,其中task指向其真正的task_struct結構體。
struct thread_info {
struct task_struct*task;//主要的程序描述符
struct exec_domain*exec_domain;
__u32flags;
__u32status;// 線程同步flags
__u32cpu;//目前cpu
intpreempt_count;
mm_segment_taddr_limit;
struct restart_block restart_block;
void __user*sysenter_return;
unsigned intsig_on_uaccess_error:1;
unsigned intuaccess_err:1;
};
2.2 程序狀态
程序結構體task_struct有一個成員state,代表的是程序的狀态。
程序所有可能的狀态定義在檔案kernel/include/linux/sched.h,
不同的linux版本略有不同,下面列舉最新Kernel的程序狀态值:
序号
狀态
縮寫
含義
1
TASK_RUNNING
R
正在運作或可運作
2
TASK_INTERRUPTIBLE
S
可中斷的休眠
3
TASK_UNINTERRUPTIBLE
D
不可中斷的休眠
4
__TASK_STOPPED
T
跟蹤狀态, 當程序接收到SIGSTOP等signal資訊
5
__TASK_TRACED
t
停止狀态,比如被debugger的ptrace()
6
EXIT_ZOMBIE
Z
僵屍狀态,即父程序還沒有執行waitpid()
7
EXIT_DEAD
X
死亡狀态
說明:
R狀态: 分為正在執行和RQ隊列等待執行兩種狀态,該狀态是唯一可執行的狀态;
D狀态:不影響任何信号,如果分析過一些系統凍屏/當機重新開機的案例,會發現很多時候是由于某個程序異常處于D狀态而導緻系統blocked。
即便如此,也有其存在的價值,比如當程序打開裝置驅動檔案時,在驅動程式執行完成之前是
不希望被打斷的,可能會出現不可預知的狀态。
Z狀态:出現這個狀态往往是父程序沒有執行waitpid()或wait4()系統調用,
在這種情況下,核心不會丢棄該死亡程序的資訊,系統無法判斷是父程序是否還需要該資訊。
程序狀态轉換圖:
2.3 程序pid
pid最大值預設為32768,一般來說pid數值越大的程序建立時間越晚,但程序再不斷建立與結束,輪完一圈又會繼續從小開始輪循,是以也就破壞了這個規則。可以通過修改/proc/sys/kernel/pid_max來提高上限。
其他相關ID:
Tgid: 線程組的ID,一個線程一定屬于一個線程組(程序組).
Pid: 程序的ID,更準确的說應該是線程的ID
PPid: 目前程序的父程序
另外,每個程序的資源是有上限,可通過cat /proc//limits檢視
2.4 程序建立
Linux程序建立: 通過fork()系統調用建立程序
Linux使用者級線程建立:通過pthread庫中的pthread_create()建立線程
Linux核心線程建立: 通過kthread_create()建立核心線程
核心線程:沒有獨立的位址空間,即mm指向NULL。這樣的線程隻在核心運作,不會切換到使用者空間。
所有核心線程都是由kthreadd作為核心線程的祖師爺,衍生而來的。
微信公衆号
Gityuan
| 微網誌
weibo.com/gityuan
| 部落格
留言區交流