天天看點

linux程序實作,Linux程序管理(一)

一. 概述

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)。

linux程式實作,Linux程式管理(一)

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()系統調用,

在這種情況下,核心不會丢棄該死亡程序的資訊,系統無法判斷是父程序是否還需要該資訊。

程序狀态轉換圖:

linux程式實作,Linux程式管理(一)

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

| 部落格

留言區交流