一、什麼是程序
程序,就是程式的一個執行執行個體,或正在執行的程式。
詳細介紹請看
點選打開連結
那麼程序在Linux中有幾種狀态呢?如下:
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;
};