1. 程式人生 > >程序管理和排程

程序管理和排程

一、馮諾依曼體系結構:

是一種常見的計算機體系結構。

  1. 不考慮快取情況,這裡的CPU能且只能對記憶體進行讀寫,不能訪問外設;
  2. 外設要輸入或者輸出資料必須通過記憶體寫入資料或讀取資料;
  3. 所有裝置都只能直接和記憶體打交道。

二、作業系統:

作業系統是一款搞管理軟硬體的軟體。包括核心(程序管理,記憶體管理,檔案管理,驅動管理)和其他程式(庫函式,shell程式等)。

  • 先描述再組織
  • 用結構體描述
  • 用資料結構組織

三、程序

  1. 程式的一個執行例項,或者一個正在執行的程式也就是說是一個擔當分配系統資源(CPU的時間和記憶體)的實體;
  2. 一個程序包含了資料和程式碼;
  3. 程序不單獨存在,程序資訊被放在一個數據結構中;
  4. 任何一個時刻,CPU只執行一個程序;
  5. 在一個時間段內,多個任務同時推進叫併發;在同一個時刻,多個任務同時推進叫並行;
  6. 時間片是一個程序,佔用CPU資源的基本單位。

四、PCB

  1. PCB是一個描述程序的結構體,包含了目標程序的所有資訊;
  2. Linux下的PCB叫做task_struct;
  3. 以下是一個task_struct包含的內容:
struct task_struct {
    volatile long state;    //任務的執行狀態(-1 不可執行,0 可執行(就緒),>0 已停止)。
    void *stack;            //程序核心棧
    atomic_t usage;         //有幾個程序正在使用該結構
    unsigned int flags;     //per process flags, defined below//反應程序狀態的資訊,但不是執行狀態
    unsigned int ptrace;    //系統呼叫
    int lock_depth;         // BKL lock depth 

#ifdef CONFIG_SMP
#ifdef __ARCH_WANT_UNLOCKED_CTXSW
    int oncpu;              //在SMP上幫助實現無加鎖的程序切換
#endif
#endif

    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;              //實時任務排程實體  

#ifdef CONFIG_PREEMPT_NOTIFIERS  
//list of struct preempt_notifier: 
    struct hlist_head preempt_notifiers;    //與搶佔有關
#endif  

    /*
     * fpu_counter contains the number of consecutive context switches
     * that the FPU is used. If this is over a threshold, the lazy fpu
     * saving becomes unlazy to save the trap. This is an unsigned char
     * so that after 256 times the counter wraps and the behavior turns
     * lazy again; this to deal with bursty apps that only use FPU for
     * a short time
     */
    unsigned char fpu_counter;    //FPU使用計數
#ifdef CONFIG_BLK_DEV_IO_TRACE
    unsigned int btrace_seq;      //blktrace是一個針對Linux核心中塊裝置I/O層的跟蹤工具
#endif

    unsigned int policy;          //排程策略
    cpumask_t cpus_allowed;       //多核體系結構中管理CPU的點陣圖

#ifdef CONFIG_TREE_PREEMPT_RCU    //RCU同步原語
    int rcu_read_lock_nesting;
    char rcu_read_unlock_special;
    struct rcu_node *rcu_blocked_node;
    struct list_head rcu_node_entry;
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */

#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
    struct sched_info sched_info;  /*排程相關的資訊,如在CPU上執行的時間/在佇列中等待的時間等。*/
#endif

    struct list_head tasks;            /*任務佇列,用於構建程序連結串列*/  
    struct plist_node pushable_tasks;

