openEuler 笔记:进程1:程序的加载运行、进程的描述(PCB、进程状态)

本系列学习笔记基本上是博主的《 openEuler 操作系统》读书笔记,中间插入一些自己查的资料以及翻到的感觉有用的源代码

默认架构为 ARM

程序及其加载执行

类 UNIX 的二进制程序一般为 ELF 格式,一个【逻辑意义上作为整体的程序】会被按照内容类型划分为数个 Segment 进行存储。主要的 Segment 有 :.text ,存机器指令序列;.data 存可变的全局变量及静态局部变量;.rodata 存只读数据、常量;.bss 存未初始化的全局变量。

加载,是操作系统将 ELF 读入内存的过程。首先检查 ELF 头,确定是否可以运行。然后读端头表,得到各个段的信息,为需要装入内存的段分配空间,然后把这些段加载到内存中。

执行。程序入口地址存在 ELF 头中,OS 读到该地址后到 .text 找到入口,将入口地址赋给 PC ,程序获得 CPU 控制权。

运行过程中,PC 保存即将执行的指令地址,LR(链接寄存器)保存函数调用返回后的下一条指令地址。

每个运行中的函数都拥有一个栈帧,其为函数的每次调用构建独立的上下文。当函数调用新的函数时,新的函数会被分配新的栈帧。函数运行结束后栈帧会被弹出,系统从下面的栈帧(即发起对刚刚结束的函数调用的函数的栈帧)中取出之前保存的 FP、LR 值,继续运行


进程的描述
操作系统使用 PCB (Process Control Block,进程控制块)对进程进行描述,操作系统通过 PCB 感知进程。
PCB
PCB 定义在 include/linux/sched.h 的 struct task_struct,是一个六百二十多行的结构体。
启动一个程序时,操作系统先创建 PCB ,然后根据其中的信息对进程进行管理和控制,程序运行后系统释放 PCB 。其中的主要信息包含描述信息、控制信息、 CPU 上下文、资源管理信息
描述信息

进程标识符,OS 用它来标记每个进程;

用户标识号,区分某个进程属于哪个用户

    kuid_t          loginuid;
    unsigned int        sessionid;

家族关系,表明该进程与其父进程、祖先进程、子进程、兄弟进程等的关系

    /*
     * 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 是创建该进程的进程,而 parent 是响应信号相关的父进程——比如 SIGCHLD 就会被发送给 parent。这么设计是因为:有些情况下真父进程可能先终止,这样如 init 这样的其他进程就会成为新的父进程,但不会改变真父进程的值。

控制信息

进程状态(就绪、阻塞、运行、终止)

    volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
  
    /* 相关宏定义如下 */
  
    /* 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

进程优先级

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

静态优先级:进程启动时指定,值越小越高。一般不用,但可用系统调用 nice() 修改;
动态优先级:可以因调度策略的改变而临时变动;
实时:有限程度仅与实时优先级有关,值越小越高。实时进程调度总优于普通进程

记账信息:记录进程占有、利用资源的情况,调度以这些信息为依据进行(如剥夺等),如时间方面、上下文切换方面的:

        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 各寄存器中的值,用于进程切换时保存状态。其保存于 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 结构体中包含所有与 CPU 相关的状态信息 */
};

资源管理信息
这部分信息在 PCB 中占比最大,包含存储器、文件系统、使用 IO 设备的信息等,主要与内存和文件相关。

    void *stack;    // 指向进程内核栈,内核栈是进程在内核态用的栈,在内核空间

    // 进程的用户空间描述符
    struct mm_struct        *mm;
    struct mm_struct        *active_mm;

    /* Filesystem information: */
    struct fs_struct        *fs;    // 与进程相关的文件系统

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

    // 内存描述符,定义在 include/linux/mm_types.h
    struct mm_struct {
        spinlock_t arg_lock; /* protect the below fields,自旋锁 */
        // 描述内存中各个段的起始位置,包括栈、映射段、堆、BSS段、数据段、代码段
        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;
        //......
    };

    // 进程关联的文件系统信息,include/linux/fs_struct.h
    struct fs_struct {
        int users;              // 该结构的引用用户数
        spinlock_t lock;
        seqcount_t seq;
        int umask;
        int in_exec;
        struct path root, pwd;  // 根与当前目录
    } __randomize_layout;

    /* include/linux/fdtable.h
     * Open file table structure
     */
    struct files_struct {
      /*
       * read mostly part
       */
        atomic_t count;             // 引用计数
        struct fdtable __rcu *fdt;  // 默认指向 fdt,可用于动态申请内存
        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;
    };

进程的状态
进程当前状态由 PCB 中的状态值描述。这里的状态就是运行、就绪、阻塞、终止(僵尸、死亡)

    /* 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
运行:当前进程主动放弃或被抢占,则转为就绪;当前进程等待资源或事件时,转为阻塞;运行结束,进入终止状态(僵尸/等待)
阻塞:通常是等待外部事件(如 I/O ),需要等来了才就绪。但可被系统调用、信号等唤醒(阻塞分轻度中度深度,不同的程度需要的条件不同)
终止:

僵尸:父进程未回收进程及其占用的资源(包括 PCB )。若父进程先结束,init 会成为子进程的 parent ,待结束后回收资源
死亡:结束后由父进程回收资源

你可能感兴趣的:(openEuler 笔记:进程1:程序的加载运行、进程的描述(PCB、进程状态))