openEuler 筆記:進程1:程序的加載運行、進程的描述(PCB、進程狀態(tài))

本系列學(xué)習(xí)筆記基本上是博主的《 openEuler 操作系統(tǒng)》讀書筆記,中間插入一些自己查的資料以及翻到的感覺有用的源代碼

默認架構(gòu)為 ARM

程序及其加載執(zhí)行

類 UNIX 的二進制程序一般為 ELF 格式,一個【邏輯意義上作為整體的程序】會被按照內(nèi)容類型劃分為數(shù)個 Segment 進行存儲。主要的 Segment 有 :.text ,存機器指令序列;.data 存可變的全局變量及靜態(tài)局部變量;.rodata 存只讀數(shù)據(jù)、常量;.bss 存未初始化的全局變量。

加載,是操作系統(tǒng)將 ELF 讀入內(nèi)存的過程。首先檢查 ELF 頭,確定是否可以運行。然后讀端頭表,得到各個段的信息,為需要裝入內(nèi)存的段分配空間,然后把這些段加載到內(nèi)存中。

執(zhí)行。程序入口地址存在 ELF 頭中,OS 讀到該地址后到 .text 找到入口,將入口地址賦給 PC ,程序獲得 CPU 控制權(quán)。

運行過程中,PC 保存即將執(zhí)行的指令地址,LR(鏈接寄存器)保存函數(shù)調(diào)用返回后的下一條指令地址。

每個運行中的函數(shù)都擁有一個棧幀,其為函數(shù)的每次調(diào)用構(gòu)建獨立的上下文。當(dāng)函數(shù)調(diào)用新的函數(shù)時,新的函數(shù)會被分配新的棧幀。函數(shù)運行結(jié)束后棧幀會被彈出,系統(tǒng)從下面的棧幀(即發(fā)起對剛剛結(jié)束的函數(shù)調(diào)用的函數(shù)的棧幀)中取出之前保存的 FP、LR 值,繼續(xù)運行


進程的描述
操作系統(tǒng)使用 PCB (Process Control Block,進程控制塊)對進程進行描述,操作系統(tǒng)通過 PCB 感知進程。
PCB
PCB 定義在 include/linux/sched.h 的 struct task_struct,是一個六百二十多行的結(jié)構(gòu)體。
啟動一個程序時,操作系統(tǒng)先創(chuàng)建 PCB ,然后根據(jù)其中的信息對進程進行管理和控制,程序運行后系統(tǒng)釋放 PCB 。其中的主要信息包含描述信息、控制信息、 CPU 上下文、資源管理信息
描述信息

進程標(biāo)識符,OS 用它來標(biāo)記每個進程;

用戶標(biāo)識號,區(qū)分某個進程屬于哪個用戶

    kuid_t          loginuid;
    unsigned int        sessionid;

家族關(guān)系,表明該進程與其父進程、祖先進程、子進程、兄弟進程等的關(guān)系

    /*
     * Pointers to the (original) parent process, youngest child, younger sibling,
     * older sibling, respectively.  (p->father can be replaced with
     * p->real_parent->pid)
     */
  
    /* Real parent process: */
    struct task_struct __rcu    *real_parent;
  
    /* Recipient of SIGCHLD, wait4() reports: */
    struct task_struct __rcu    *parent;
  
    /*
     * Children/sibling form the list of natural children:
     */
    struct list_head        children;
    struct list_head        sibling;
    struct task_struct      *group_leader;

real_parent 是創(chuàng)建該進程的進程,而 parent 是響應(yīng)信號相關(guān)的父進程——比如 SIGCHLD 就會被發(fā)送給 parent。這么設(shè)計是因為:有些情況下真父進程可能先終止,這樣如 init 這樣的其他進程就會成為新的父進程,但不會改變真父進程的值。

控制信息

進程狀態(tài)(就緒、阻塞、運行、終止)

    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
  
    /* 相關(guān)宏定義如下 */
  
    /* Used in tsk->state: */
    #define TASK_RUNNING        0x0000
    #define TASK_INTERRUPTIBLE      0x0001
    #define TASK_UNINTERRUPTIBLE    0x0002
    #define __TASK_STOPPED      0x0004
    #define __TASK_TRACED       0x0008
    #define TASK_PARKED         0x0040
    #define TASK_DEAD           0x0080
    #define TASK_WAKEKILL       0x0100
    #define TASK_WAKING         0x0200
    #define TASK_NOLOAD         0x0400
    #define TASK_NEW            0x0800
    #define TASK_STATE_MAX      0x1000

進程優(yōu)先級

    int             prio;
    int             static_prio;
    int             normal_prio;
    unsigned int        rt_priority;

靜態(tài)優(yōu)先級:進程啟動時指定,值越小越高。一般不用,但可用系統(tǒng)調(diào)用 nice() 修改;
動態(tài)優(yōu)先級:可以因調(diào)度策略的改變而臨時變動;
實時:有限程度僅與實時優(yōu)先級有關(guān),值越小越高。實時進程調(diào)度總優(yōu)于普通進程

記賬信息:記錄進程占有、利用資源的情況,調(diào)度以這些信息為依據(jù)進行(如剝奪等),如時間方面、上下文切換方面的:

        u64             utime;
        u64             stime;
    #ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
        u64             utimescaled;
        u64             stimescaled;
    #endif
        u64             gtime;
        struct prev_cputime     prev_cputime;
    #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
        struct vtime            vtime;
    #endif
  
    #ifdef CONFIG_NO_HZ_FULL
        atomic_t            tick_dep_mask;
    #endif
        /* Context switch counts: */
        unsigned long           nvcsw;
        unsigned long           nivcsw;
  
        /* Monotonic time in nsecs: */
        u64             start_time;
  
        /* Boot based time in nsecs: */
        u64             real_start_time;