    struct mm_struct *mm, *active_mm;   /*mm指向程序所擁有的記憶體描述符,而active_mm指向程序執行時所使用的記憶體描述符。對於普通程序而言,這兩個指標變數的值相同。但是,核心執行緒不擁有任何記憶體描述符,所以它們的mm成員總是為NULL。當核心執行緒得以執行時,它的active_mm成員被初始化為前一個執行程序的active_mm值.*/

/* task state */
    int exit_state;             /*程序退出時的狀態*/
    int exit_code, exit_signal; /*exit_code用於設定程序的終止代號,這個值要麼是_exit()或exit_group()系統呼叫引數(正常終止),要麼是由核心提供的一個錯誤代號(異常終止)。exit_signal被置為-1時表示是某個執行緒組中的一員。只有當執行緒組的最後一個成員終止時,才會產生一個訊號,以通知執行緒組的領頭程序的父程序。*/

    int pdeath_signal;          /* pdeath_signal用於判斷父程序終止時傳送訊號。*/
    unsigned int personality;   /*由於Unix有許多不同的版本和變種,應用程式也有了適用範圍,personality用於處理不同的ABI*/
    unsigned did_exec:1;        /*/根據POSIX程式設計的標準,did_exec是用來表示當前程序是在執行原來的程式碼還是在執行由execve排程的新的程式碼*/
    unsigned in_execve:1;       /* in_execve用於通知LSM是否被do_execve()函式所呼叫。*/
    unsigned in_iowait:1;       /* in_iowait用於判斷是否進行iowait計數。*/

    /* Revert to default priority/policy when forking */
    unsigned sched_reset_on_fork:1;    /*用於判斷是否恢復預設的優先順序或排程策略。*/

    pid_t pid;//程序ID  
    pid_t tgid;//執行緒組ID

#ifdef CONFIG_CC_STACKPROTECTOR
    /* Canary value for the -fstack-protector gcc feature */
    unsigned long stack_canary;
#endif

    /*
     * pointers to (original) parent process, youngest child, younger sibling,
     * older sibling, respectively.  (p->father can be replaced with
     * p->real_parent->pid)
     */
    struct task_struct *real_parent; /* real_parent指向其父程序,如果建立它的父程序不再存在,則指向PID為1的init程序。*/
    struct task_struct *parent; /* parent指向其父程序,當它終止時,必須向它的父程序傳送訊號。它的值通常與real_parent相同。*/
    /*
     * children/sibling forms the list of my natural children
     */
    struct list_head children;  /*children表示連結串列的頭部,連結串列中的所有元素都是它的子程序。*/
    struct list_head sibling;   /* sibling用於把當前程序插入到兄弟連結串列中。*/
    struct task_struct *group_leader;   /* group_leader指向其所在程序組的領頭程序。*/

    /*
     * ptraced is the list of tasks this task is using ptrace on.
     * This includes both natural children and PTRACE_ATTACH targets.
     * p->ptrace_entry is p's link on the p->parent->ptraced list.
     */
    struct list_head ptraced;          /*成員ptrace被設定為0時表示不需要被跟蹤*/
    struct list_head ptrace_entry;

    /* PID/PID hash table linkage. */
    struct pid_link pids[PIDTYPE_MAX];
    struct list_head thread_group;

    struct completion *vfork_done;      /* for vfork() */
    int __user *set_child_tid;          /* CLONE_CHILD_SETTID */
    int __user *clear_child_tid;        /* CLONE_CHILD_CLEARTID */

    cputime_t utime, stime, utimescaled, stimescaled;/*utime是程序使用者態耗費的時間,stime是系統態執行時間。utimescaled/stimescaled也是用於記錄程序在使用者態/核心態的執行時間,但它們以處理器的頻率為刻度*/                         
    cputime_t gtime;                    /*gtime是以節拍計數的虛擬機器執行時間(guest time)*/
    cputime_t prev_utime, prev_stime;   /*prev_utime/prev_stime是先前的執行時間*/
    unsigned long nvcsw, nivcsw;        /*nvcsw/nivcsw是自願(voluntary)/非自願(involuntary)上下文切換計數。last_switch_count是nvcsw和nivcsw的總和*/
    struct timespec start_time;         /* monotonic time */
    struct timespec real_start_time;    /* boot based time *//*start_time和real_start_time都是程序建立時間,real_start_time還包含了程序睡眠時間,常用於/proc/pid/stat*/
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
    unsigned long min_flt, maj_flt;

