Linux进程模型
????? ----原文鏈接:http://www.cnblogs.com/biyeymyhjob/archive/2012/08/01/2617884.html------
Linux進程通過一個task_struct結構體描述,在linux/sched.h中定義,通過理解該結構,可更清楚的理解linux進程模型。 ? ??? 包含進程所有信息的task_struct數據結構是比較龐大的,但是該數據結構本身并不復雜,我們將它的所有域按其功能可做如下劃分:
- 進程狀態(State)
- 進程調度信息(Scheduling Information)
- 各種標識符(Identifiers)
- 進程通信有關信息(IPC:Inter_Process Communication)
- 時間和定時器信息(Times and Timers)
- 進程鏈接信息(Links)
- 文件系統信息(File System)
- 虛擬內存信息(Virtual Memory)
- 頁面管理信息(page)
- 對稱多處理器(SMP)信息
- 和處理器相關的環境(上下文)信息(Processor Specific Context)
- 其它信息
?1. 進程狀態(State)
進程執行時,它會根據具體情況改變狀態。進程狀態是調度和對換的依據。Linux中的進程主要有如下狀態,如表1所示。
?
表3.1? Linux進程的狀態
內核表示 | 含義 |
TASK_RUNNING | 可運行 |
TASK_INTERRUPTIBLE | 可中斷的等待狀態 |
TASK_UNINTERRUPTIBLE | 不可中斷的等待狀態 |
TASK_ZOMBIE | 僵死 |
TASK_STOPPED | 暫停 |
TASK_SWAPPING | 換入/換出 |
?
1).可運行狀態:處于這種狀態的進程,要么正在運行、要么正準備運行。正在運行的進程就是當前進程(由current所指向的進程),而準備運行的進程只要得到CPU就可以立即投入運行,CPU是這些進程唯一等待的系統資源。
2).等待狀態:處于該狀態的進程正在等待某個事件(event)或某個資源,它肯定位于系統中的某個等待隊列(wait_queue)中。
3).暫停狀態:此時的進程暫時停止運行來接受某種特殊處理。通常當進程接收到SIGSTOP、SIGTSTP、SIGTTIN或 SIGTTOU信號后就處于這種狀態。例如,正接受調試的進程就處于這種狀態。
4).僵死狀態:進程雖然已經終止,但由于某種原因,父進程還沒有執行wait()系統調用,終止進程的信息也還沒有回收。顧名思義,處于該狀態的進程就是死進程,這種進程實際上是系統中的垃圾,必須進行相應處理以釋放其占用的資源。
進程狀態轉換如下圖:
2.進程調度信息
??? 調度程序利用這部分信息決定系統中哪個進程最應該運行,并結合進程的狀態信息保證系統運轉的公平和高效。這一部分信息通常包括進程的類別(普通進程還是實時進程)、進程的優先級等。表2描述了跟進程調度有關的字段,表3.3說明了幾種常用的進程調度算法及這些算法的使用范圍,如先來先服務主要用于實時進程的調度。
表2 進程調度信息
域名 | 含義 |
need_resched | 調度標志 |
Nice | 靜態優先級 |
Counter | 動態優先級 |
Policy | 調度策略 |
rt_priority | 實時優先級 |
?
表3? 進程調度的策略
名稱 | 解釋 | 適用范圍 |
SCHED_OTHER | 其他調度 | 普通進程 |
SCHED_FIFO | 先來先服務調度 | 實時進程 |
SCHED_RR | 時間片輪轉調度 |
?
只有root用戶能通過sched_setscheduler()系統調用來改變調度策略。
?
3.標識符(Identifiers)
?? 每個進程有進程標識符、用戶標識符、組標識符,如表4所示。
?? 不管對內核還是普通用戶來說,怎么用一種簡單的方式識別不同的進程呢?這就引入了進程標識符(PID:process identifier),每個進程都有一個唯一的標識符,內核通過這個標識符來識別不同的進程,同時,進程標識符PID也是內核提供給用戶程序的接口,用戶程序通過PID對進程發號施令。PID是32位的無符號整數,它被順序編號:新創建進程的PID通常是前一個進程的PID加1。然而,為了與16位硬件平臺的傳統Linux系統保持兼容,在Linux上允許的最大PID號是32767,當內核在系統中創建第32768個進程時,就必須重新開始使用已閑置的PID號。
表4 各種標識符
域名 | 含義 |
Pid | 進程標識符 |
Uid、gid | 用戶標識符、組標識符 |
Euid、egid | 有效用戶標識符、有效組標識符 |
Suid、sgid | 備份用戶標識符、備份組標識符 |
Fsuid、fsgid | 文件系統用戶標識符、文件系統組標識符 |
?
4.進程通信有關信息(IPC:Inter_Process Communication)
??? 為了使進程能在同一項任務上協調工作,進程之間必須能進行通信即交流數據。
????Linux支持多種不同形式的通信機制。它支持典型的Unix 通信機制(IPC Mechanisms):信號(Signals)、管道(Pipes),也支持System V 通信機制:共享內存(Shared Memory)、信號量和消息隊列(Message Queues),如表5
表5 進程通信有關信息
域名 | 含義 |
Spinlock_t sigmask_lock | 信號掩碼的自旋鎖 |
Long blocked | 信號掩碼 |
Struct signal? *sig | 信號處理函數 |
Struct sem_undo *semundo | 為避免死鎖而在信號量上設置的取消操作 |
Struct sem_queue *semsleeping | 與信號量操作相關的等待隊列 |
?
?
5.進程鏈接信息(Links)
??? 程序創建的進程具有父/子關系。因為一個進程能創建幾個子進程,而子進程之間有兄弟關系,在task_struct結構中有幾個域來表示這種關系。
????在Linux系統中,除了初始化進程init,其他進程都有一個父進程(parent process)或稱為雙親進程。可以通過fork()或clone()系統調用來創建子進程,除了進程標識符(PID)等必要的信息外,子進程的task_struct結構中的絕大部分的信息都是從父進程中拷貝,或說“克隆”過來的。系統有必要記錄這種“親屬”關系,使進程之間的協作更加方便,例如父進程給子進程發送殺死(kill)信號、父子進程通信等,就可以用這種關系很方便地實現。
????每個進程的task_struct結構有許多指針,通過這些指針,系統中所有進程的task_struct結構就構成了一棵進程樹,這棵進程樹的根就是初始化進程init的task_struct結構(init進程是Linux內核建立起來后人為創建的一個進程,是所有進程的祖先進程)。表6是進程所有的鏈接信息。
表6 進程鏈接信息
名稱 | 解釋 [指向哪個進程] |
p_opptr | 祖先 |
p_pptr | 父進程 |
p_cptr | 子進程 |
p_ysptr | 弟進程 |
p_osptr | 兄進程 |
Pidhash_next、 Pidhash_pprev | 進程在哈希表中的鏈接 |
Next_task、 prev_task | 進程在雙向循環鏈表中的鏈接 |
Run_list | 運行隊列的鏈表 |
?
6.時間和定時器信息(Times and Timers)
??? 一個進程從創建到終止叫做該進程的生存期(lifetime)。進程在其生存期內使用CPU的時間,內核都要進行記錄,以便進行統計、計費等有關操作。進程耗費CPU的時間由兩部分組成:一是在用戶模式(或稱為用戶態)下耗費的時間、一是在系統模式(或稱為系統態)下耗費的時間。每個時鐘滴答,也就是每個時鐘中斷,內核都要更新當前進程耗費CPU的時間信息。
??? 表7是和時間有關的域,上面所說的counter是指進程剩余的CPU時間片,也和時間有關,所以這里我們再次提及它。表8是進程的所有定時器。
?
表7與時間有關的域
域名 | 含義 |
Start_time | 進程創建時間 |
Per_cpu_utime | 進程在某個CPU上運行時在用戶態下耗費的時間 |
Per_cpu_stime | 進程在某個CPU上運行時在系統態下耗費的時間 |
Counter | 進程剩余的時間片 |
?
表8? 進程的所有定時器
?
定時器類型 | 解釋 | 什么時候更新 | 用來表示此種定時器的域 |
ITIMER_REAL | 實時定時器 | 實時更新,即不論該進程是否運行 | it_real_value |
it_real_incr | |||
real_timer | |||
ITIMER_VIRTUAL | 虛擬定時器 | 只在進程運行于用戶態時更新 | it_virt_value |
it_virt_incr | |||
ITIMER_PROF | 概況定時器 | 進程運行于用戶態和系統態時更新 | it_prof_value |
it_prof_incr |
?
7.文件系統信息(File System)
?? 進程可以打開或關閉文件,文件屬于系統資源,Linux內核要對進程使用文件的情況進行記錄。task_struct結構中有兩個數據結構用于描述進程與文件相關的信息。其中,fs_struct中描述了兩個VFS索引節點(VFS inode),這兩個索引節點叫做root和pwd,分別指向進程的可執行映象所對應的根目錄(home directory)和當前目錄或工作目錄。file_struct結構用來記錄了進程打開的文件的描述符(descriptor)。如表9所示。
表9? 與文件系統相關的域
定義形式 | 解釋 |
Sruct fs_struct *fs | 進程的可執行映象所在的文件系統 |
Struct files_struct *files | 進程打開的文件 |
??? 在文件系統中,每個VFS索引節點唯一描述一個文件或目錄,同時該節點也是向更低層的文件系統提供的統一的接口。
?
8.虛擬內存信息(Virtual Memory)
???除了內核線程(kernel thread),每個進程都擁有自己的地址空間(也叫虛擬空間),用mm_struct來描述。另外Linux2.4還引入了另外一個域active_mm,這是為內核線程而引入。因為內核線程沒有自己的地址空間,為了讓內核線程與普通進程具有統一的上下文切換方式,當內核線程進行上下文切換時,讓切換進來的線程的active_mm指向剛被調度出去的進程的active_mm(如果進程的mm域不為空,則其active_mm域與mm域相同)。內存信息如表10所示
表10?? 虛擬內存描述信息
定義形式 | 解釋 |
Struct mm_struct *mm | 描述進程的地址空間 |
Struct mm_struct *active_mm | 內核線程所借用的地址空間 |
?
9.頁面管理信息
?? 當物理內存不足時,Linux內存管理子系統需要把內存中的部分頁面交換到外存,其交換是以頁為單位的。有關頁面的描述信息如表11。
表11 頁面管理信息
? 定義形式 | 解釋 |
Int swappable | 進程占用的內存頁面是否可換出 |
Unsigned long min_flat, maj_flt,nswap | 進程累計的次(minor)缺頁次數、 主(major)次數及累計換出、換入頁面數 |
Unsigned long cmin_flat, cmaj_flt,cnswap | 本進程作為祖先進程,其所有層次子進程的累計的次(minor)缺頁次數、主(major)次數及累計換出、換入頁面數 |
?
10.進程內核棧及current宏
??? 在Linux-2.6內核中堆棧這么定義:
???? 根據內核的配置,THREAD_SIZE既可以是4K字節(1個頁面)也可以是8K字節(2個頁面)。thread_info是52個字節長。下圖是當設為8KB時候的內核堆棧:Thread_info在這個內存區的開始處,內核堆棧從末端向下增長。進程描述符不是在這個內存區中,而分別通過task與thread_info指針使thread_info與進程描述符互聯。所以獲得當前進程描述符的current定義如下:?
#define current get_current() static inline struct task_struct * get_current(void) {return current_thread_info()->task; } static inline struct thread_info *current_thread_info(void) {struct thread_info *ti;__asm__("andl %%esp,%0; ":"=r" (ti) : "" (~(THREAD_SIZE - 1)));return ti; }
根據THREAD_SIZE大小,分別屏蔽掉內核棧的12-bit LSB(4K)或13-bit LSB(8K),從而獲得內核棧的起始位置,及當前進程描述符的指針。
總結
- 上一篇: linux distribution t
- 下一篇: 走进移动web开发的四大框架