進程,調度算法,task_struct結構體

一.進程的調度算法

  1. 先來先服務 (FCFS,first come first served) 
    在所有調度算法中,最簡單的是非搶佔式的FCFS算法。 
    算法原理:進程按照它們請求CPU的順序使用CPU.就像你買東西去排隊,誰第一個排,誰就先被執行,在它執行的過程中,不會中斷它。當其他人也想進入內存被執行,就要排隊等着,如果在執行過程中出現一些事,他現在不想排隊了,下一個排隊的就補上。此時如果他又想排隊了,只能站到隊尾去。 
    算法優點:易於理解且實現簡單,只需要一個隊列(FIFO),且相當公平 
    算法缺點:比較有利於長進程,而不利於短進程,有利於CPU 繁忙的進程,而不利於I/O 繁忙的進程

  2. 最短作業優先(SJF, Shortest Job First) 
    短作業優先(SJF, Shortest Job First)又稱爲“短進程優先”SPN(Shortest Process Next);這是對FCFS算法的改進,其目標是減少平均週轉時間。 
    算法原理:對預計執行時間短的進程優先分派處理機。通常後來的短進程不搶先正在執行的進程。 
    算法優點:相比FCFS 算法,該算法可改善平均週轉時間和平均帶權週轉時間,縮短進程的等待時間,提高系統的吞吐量。 
    算法缺點:對長進程非常不利,可能長時間得不到執行,且未能依據進程的緊迫程度來劃分執行的優先級,以及難以準確估計進程的執行時間,從而影響調度性能。

  3. 最高響應比優先法(HRRN,Highest Response Ratio Next) 
    最高響應比優先法(HRRN,Highest Response Ratio Next)是對FCFS方式和SJF方式的一種綜合平衡。FCFS方式只考慮每個作業的等待時間而未考慮執行時間的長短,而SJF方式只考慮執行時間而未考慮等待時間的長短。因此,這兩種調度算法在某些極端情況下會帶來某些不便。HRN調度策略同時考慮每個作業的等待時間長短和估計需要的執行時間長短,從中選出響應比最高的作業投入執行。這樣,即使是長作業,隨着它等待時間的增加,W / T也就隨着增加,也就有機會獲得調度執行。這種算法是介於FCFS和SJF之間的一種折中算法。 
    算法原理:響應比R定義如下: R =(W+T)/T = 1+W/T 
    其中T爲該作業估計需要的執行時間,W爲作業在後備狀態隊列中的等待時間。每當要進行作業調度時,系統計算每個作業的響應比,選擇其中R最大者投入執行。 
    算法優點:由於長作業也有機會投入運行,在同一時間內處理的作業數顯然要少於SJF法,從而採用HRRN方式時其吞吐量將小於採用SJF 法時的吞吐量。 
    算法缺點:由於每次調度前要計算響應比,系統開銷也要相應增加。

  4. 時間片輪轉算法(RR,Round-Robin) 
    該算法採用剝奪策略。時間片輪轉調度是一種最古老,最簡單,最公平且使用最廣的算法,又稱RR調度。每個進程被分配一個時間段,稱作它的時間片,即該進程允許運行的時間。 
    算法原理:讓就緒進程以FCFS 的方式按時間片輪流使用CPU 的調度方式,即將系統中所有的就緒進程按照FCFS 原則,排成一個隊列,每次調度時將CPU 分派給隊首進程,讓其執行一個時間片,時間片的長度從幾個ms 到幾百ms。在一個時間片結束時,發生時鐘中斷,調度程序據此暫停當前進程的執行,將其送到就緒隊列的末尾,並通過上下文切換執行當前的隊首進程,進程可以未使用完一個時間片,就出讓CPU(如阻塞)。 
    算法優點:時間片輪轉調度算法的特點是簡單易行、平均響應時間短。 
    算法缺點:不利於處理緊急作業。在時間片輪轉算法中,時間片的大小對系統性能的影響很大,因此時間片的大小應選擇恰當 

  5. 怎樣確定時間片的大小:

    時間片大小的確定 
    1.系統對響應時間的要求 
    2.就緒隊列中進程的數目 
    3.系統的處理能力

  6. 多級反饋隊列(Multilevel Feedback Queue) 
    多級反饋隊列調度算法是一種CPU處理機調度算法,UNIX操作系統採取的便是這種調度算法。 
    多級反饋隊列調度算法描述: 
      1、進程在進入待調度的隊列等待時,首先進入優先級最高的Q1等待。 
      2、首先調度優先級高的隊列中的進程。若高優先級中隊列中已沒有調度的進程,則調度次優先級隊列中的進程。例如:Q1,Q2,Q3三個隊列,只有在Q1中沒有進程等待時纔去調度Q2,同理,只有Q1,Q2都爲空時纔會去調度Q3。 
      3、對於同一個隊列中的各個進程,按照時間片輪轉法調度。比如Q1隊列的時間片爲N,那麼Q1中的作業在經歷了N個時間片後若還沒有完成,則進入Q2隊列等待,若Q2的時間片用完後作業還不能完成,一直進入下一級隊列,直至完成。 
      4、在低優先級的隊列中的進程在運行時,又有新到達的作業,那麼在運行完這個時間片後,CPU馬上分配給新到達的作業(搶佔式)。 

      在多級反饋隊列調度算法中,如果規定第一個隊列的時間片略大於多數人機交互所需之處理時間時,便能夠較好的滿足各種類型用戶的需要。

二.task_struct結構體

  1. 在linux中描述進程的結構體叫做task_struct,它是linux內核的一種數據結構,會被裝載在RAM(內存)中幷包含着進程的信息。

  2. 那什麼是進程呢? 進程就是程序動態運行的實例,它是承擔分配系統資源的實體。我們也可以把進程當成是由一組元素組成的實體,進程的兩個基本的元素時程序代碼和與代碼相關聯的數據集合。在進程執行時,都可以被表徵爲一下元素(task_struct內容分類):

  3.     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爲1的進程信息,你需要查看 /proc/1 這個文件夾。大多數進信息同樣可以使用top和ps這些用戶級工具來獲取

  4. 殭屍進程和孤兒進程
  1. //殭屍進程代碼
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    
    int main(){
            pid_t pid=fork();
            if(pid<0){
                    perror("fork");
                    return 1;
    }
            else if(pid>0){
                    printf("parent[%d] is sleeping...\n",getpid());
                    sleep(20);
    }
            else{
                    printf("child[%d] is begin Z...\n",getpid());
                    sleep(5);
                    exit(1);
    }
            return 0;
    }
    //孤兒進程代碼
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    
    int main(){
            pid_t pid=fork();
            if(pid<0){
                    perror("fork");
                    return 1;
    }
            else if(pid==0){
                    printf("I am child,pid: %d\n",getpid());
                    sleep(10);
    }
            else{
                    printf("I am parent,pid :%d\n",getpid());
                    sleep(2);
                    exit(0);
    }
            return 0;
    }

三.淺析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

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;  
endif  

 成員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;
};

參考鏈接http://blog.csdn.net/peiyao456/article/details/54407343

 

 

 

 

 

    1.  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章