目錄
【Linux kernel】 task_struct解析
1.首先什麼是進程?
1.1進程可以這樣描述:
- 1>進程是程序的一個執行實例;
- 2>進程是正在執行的程序;
- 3>進程是能分配處理器並由處理器執行的實體。
- 按內核觀點來談進程:它擔當分配系統資源(CPU時間,內存)的實體。
1.2進程的兩個基本元素
- 一是程序代碼(可能被執行相同程序的其他進程共享)
- 二是和代碼相關聯的數據集。這裏的“和代碼相關聯的數據集指的是數據段和進程控制塊”。進程是一種動態描述,但是並不代表所有的進程都在運行。(進程在內存中因策略或調度需求,會處於各種狀態)。
1.3這裏再明確以下幾點:
- (1)操作系統執行程序的過程:
- (2)進程是動態運行的實例,但是並不是所有的進程都在運行,這前後兩句話並不矛盾,因爲程序被加載到內存中後,它的執行由進程的狀態以及調度算法等來決定。
2.進程的描述
PCB到底是用來幹什麼的呢?
- 學了操作系統後,我們都知道操作系統要想管理一個對象,它不是直接進行管理的,而是通過得到被管理者的一些有效信息加以管理的,因此在這裏我們也可以這樣理解,操作系統管理進程,實則是將進程的有效信息提取出來然後通過管理這些信息來管理進程,而所有的進程信息被存放在一個叫做進程控制塊的數據結構中(可以理解爲進程屬性的集合),這也就是即將要介紹的進程控制塊(PCB).
- 每個進程在內核中都有一個進程控制塊(PCB)來維護進程相關的信息,Linux內核的進程控制塊是task_struct結構體.
-
task_struct是Linux內核的一種數據結構,它會被裝載到RAM中並且包含着進程的信息。每個進程都把它的信息放在 task_struct 這個數據結構體,task_struct 包含了這些內容:
- (1)標示符 : 描述本進程的唯一標識符,用來區別其他進程。
- (2)狀態 :任務狀態,退出代碼,退出信號等。
- (3)優先級 :相對於其他進程的優先級。
- (4)程序計數器:程序中即將被執行的下一條指令的地址。
- (5)內存指針:包括程序代碼和進程相關數據的指針,還有和其他進程共享的內存塊的指針。
- (6)上下文數據:進程執行時處理器的寄存器中的數據。
- (7) I/O狀態信息:包括顯示的I/O請求,分配給進程的I/O設備和被進程使用的文件列表。
- (8) 記賬信息:可能包括處理器時間總和,使用的時鐘數總和,時間限制,記賬號等。
-
有關進程信息還有以下三點需要了解:
- 1>保存進程信息的數據結構叫做 task_struct,可以在 include/linux/sched.h 中找到它;
- 2>所有運行在系統中的進程都以 task_struct 鏈表的形式存在內核中;
- 3>進程的信息可以通過 /proc 系統文件夾查看。要獲取PID爲400的進程信息,你需要查看 /proc/400 這個文件夾。大多數進程信息同樣可以使用top和ps這些用戶級工具來獲取
3.剖析task_struct結構體
(1)進程的狀態(volatile long state)
state的可能取值爲:
#define TASK_RUNNING 0//進程要麼正在執行,要麼準備執行
#define TASK_INTERRUPTIBLE 1 //可中斷的睡眠,可以通過一個信號喚醒
#define TASK_UNINTERRUPTIBLE 2 //不可中斷睡眠,不可以通過信號進行喚醒
#define __TASK_STOPPED 4 //進程停止執行
#define __TASK_TRACED 8 //進程被追蹤
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16 //殭屍狀態的進程,表示進程被終止,但是父進程還沒有獲取它的終止信息,比如進程有沒有執行完等信息。
#define EXIT_DEAD 32 //進程的最終狀態,進程死亡
/* in tsk->state again */
#define TASK_DEAD 64 //死亡
#define TASK_WAKEKILL 128 //喚醒並殺死的進程
#define TASK_WAKING 256 //喚醒進程
(2)進程的唯一標識(pid)
pid_t pid;//進程的唯一標識
pid_t tgid;// 線程組的領頭線程的pid成員的值
- 在Linux系統中,一個線程組中的所有線程使用和該線程組的領頭線程(該組中的第一個輕量級進程)相同的PID,並被存放在tgid成員中。只有線程組的領頭線程的pid成員纔會被設置爲與tgid相同的值。注意,getpid()系統調用返回的是當前進程的tgid值而不是pid值。(線程是程序運行的最小單位,進程是程序運行的基本單位。)
(3)進程的標記:(unsigned int flags)
//flags成員的可能取值如下
//進程的標誌信息
#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */
/* Not implemented yet, only for 486*/
#define PF_STARTING 0x00000002 /* being created */
#define PF_EXITING 0x00000004 /* getting shut down */
#define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */
#define PF_VCPU 0x00000010 /* I'm a virtual CPU */
#define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */
#define PF_MCE_PROCESS 0x00000080 /* process policy on mce errors */
#define PF_SUPERPRIV 0x00000100 /* used super-user privileges */
#define PF_DUMPCORE 0x00000200 /* dumped core */
#define PF_SIGNALED 0x00000400 /* killed by a signal */
#define PF_MEMALLOC 0x00000800 /* Allocating memory */
#define PF_FLUSHER 0x00001000 /* responsible for disk writeback */
#define PF_USED_MATH 0x00002000 /* if unset the fpu must be initialized before use */
#define PF_FREEZING 0x00004000 /* freeze in progress. do not account to load */
#define PF_NOFREEZE 0x00008000 /* this thread should not be frozen */
#define PF_FROZEN 0x00010000 /* frozen for system suspend */
#define PF_FSTRANS 0x00020000 /* inside a filesystem transaction */
#define PF_KSWAPD 0x00040000 /* I am kswapd */
#define PF_OOM_ORIGIN 0x00080000 /* Allocating much memory to others */
#define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */
#define PF_KTHREAD 0x00200000 /* I am a kernel thread */
#define PF_RANDOMIZE 0x00400000 /* randomize virtual address space */
#define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */
#define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */
#define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */
#define PF_THREAD_BOUND 0x04000000 /* Thread bound to specific cpu */
#define PF_MCE_EARLY 0x08000000 /* Early kill for mce process policy */
#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */
#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */
#define PF_FREEZER_NOSIG 0x80000000 /* Freezer won't send signals to it */
(4)進程之間的親屬關係:
struct task_struct *real_parent; /* real parent process */
struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */
- 在Linux系統中,所有進程之間都有着直接或間接地聯繫,每個進程都有其父進程,也可能有零個或多個子進程。擁有同一父進程的所有進程具有兄弟關係。
- real_parent指向其父進程,如果創建它的父進程不再存在,則指向PID爲1的init進程。
- parent指向其父進程,當它終止時,必須向它的父進程發送信號。它的值通常與 real_parent相同。
- children表示鏈表的頭部,鏈表中的所有元素都是它的子進程(進程的子進程鏈表)。
- sibling用於把當前進程插入到兄弟鏈表中(進程的兄弟鏈表)。
- group_leader指向其所在進程組的領頭進程。
(5)進程調度信息:
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;
- 實時優先級範圍是0到MAX_RT_PRIO-1(即99),而普通進程的靜態優先級範圍是從MAX_RT_PRIO到MAX_PRIO-1(即100到139)。值越大靜態優先級越低。
- 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(輪流調度算法)都是實時調度策略.
(6)ptrace系統調用
unsigned int ptrace;
struct list_head ptraced;
struct list_head ptrace_entry;
unsigned long ptrace_message;
siginfo_t *last_siginfo; /* For ptrace use. */
ifdef CONFIG_HAVE_HW_BREAKPOINT
atomic_t ptrace_bp_refcnt;
- 成員ptrace被設置爲0時表示不需要被跟蹤,它的可能取值如下:
/* linux-2.6.38.8/include/linux/ptrace.h */
#define PT_PTRACED 0x00000001
#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */
#define PT_TRACESYSGOOD 0x00000004
#define PT_PTRACE_CAP 0x00000008 /* ptracer can follow suid-exec */
#define PT_TRACE_FORK 0x00000010
#define PT_TRACE_VFORK 0x00000020
#define PT_TRACE_CLONE 0x00000040
#define PT_TRACE_EXEC 0x00000080
#define PT_TRACE_VFORK_DONE 0x00000100
#define PT_TRACE_EXIT 0x00000200
-
(7) 時間數據成員
- 關於時間,一個進程從創建到終止叫做該進程的生存期,進程在其生存期內使用CPU時間,內核都需要進行記錄,進程耗費的時間分爲兩部分,一部分是用戶模式下耗費的時間,一部分是在系統模式下耗費的時間.
cputime_t utime, stime, utimescaled, stimescaled;
cputime_t gtime;
cputime_t prev_utime, prev_stime;//記錄當前的運行時間(用戶態和內核態)
unsigned long nvcsw, nivcsw; //自願/非自願上下文切換計數
struct timespec start_time; //進程的開始執行時間
struct timespec real_start_time; //進程真正的開始執行時間
unsigned long min_flt, maj_flt;
struct task_cputime cputime_expires;//cpu執行的有效時間
struct list_head cpu_timers[3];//用來統計進程或進程組被處理器追蹤的時間
struct list_head run_list;
unsigned long timeout;//當前已使用的時間(與開始時間的差值)
unsigned int time_slice;//進程的時間片的大小
int nr_cpus_allowed
(8)信號處理信息
struct signal_struct *signal;//指向進程信號描述符
struct sighand_struct *sighand;//指向進程信號處理程序描述符
sigset_t blocked, real_blocked;//阻塞信號的掩碼
sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
struct sigpending pending;//進程上還需要處理的信號
unsigned long sas_ss_sp;//信號處理程序備用堆棧的地址
size_t sas_ss_size;//信號處理程序的堆棧的地址
(9)文件系統信息
/* filesystem information */
struct fs_struct *fs;//文件系統的信息的指針
/* open file information */
struct files_struct *files;//打開文件的信息指針
- 以下是對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;