    struct task_cputime cputime_expires;  /*cputime_expires用來統計程序或程序組被跟蹤的處理器時間,其中的三個成員對應著cpu_timers[3]的三個連結串列*/
    struct list_head cpu_timers[3];  /*請參考cred結構定義檔案的註釋說明*/  

const struct cred __rcu *real_cred; /* objective and real subjective task * credentials (COW) */  
        const struct cred __rcu *cred; /* effective (overridable) subjective task * credentials (COW) */  
        struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */  
        char comm[TASK_COMM_LEN]; /* executable name excluding path - access with [gs]et_task_comm (which lock it with task_lock()) - initialized normally by setup_new_exec */  
        /* file system info */  
        int link_count, total_link_count;  /*硬連線的數量*/  
        #ifdef CONFIG_SYSVIPC/* ipc stuff  /*程序間通訊相關的東西*/  
        struct sysv_sem sysvsem;   
        #endif  
        #ifdef CONFIG_DETECT_HUNG_TASK/* hung task detection */   
        unsigned long last_switch_count;  
        #endif/* CPU-specific state of this task */  
struct thread_struct thread; /*因為task_stcut是與硬體體系結構無關的,因此用thread_struct這個結構來包容不同的體系結構*/  
        /* filesystem information */  
        struct fs_struct *fs;  
        /* open file information */  
        struct files_struct *files;  
        //關於名稱空間深入討論  
        struct nsproxy *nsproxy;/* signal handlers */  
        struct signal_struct *signal;  /* signal指向程序的訊號描述符。*/
        struct sighand_struct *sighand;  /*sighand指向程序的訊號處理程式描述符。*/
        sigset_t blocked, real_blocked;  /*blocked表示被阻塞訊號的掩碼,real_blocked表示臨時掩碼。*/
        sigset_t saved_sigmask; 
        struct sigpending pending;  /*pending存放私有掛起訊號的資料結構。*/ 
        unsigned long sas_ss_sp;size_t sas_ss_size; /*sas_ss_sp是訊號處理程式備用堆疊的地址,sas_ss_size表示堆疊的大小。*/ 
        /*Although signal handling takes place in the kernel, the installed signal handlers run in usermode — otherwise, 
          it would be very easy to introduce malicious or faulty code into the kernel andundermine the system security mechanisms. 
          Generally, signal handlers use the user mode stack ofthe process in question. 
          However, POSIX mandates the option of running signal handlers on a stackset up specifically for this purpose (using the 
          sigaltstack system call). The address and size of this additional stack (which must be explicitly allocated by the 
          user application) are held in sas_ss_sp andsas_ss_size, respectively. (Professional Linux® Kernel Architecture Page384)*/  
        int (*notifier)(void *priv);  /*裝置驅動程式常用notifier指向的函式來阻塞程序的某些訊號(notifier_mask是這些訊號的位掩碼),notifier_data指的是notifier所指向的函式可能使用的資料。*/
        void *notifier_data;  
        sigset_t *notifier_mask;  
        struct audit_context *audit_context; //請參看 Professional Linux® Kernel Architecture Page1100  
        #ifdef CONFIG_AUDITSYSCALL  
        uid_t loginuid;  
        unsigned int sessionid;  
        #endif  
        seccomp_t seccomp;  
        /* Thread group tracking */   
        u32 parent_exec_id;   
        u32 self_exec_id;/* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, * mempolicy */  
        spinlock_t alloc_lock;  
        #ifdef CONFIG_GENERIC_HARDIRQS/* IRQ handler threads */  
        struct irqaction *irqaction;#endif/* Protection of the PI data structures: */   //PI --> Priority Inheritanceraw_spinlock_t pi_lock;  
        #ifdef CONFIG_RT_MUTEXES    //RT-->  RealTime Task 實時任務/* PI waiters blocked on a rt_mutex held by this task */  
        struct plist_head pi_waiters;/* Deadlock detection and priority inheritance handling */  
        struct rt_mutex_waiter *pi_blocked_on;  
        #endif  
        #ifdef CONFIG_DEBUG_MUTEXES/* mutex deadlock detection */  
        struct mutex_waiter *blocked_on;  
        #endif  
        #ifdef CONFIG_TRACE_IRQFLAGS  
        unsigned int irq_events;  
        unsigned long hardirq_enable_ip;  
        unsigned long hardirq_disable_ip;  
        unsigned int hardirq_enable_event;  
        unsigned int hardirq_disable_event;  
        int hardirqs_enabled;  
        int hardirq_context;  
        unsigned long softirq_disable_ip;  
        unsigned long softirq_enable_ip;  
        unsigned int softirq_disable_event;  
        unsigned int softirq_enable_event;  
        int softirqs_enabled;   
        int softirq_context;  
        #endif  
        #ifdef CONFIG_LOCKDEP  
        # define MAX_LOCK_DEPTH 48UL  
        u64 curr_chain_key;  
        int lockdep_depth; //鎖的深度  
        unsigned int lockdep_recursion;  
        struct held_lock held_locks[MAX_LOCK_DEPTH];  
        gfp_t lockdep_reclaim_gfp;  
        #endif  
        /* journalling filesystem info */  
        void *journal_info; //檔案系統日誌資訊  
        /* stacked block device info */  
        struct bio_list *bio_list; //塊IO裝置表  
        #ifdef CONFIG_BLOCK  
        /* stack plugging */  
        struct blk_plug *plug;  
        #endif  
        /* VM state */   
        struct reclaim_state *reclaim_state;  
        struct backing_dev_info *backing_dev_info;  
        struct io_context *io_context;  
        unsigned long ptrace_message;  
        siginfo_t *last_siginfo;  
        /* For ptrace use. */  
        struct task_io_accounting ioac; //a structure which is used for recording a single task's IO statistics.  
        #if defined(CONFIG_TASK_XACCT)  
        u64 acct_rss_mem1;   
        /* accumulated rss usage */  
        u64 acct_vm_mem1;   
        /* accumulated virtual memory usage */  
        cputime_t acct_timexpd;  
        /* stime + utime since last update */  
        #endif  
        #ifdef CONFIG_CPUSETS  
        nodemask_t mems_allowed;   
       /* Protected by alloc_lock */  
        int mems_allowed_change_disable;  
        int cpuset_mem_spread_rotor;  
        int cpuset_slab_spread_rotor;  
        #endif  
        #ifdef CONFIG_CGROUPS  
        /* Control Group info protected by css_set_lock */  
        struct css_set __rcu *cgroups;  
        /* cg_list protected by css_set_lock and tsk->alloc_lock */  
        struct list_head cg_list;  
        #endif  
        #ifdef CONFIG_FUTEX  
        struct robust_list_head __user *robust_list;  
        #ifdef CONFIG_COMPAT  
        struct compat_robust_list_head __user *compat_robust_list;  
        #endifstruct list_head pi_state_list;  
        struct futex_pi_state *pi_state_cache;  
        #endif  
        #ifdef CONFIG_PERF_EVENTS  
        struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];  
        struct mutex perf_event_mutex;  
        struct list_head perf_event_list;  
        #endif  
        #ifdef CONFIG_NUMA  
        struct mempolicy *mempolicy;  
        /* Protected by alloc_lock */  
        short il_next;  
        short pref_node_fork;  
        #endifatomic_t fs_excl; /* holding fs exclusive resources *///是否允許程序獨佔檔案系統。為0表示否。  
        struct rcu_head rcu;/* * cache last used pipe for splice */  
        struct pipe_inode_info *splice_pipe;  
        #ifdef CONFIG_TASK_DELAY_ACCT  
        struct task_delay_info *delays;  
        #endif  
        #ifdef CONFIG_FAULT_INJECTION  
        int make_it_fail;  
        #endif  
        struct prop_local_single dirties;  
        #ifdef CONFIG_LATENCYTOP  
        int latency_record_count;  
        struct latency_record latency_record[LT_SAVECOUNT];  
        #endif  
        /* * time slack values; these are used to round up poll() and * select() etc timeout values. 
           These are in nanoseconds. */  
        unsigned long timer_slack_ns;  
        unsigned long default_timer_slack_ns;  
        struct list_head *scm_work_list;  
        #ifdef CONFIG_FUNCTION_GRAPH_TRACER  
        /* Index of current stored address in ret_stack */  
        int curr_ret_stack;/* Stack of return addresses for return function tracing */  
        struct ftrace_ret_stack *ret_stack;/* time stamp for last schedule */  
        unsigned long long ftrace_timestamp;  
        /* * Number of functions that haven't been traced * because of depth overrun. */  
        atomic_t trace_overrun;  
        /* Pause for the tracing */  
        atomic_t tracing_graph_pause;  
        #endif  
        #ifdef CONFIG_TRACING  
        /* state flags for use by tracers */  
        unsigned long trace;/* bitmask and counter of trace recursion */  
        unsigned long trace_recursion;  
        #endif /* CONFIG_TRACING */  
        #ifdef CONFIG_CGROUP_MEM_RES_CTLR   
        /* memcg uses this to do batch job */  
        struct memcg_batch_info {int do_batch; /* incremented when batch uncharge started */  
        struct mem_cgroup *memcg; /* target memcg of uncharge */  
        unsigned long nr_pages; /* uncharged usage */  
        unsigned long memsw_nr_pages; /* uncharged mem+swap usage */  
        } memcg_batch;  
        #endif  
        #ifdef CONFIG_HAVE_HW_BREAKPOINT  
        atomic_t ptrace_bp_refcnt;  
        #endif  
     };
  • 識別符號(pid):與程序相關的唯一識別符號,用來區別正在執行的程序和其他程序。
  • 狀態:描述程序的狀態,因為程序有掛起,阻塞,執行等好幾個狀態,所以都有個識別符號來記錄程序的執行狀態
  • 狀態  描述
    TASK_RUNNING(就緒狀態) 表示程序正在執行或者處於準備執行的狀態,並不一定在執行
    TASK_INTERRUPTIBLE(淺度睡眠狀態) 也叫睡眠狀態(sleeping),程序因為等待某些條件處於阻塞(掛起的狀態),一旦等待的條件成立,程序便會從該狀態轉化成就緒狀態
    TASK_UNINTERRUPTIBLE(深度睡眠狀態)  也叫磁碟休眠狀態(Disk sleeping),意思與TASK_INTERRUPTIBLE類似,但是我們傳遞任意訊號等不能喚醒他們,只有它所等待的資源可用的時候,他才會被喚醒,這個狀態通常會等待IO的結束
    TASK_STOPPED(暫停狀態) 程序被停止執行,可以通過傳送SIGSTOP訊號來停止程序,通過SIGCONT訊號讓程序繼續執行
    TASK_TRACED(跟蹤狀態) 程序被debugger等程序所監視
    EXIT_ZOMBIE(殭屍狀態) 程序的執行被終止,但是其父程序還沒有使用wait()等系統呼叫來獲知它的終止資訊,此時程序成為殭屍程序
    EXIT_DEAD(死亡狀態) 程序被殺死,即程序的最終狀態,這個狀態只是一個返回狀態,不會在任務列表裡看到
    TASK_KILLABLE(新程序狀態) 當程序處於這種可以終止的新睡眠狀態中,它的執行原理類似於TASK_UNINTERRUPTIBLE,只不過可以響應致命訊號
  • 優先順序:如果有好幾個程序正在執行,就涉及到程序被執行的先後順序的問題,這和程序優先順序這個識別符號有關。
  • 成員    描述
    static_prio  用來儲存靜態優先順序,可以呼叫nice系統直接來修改,取值範圍為100~139
    rt_priority 用來儲存實時優先順序,取值範圍為0~99
    prio 用來儲存動態優先順序
    normal_prio 它的值取決於靜態優先順序和排程策略
  • 程式計數器(pc暫存器指標):程式中即將被執行的下一條指令的地址。
  • 記憶體指標:程式程式碼和程序相關資料的指標。
  • 上下文資料:程序執行時CPU處理器的暫存器中的資料。
  • I/O狀態資訊:包括顯示的I/O請求,分配給程序的I/O裝置和被程序使用的檔案列表等。
  • 記賬資訊:包括處理器的時間總和,使用的時鐘總和,時間限制,記賬號等等。
  • 其他資訊。

