1. 程式人生 > >程序控制塊:PCB之task_struct

程序控制塊:PCB之task_struct

程序就是程式動態執行的例項,是承擔分配系統資源的實體
程序資訊被存放在一個叫程序控制塊的資料結構中,將其稱之為PCB。而Linux作業系統下的task_struct是PCB的一種,task_struct是Linux核心的一種資料結構,他會被裝載到記憶體裡並儲存著程序的資訊
資訊分類如下:

狀態:描述程序的狀態,因為程序有掛起,阻塞,執行等好幾個狀態,所以都有個識別符號來記錄程序的執行狀態。
記憶體指標:程式程式碼和程序相關資料的指標。
程式計數器:程式中即將被執行的下一條指令的地址。
上下文資料:程序執行時處理器的暫存器中的資料。
記賬資訊:包括處理器的時間總和,記賬號等等。
識別符號:與程序相關的唯一識別符號,用來區別正在執行的程序和其他程序。
優先順序:如果有好幾個程序正在執行,就涉及到程序被執行的先後順序的問題,這和程序優先順序這個識別符號有關。
I/O狀態資訊:包括顯示的I/O請求,分配給程序的I/O裝置和程序使用的檔案列表等。

volatile long states;
狀態 常見的可能取值:

TASK_RUNNING:表示程序正在執行或者處於準備執行的狀態
TASK_INTERRUPTIBLE:程序因為等待某些條件處於阻塞(掛起的狀態),一旦等待的條件成立,程序便會從該狀態轉化成就緒狀態
TASK_UNINTERRUPTIBLE:意思與TASK_INTERRUPTIBLE類似,但是我們傳遞任意訊號等不能喚醒他們,只有它所等待的資源可用的時候,他才會被喚醒。
TASK_STOPPED:程序被停止執行
TASK_TRACED:程序被debugger等程序所監視。
EXIT_ZOMBIE:程序的執行被終止,但是其父程序還沒有使用wait()等系統呼叫來獲知它的終止資訊,此時程序成為殭屍程序
EXIT_DEAD:程序被殺死,即程序的最終狀態。
TASK_KILLABLE:當程序處於這種可以終止的新睡眠狀態中,它的執行原理類似於 TASK_UNINTERRUPTIBLE,只不過可以響應致命訊號

unsigned long flags;
程序標誌:

*PF_ALIGNWARN 列印“對齊”警告資訊。
*PF_PTRACED 被ptrace系統呼叫監控。
*PF_TRACESYS 正在跟蹤。
*PF_FORKNOEXEC 程序剛建立,但還沒執行。
*PF_SUPERPRIV 超級使用者特權。
*PF_DUMPCORE dumped core。
*PF_SIGNALED 程序被訊號(signal)殺出。
*PF_STARTING 程序正被建立。
*PF_EXITING 程序開始關閉。
*PF_USEDFPU 該程序使用FPU(SMP only)。
*PF_DTRACE delayed trace (used on m68k)。

程序優先順序,CPU分配資源的先後順序就是程序的優先權。優先權高的有優先執行的權利。使用ps -l命令可以檢視系統程序的相關資訊,我們重點會關注到以下幾個資訊:

UID:代表執行者的身份
PID:代表這個程序的代號
PPID:代表該程序是由哪一個程序發展而來的,即其父程序代號
PRI:代表該程序的優先順序,其值越小越先被執行
NI:代表該程序的nice值

NI:PRI值越小程序越先被執行,加入nice值後,PRI值就會被改變為
PRI(new) = PRI(old) + nice。nice取值的範圍為[-20,19]。當nice值為負值時,程序的優先順序值會減小,其對應的程序的優先順序更高所以更先被執行。所以說,Linux下想要更改程序的優先順序,只需要更改nice值即可。
修改優先順序命令:

啟動程序前調整:nice(例如:nice -n -2 ./test)
調整已存在程序nice:renice(例如:renice -3 -p 3267//PID為3267的程序的nice值設定為-3)
用top命令更改已存在程序的nice值:top進入以後輸入r然後輸入程序PID再輸入nice值

上下文資料 (重要)

  (1) struct desc_struct *ldt;
  程序關於CPU段式儲存管理的區域性描述符表的指標,用於模擬WINE Windows的程式。其他情況下取值NULL,程序的ldt就是arch/i386/traps.c定義的default_ldt。

  (2) struct thread_struct tss;
  任務狀態段,其內容與INTEL CPU的TSS對應,如各種通用暫存器.CPU排程時,當前執行程序的TSS儲存到PCB的tss,新選中程序的tss內容複製到CPU的TSS。結構定義在include/linux/tasks.h中。

  (3) unsigned long saved_kernel_stack;
  為MS-DOS的模擬程式(或叫系統呼叫vm86)儲存的堆疊指標。
  
  (4) unsigned long kernel_stack_page;
  在核心態執行時,每個程序都有一個核心堆疊,其基地址就儲存在kernel_stack_page中。
程序佇列指標
  (1) struct task_struct *next_task,*prev_task;
  所有程序(以PCB的形式)組成一個雙向連結串列。next_task和就是連結串列的前後指標。連結串列的頭和尾都是init_task(即0號程序)。

  (2) struct task_struct *next_run,*prev_run;
  由正在執行或是可以執行的,其程序狀態均為TASK_RUNNING的程序所組成的一個雙向迴圈連結串列,即run_queue就緒佇列。該連結串列的前後向指標用next_run和prev_run,連結串列的頭和尾都是init_task(即0號程序)。

  (3) struct task_struct *p_opptr,*p_pptr;和struct task_struct *p_cptr,*p_ysptr,*p_osptr;
  以上分別是指向原始父程序(original parent)、父程序(parent)、子程序(youngest child)及新老兄弟程序(younger sibling,older sibling)的指標。
程序標識

pid_t pid;//程序的唯一標識
pid_t tgid;//執行緒組的領頭執行緒的pid成員的值

在Linux系統中,一個執行緒組中的所有執行緒使用和該執行緒組的領頭執行緒(該組中的第一個輕量級程序)相同的PID,並被存放在tgid成員中。只有執行緒組的領頭執行緒的pid成員才會被設定為與tgid相同的值。注意,getpid()系統呼叫返回的是當前程序的tgid值而不是pid值。(執行緒是程式執行的最小單位,程序是程式執行的基本單位。)
程序排程

 int prio, static_prio, normal_prio;  
 unsigned int rt_priority;  
 const struct sched_class *sched_class;  
 struct sched_entity se;  
 struct sched_rt_entity rt;  
 unsigned int policy;

static_prio用於儲存靜態優先順序,可以通過nice系統呼叫來進行修改。
rt_priority用於儲存實時優先順序。
normal_prio的值取決於靜態優先順序和排程策略(程序的排程策略有:先來先服務,短作業優先、時間片輪轉、高響應比優先等等的排程演算法)
prio用於儲存動態優先順序。
policy表示程序的排程策略,目前主要有以下五種:

#define SCHED_NORMAL         0//按照優先順序進行排程(有些地方也說是CFS排程器)
#define SCHED_FIFO           1//先進先出的排程演算法  
#define SCHED_RR             2//時間片輪轉的排程演算法  
#define SCHED_BATCH          3//用於非互動的處理機消耗型的程序  
#define SCHED_IDLE           5//系統負載很低時的排程演算法  
#define SCHED_RESET_ON_FORK  0x40000000 

SCHED_NORMAL用於普通程序,通過CFS排程器實現;
SCHED_BATCH用於非互動的處理器消耗型程序;
SCHED_IDLE是在系統負載很低時使用;
SCHED_FIFO(先入先出排程演算法)和SCHED_RR(輪流排程演算法)都是實時排程策略.

附:
以下是對task_struct的定義及註釋:

struct task_struct {  
volatile long state;  //說明了該程序是否可以執行,還是可中斷等資訊  
unsigned long flags;  //Flage 是程序號,在呼叫fork()時給出  
int sigpending;    //程序上是否有待處理的訊號  
mm_segment_t addr_limit; //程序地址空間,區分核心程序與普通程序在記憶體存放的位置不同  
                        //0-0xBFFFFFFF for user-thead  
                        //0-0xFFFFFFFF for kernel-thread  
//排程標誌,表示該程序是否需要重新排程,若非0,則當從核心態返回到使用者態,會發生排程  
volatile long need_resched;  
int lock_depth;  //鎖深度  
long nice;       //程序的基本時間片  
//程序的排程策略,有三種,實時程序:SCHED_FIFO,SCHED_RR, 分時程序:SCHED_OTHER  
unsigned long policy;  
struct mm_struct *mm; //程序記憶體管理資訊  
int processor;  
//若程序不在任何CPU上執行, cpus_runnable 的值是0,否則是1 這個值在執行佇列被鎖時更新  
unsigned long cpus_runnable, cpus_allowed;  
struct list_head run_list; //指向執行佇列的指標  
unsigned long sleep_time;  //程序的睡眠時間  
//用於將系統中所有的程序連成一個雙向迴圈連結串列, 其根是init_task  
struct task_struct *next_task, *prev_task;  
struct mm_struct *active_mm;  
struct list_head local_pages;       //指向本地頁面        
unsigned int allocation_order, nr_local_pages;  
struct linux_binfmt *binfmt;  //程序所執行的可執行檔案的格式  
int exit_code, exit_signal;  
int pdeath_signal;     //父程序終止時向子程序傳送的訊號  
unsigned long personality;  
//Linux可以執行由其他UNIX作業系統生成的符合iBCS2標準的程式  
int did_exec:1;   
pid_t pid;    //程序識別符號,用來代表一個程序  
pid_t pgrp;   //程序組標識,表示程序所屬的程序組  
pid_t tty_old_pgrp;  //程序控制終端所在的組標識  
pid_t session;  //程序的會話標識  
pid_t tgid;  
int leader;     //表示程序是否為會話主管  
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;  
struct list_head thread_group;   //執行緒連結串列  
struct task_struct *pidhash_next; //用於將程序鏈入HASH表  
struct task_struct **pidhash_pprev;  
wait_queue_head_t wait_chldexit;  //供wait4()使用  
struct completion *vfork_done;  //供vfork() 使用  
unsigned long rt_priority; //實時優先順序,用它計算實時程序排程時的weight值  

//it_real_value,it_real_incr用於REAL定時器,單位為jiffies, 系統根據it_real_value  
//設定定時器的第一個終止時間. 在定時器到期時,向程序傳送SIGALRM訊號,同時根據  
//it_real_incr重置終止時間,it_prof_value,it_prof_incr用於Profile定時器,單位為jiffies。  
//當程序執行時,不管在何種狀態下,每個tick都使it_prof_value值減一,當減到0時,向程序傳送  
//訊號SIGPROF,並根據it_prof_incr重置時間.  
//it_virt_value,it_virt_value用於Virtual定時器,單位為jiffies。當程序執行時,不管在何種  
//狀態下,每個tick都使it_virt_value值減一當減到0時,向程序傳送訊號SIGVTALRM,根據  
//it_virt_incr重置初值。  
unsigned long it_real_value, it_prof_value, it_virt_value;  
unsigned long it_real_incr, it_prof_incr, it_virt_value;  
struct timer_list real_timer;   //指向實時定時器的指標  
struct tms times;      //記錄程序消耗的時間  
unsigned long start_time;  //程序建立的時間  
//記錄程序在每個CPU上所消耗的使用者態時間和核心態時間  
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];   
//記憶體缺頁和交換資訊:  
//min_flt, maj_flt累計程序的次缺頁數(Copy on Write頁和匿名頁)和主缺頁數(從對映檔案或交換  
//裝置讀入的頁面數); nswap記錄程序累計換出的頁面數,即寫到交換裝置上的頁面數。  
//cmin_flt, cmaj_flt, cnswap記錄本程序為祖先的所有子孫程序的累計次缺頁數,主缺頁數和換出頁面數。  
//在父程序回收終止的子程序時,父程序會將子程序的這些資訊累計到自己結構的這些域中  
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;  
int swappable:1; //表示程序的虛擬地址空間是否允許換出  
//程序認證資訊  
//uid,gid為執行該程序的使用者的使用者識別符號和組識別符號,通常是程序建立者的uid,gid  
//euid,egid為有效uid,gid  
//fsuid,fsgid為檔案系統uid,gid,這兩個ID號通常與有效uid,gid相等,在檢查對於檔案  
//系統的訪問許可權時使用他們。  
//suid,sgid為備份uid,gid  
uid_t uid,euid,suid,fsuid;  
gid_t gid,egid,sgid,fsgid;  
int ngroups; //記錄程序在多少個使用者組中  
gid_t groups[NGROUPS]; //記錄程序所在的組  
//程序的權能,分別是有效位集合,繼承位集合,允許位集合  
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;  
int keep_capabilities:1;  
struct user_struct *user;  
struct rlimit rlim[RLIM_NLIMITS];  //與程序相關的資源限制資訊  
unsigned short used_math;   //是否使用FPU  
char comm[16];   //程序正在執行的可執行檔名  
 //檔案系統資訊  
int link_count, total_link_count;  
//NULL if no tty 程序所在的控制終端,如果不需要控制終端,則該指標為空  
struct tty_struct *tty;  
unsigned int locks;  
//程序間通訊資訊  
struct sem_undo *semundo;  //程序在訊號燈上的所有undo操作  
struct sem_queue *semsleeping; //當程序因為訊號燈操作而掛起時,他在該佇列中記錄等待的操作  
//程序的CPU狀態,切換時,要儲存到停止程序的task_struct中  
struct thread_struct thread;  
  //檔案系統資訊  
struct fs_struct *fs;  
  //開啟檔案資訊  
struct files_struct *files;  
  //訊號處理函式  
spinlock_t sigmask_lock;  
struct signal_struct *sig; //訊號處理函式  
sigset_t blocked;  //程序當前要阻塞的訊號,每個訊號對應一位  
struct sigpending pending;  //程序上是否有待處理的訊號  
unsigned long sas_ss_sp;  
size_t sas_ss_size;  
int (*notifier)(void *priv);  
void *notifier_data;  
sigset_t *notifier_mask;  
u32 parent_exec_id;  
u32 self_exec_id;  

spinlock_t alloc_lock;  
void *journal_info;  
};