CPU 上下文
CPU 上下文指某時刻 CPU 各寄存器中的值,用于進程切換時保存狀態(tài)。其保存于 task_struct->thread_struct->cpu_context
從下面的代碼中可以看到,cpu_context 就是各個寄存器中的值的集合

/* arch/arm64/include/asm/processor.h */
struct cpu_context {
    unsigned long x19;
    unsigned long x20;
    unsigned long x21;
    unsigned long x22;
    unsigned long x23;
    unsigned long x24;
    unsigned long x25;
    unsigned long x26;
    unsigned long x27;
    unsigned long x28;
    unsigned long fp;
    unsigned long sp;
    unsigned long pc;
};

struct thread_struct {
    struct cpu_context  cpu_context;    /* cpu context */
    /* 省略其他變量,thread_struct 結(jié)構(gòu)體中包含所有與 CPU 相關(guān)的狀態(tài)信息 */
};

資源管理信息
這部分信息在 PCB 中占比最大,包含存儲器、文件系統(tǒng)、使用 IO 設(shè)備的信息等,主要與內(nèi)存和文件相關(guān)。

    void *stack;    // 指向進程內(nèi)核棧,內(nèi)核棧是進程在內(nèi)核態(tài)用的棧,在內(nèi)核空間

    // 進程的用戶空間描述符
    struct mm_struct        *mm;
    struct mm_struct        *active_mm;

    /* Filesystem information: */
    struct fs_struct        *fs;    // 與進程相關(guān)的文件系統(tǒng)

    /* Open file information: */
    struct files_struct     *files; // 進程正打開的文件列表

    // 內(nèi)存描述符,定義在 include/linux/mm_types.h
    struct mm_struct {
        spinlock_t arg_lock; /* protect the below fields,自旋鎖 */
        // 描述內(nèi)存中各個段的起始位置,包括棧、映射段、堆、BSS段、數(shù)據(jù)段、代碼段
        unsigned long start_code, end_code, start_data, end_data;
        unsigned long start_brk, brk, start_stack;
        unsigned long arg_start, arg_end, env_start, env_end;
        //......
    };

    // 進程關(guān)聯(lián)的文件系統(tǒng)信息,include/linux/fs_struct.h
    struct fs_struct {
        int users;              // 該結(jié)構(gòu)的引用用戶數(shù)
        spinlock_t lock;
        seqcount_t seq;
        int umask;
        int in_exec;
        struct path root, pwd;  // 根與當(dāng)前目錄
    } __randomize_layout;

    /* include/linux/fdtable.h
     * Open file table structure
     */
    struct files_struct {
      /*
       * read mostly part
       */
        atomic_t count;             // 引用計數(shù)
        struct fdtable __rcu *fdt;  // 默認指向 fdt,可用于動態(tài)申請內(nèi)存
        struct fdtable fdtab;       // 為 fdt 提供初始值
        // fdt、fdtable 是管理文件描述符用的
        // ...
    };

    // path. include/linux/path
    struct path {
        struct vfsmount *mnt;
        struct dentry *dentry;
    } __randomize_layout;

    struct fdtable {
        unsigned int max_fds;
        struct file __rcu **fd;      /* current fd array */
        unsigned long *close_on_exec;
        unsigned long *open_fds;
        unsigned long *full_fds_bits;
        struct rcu_head rcu;
    };

進程的狀態(tài)
進程當(dāng)前狀態(tài)由 PCB 中的狀態(tài)值描述。這里的狀態(tài)就是運行、就緒、阻塞、終止(僵尸、死亡)

    /* Used in tsk->state: */
    #define TASK_RUNNING        0x0000
    #define TASK_INTERRUPTIBLE      0x0001
    #define TASK_UNINTERRUPTIBLE    0x0002
    #define __TASK_STOPPED      0x0004
    #define __TASK_TRACED       0x0008
    /* Used in tsk->exit_state: */
    #define EXIT_DEAD           0x0010
    #define EXIT_ZOMBIE         0x0020
    #define EXIT_TRACE          (EXIT_ZOMBIE | EXIT_DEAD)
    /* Used in tsk->state again: */
    #define TASK_PARKED         0x0040
    #define TASK_DEAD           0x0080
    #define TASK_WAKEKILL       0x0100
    #define TASK_WAKING         0x0200
    #define TASK_NOLOAD         0x0400
    #define TASK_NEW            0x0800
    #define TASK_STATE_MAX      0x1000

就緒:進程位于運行隊列中,已獲得 CPU 外的所有資源,等待 OS 選中占用 CPU
運行:當(dāng)前進程主動放棄或被搶占,則轉(zhuǎn)為就緒;當(dāng)前進程等待資源或事件時,轉(zhuǎn)為阻塞;運行結(jié)束,進入終止?fàn)顟B(tài)(僵尸/等待)
阻塞:通常是等待外部事件(如 I/O ),需要等來了才就緒。但可被系統(tǒng)調(diào)用、信號等喚醒(阻塞分輕度中度深度,不同的程度需要的條件不同)
終止:

僵尸:父進程未回收進程及其占用的資源(包括 PCB )。若父進程先結(jié)束,init 會成為子進程的 parent ,待結(jié)束后回收資源
死亡:結(jié)束后由父進程回收資源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容