延伸:

(一)檢視程序

  • 【ls /proc/】檢視程序

  • 【ps aux | grep '程序檔名'】檢視某一個程序具體資訊

  • 【top】檢視程序資訊,更新狀態;

  • 【Ctrl+c】退出程序;
  • 想要獲取程序pid,可以使用getpid()函式,獲取父程序pid使用getppid()函式;

六、fork

  • fork通過系統呼叫建立程序,先建立父程序,再建立子程序,有兩個返回值,給父程序返回子程序的pid,給子程序返回0;
  • 父子程序程式碼共享,資料各自開闢空間,採用寫時拷貝,各自私有一份;
  • fork之後通常用if分流(為0進入子程序,>0進入父程序);

(二)、狀態

  • 殭屍狀態
  1. 殭屍狀態是一個比較特殊的狀態,當程序退出並且父程序沒有讀取到子程序退出的返回程式碼時就會產生殭屍狀態;
  2. 殭屍狀態會以終止狀態保持在程序中並且會一直等待父程序讀取退出狀態程式碼;
  3. 所以,只要子程序退出,父程序還在執行,但父程序沒有讀取到子程序狀態,子程序進入殭屍狀態;
  4. 維護退出狀態需要用資料維護,儲存在PCB中;
  5. 殭屍狀態會造成記憶體資源浪費,和記憶體洩漏;
  6. 為了避免造成殭屍狀態,讓父程序回收子程序。
  • 孤兒狀態
  1. 父程序如果提前退出,子程序稱為孤兒狀態;
  2. 孤兒程序被1號init程序領養。

