程序控制塊: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;
};