天天看點

程序的狀态與task_struct結構體

一、什麼是程序

程序,就是程式的一個執行執行個體,或正在執行的程式。

詳細介紹請看

​​點選打開連結​​

那麼程序在Linux中有幾種狀态呢?如下:

程式的狀态與task_struct結構體

1、R

處于運作或可運作狀态,即程序正在運作或在運作隊列(可執行隊列)中等待。隻有在該狀态的程序才可能在CPU上運作,同一時刻可能有多個程序處于該狀态。

(注:很多教科書上将正在CPU上執行的程序的狀态定義為Running,将可執行但尚未被排程執行的程序狀态定義為Ready,這2種狀态在Linux下統一為R狀态)

2、S

處于可中斷的睡眠狀态,即程序在休眠中,由于在等待某個事件的完成(或等待某個條件的形成或等待某個信号等)

(注:等待socket連接配接、等待信号量等)而被挂起;當這些事件發生時,對應的等待隊列中的一個或多個程序将被喚醒。一般情況下,程序清單中絕大多數程序都處于該狀态。

3、D

處于不可中斷的睡眠狀态,不可中斷指的并不是CPU不響應外部硬體的中斷,而是指程序不響應異步信号,無法用kill指令殺死,程序必須等待直到有中斷發生。

4、T

處于暫停或跟蹤狀态。程序收到SIGSTOP、SIGSTP、SIGTIN、SIGTOU等信号進入暫停狀态(除非程序處于不可中斷的睡眠狀态);當接着向程序發送1個SIGCONT信号,程序可以從暫停狀态恢複到運作或能運作狀态。

當程序被跟蹤時,它處于被跟蹤狀态。“被跟蹤”指程序暫停下來,等待跟蹤它的程序對它進行操作。例如在GDB調試中,對被跟蹤的程序設定某個斷點,程序執行到斷點處停下來的時候就處于被跟蹤狀态。

暫停與跟蹤狀态還是有差別的,被跟蹤狀态相當于在暫停狀态之上多了一層保護,處于被跟蹤狀态的程序不能響應SIGCONT信号而被喚醒,隻能等到調試程序通過ptrace系統調用執行ptrace_cont、ptrace_detach等操作(通過ptrace系統調用的參數指定操作),或調試程序退出,被調試的程序才能恢複到R狀态。

5、Z

處于僵死狀态,也稱退出狀态。它指程序已經結束,放棄了幾乎所有的記憶體空間,沒有任何可執行代碼,也不能被排程,僅僅在程序清單中保留一個位置來記載該程序的退出狀态等資訊(task_struct結構體[儲存了該程序的退出碼])供其他程序收集。

6、X

程序在退出過程中可能不會保留它的task_struct。例如某個程序是多線程程式中被detach過的程序;或者父程序通過設定SIGCHLD信号的Handler為SIG_IGN,顯示的忽略了SIGCHLD信号。

此時該程序被置于exit_dead退出狀态,這意味着接下來的代碼立即會将該程序徹底釋放。故exit_dead狀态非常短暫,幾乎不可能通過ps指令捕捉到。

二、模拟實作僵屍程序與孤兒程序

1、僵屍程序

Makefile檔案:

1 
  2 .PHONY:jiangshi
  3 
  4 jiangshi:jiangshi.c
  5     gcc -o $@ $^
  6 
  7 .PHONY:clean
  8 clean:
  9     rm -f jiangshi      

jiangshi.c

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 int main(){
  4         pid_t id=fork();
  5         if(id<0){
  6                 perror("fork");
  7                 return 1;
  8         }
  9         else if(id>0){//parent
 10                 printf("parent[%d] is sleeping...\n",getpid());
 11                 sleep(30);
 12             }
 13         else{
 14             printf("child[%d] is begin Z...\n",getpid());
 15             sleep(5);
 16             exit(EXIT_SUCCESS);
 17         }
 18         return 0;
 19     }      

2、孤兒程序

Makefile檔案

1 
  2 
  3 .PHONY:guer
  4 
  5 guer:guer.c
  6     gcc -o $@ $^
  7 
  8 .PHONY:clean
  9 clean:
 10     rm -f guer      

guer.c

1 #include<unistd.h>
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 int main(){
  5         pid_t id=fork();
  6         if(id<0){
  7                 perror("fork");
  8                 return 1;
  9         }
 10             else if(id==0){//child
 11                 printf("I am child,pid:%d\n",getpid());
 12                 sleep(10);
 13             }
 14         else{//parent
 15             printf("I am parent,pid:%d\n",getpid());
 16             sleep(3);
 17             exit(0);
 18         }
 19         return 0;
 20     }      

三、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值

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;
};​​      

繼續閱讀