(三)、程序優先順序

  • 程序優先順序指的是CPU資源分配的先後順序;
  • 多個程序處於就緒狀態時,CPU選擇程序優先順序可以看做鏈式結構;
  • PRI表示一個程序可被執行的優先順序,其值越小越早被執行;
  • NI表示一個程序的nice值,是一個程序可被執行的優先順序的修正數值;
  • PRI(new)=PRI(old)+nice;
  • 調整程序優先順序就是調整nice值;
  • nice是-20~19,一共40個級別。
  • 【nice -n+nice值+程序檔名】開始執行程序程式時就指定nice值;
  • 【renice +nice值+-p+某個程序pid】將已存在程序的nice值進行調整;
  • 【top】+【r】+輸入程序pid+輸入nice值:用top命令更改已存在程序的nice值。

五、程式地址空間

  • 此地址不是實體地址,在Linux下這種地址叫做虛擬地址;
  • 我們平常看見的也是虛擬地址,實體地址由OS統一管理,使用者不可見;
  • 父程序在建立子程序時,程式碼共享,資料以寫時拷貝的形式拷貝;(寫時拷貝見如下)
  • 父程序,子程序執行的先後順序看OS排程器;

六、程序排程

一個程序,一個PCB,一個虛擬地址空間,一個頁表,一份對映關係;