Linux 内核编程 - 进程篇

本文详细介绍了Linux内核中的进程管理,包括进程描述符task_struct、进程的创建、生命周期、终止以及调度程序的工作原理。阐述了fork、vfork和clone创建进程的区别,分析了进程状态转换及终止过程,同时探讨了等待队列和调度策略在进程管理中的作用。
摘要由CSDN通过智能技术生成

进程

进程是程序执行的基本单位,而程序是若干个函数组成的可执行文件。

进程由当前已有进程调用 fork 创建,分叉的进程叫做子进程,创建者叫做父进程。子进程和父进程并发运行。如果父进程有多个子进程,那么这些子进程间是兄弟进程。

进程被创建后是就绪状态,表示内核已经为进程建立了所有结构并且获取了必要的信息。当它被 CPU 选中时,就会进入运行状态。在运行状态,进程可以被取消,进入就绪状态;被中断,进入阻塞状态;僵死,调用 exit 来销毁进程。

进程描述符

内核中有个 task_struct 结构体表示进程描述符,用于存放进程的属性和信息。并且内核为了进行内存管理和进程调度,还采用了 task_list 结构体来存放所有进程的状态,并且借助全局变量 current 来存放当前进程的 task_struct 引用。

  1. 进程属性相关字段

    struct task_struct{
         
      // 进程的属性
    	volatile long state;
      pid_t pid;
      unsigned long flags;
      struct linux_binfmt *binfmt; // Linux 可执行文件格式
      int exit_code, exit_sinal; // 进程退出值和终止信号
      int pdeath_signal; // 父进程消亡时发出的信号
      ...
    }
    
    • state:进程的状态,有 task_ruunning、task_interruptible、task_uniterruptible、task_zombie 、task_stopped 和 task_dead
    • pid:进程标识符,有PF_STARTING、PF_EXITING、PF_DEAD、PF_FORKNOEXEC
  2. 进程调度相关字段

在这里插入图片描述

struct task_struct{
   
  int prio, static_prio; // 动态优先级;
  prio_arry_t *array;  // 指向 runqueue 的优先级数组
  
  // interactive_credit、sleep_avg 和 activated 共同计算出 sleep_avg
  unsigned long sleep_avg; // 用于计算进程的有效优先级;进程睡眠过程耗费的时钟周期平均值
  
  long interactive_credit; 
  unsigned long long timestap; // 时间戳;用于当前进程睡眠或放弃处理器时计算 sleep_avg
  struct list_head run_list; // 进程队列
  unsigned long policy; // 确定进程的类型
  cpumask_t cpus_allowed; // 指定处理任务的 cpu
  unsigned int time_slice, first_time_slice; // 每次被调度允许运行的最长时间
  int activated; // 记录平均睡眠时间的增减;如果不可打断的进程被唤醒,字段将被设为-1
  unsigned long rt_priority; // 实时进程的优先级
  // nvscw 主动上下文切换次数; nivcsw 被动上下文切换次数
  unsigned long nvcsw, nivcsw; 
}
  1. 进程间相互关系

    struct task_struct{
         
      struct task_struct *real_parent; // 指向当前进程的父进程的描述符
      struct task_struct *parent; // 指向父进程描述符的指针
      struct list_head children; // 指向当前进程的子进程列表
      struct list_head sibling; // 指向当前进程的兄弟进程列表
    }
    
  2. 进程信任状相关

    struct task_struct{
         
      // 进程 0 的 uid 和 gid 分别是 root 的 用户id 和 组id
      // 
      uid_t uid, euid, suid, fsuid;
      gid_t gid, egid, sgid, fsgid;
      struct group_info *group_info;
    }
    

进程的创建

进程的创建过程:

  1. 为新进程分配一个唯一的进程标识号,并申请空白的PCB,若申请失败则返回。

    在 Linux 中表现为分配 task_struct,其中的 pid 是一定的

  2. 为进程分配资源,如果资源不足则等待资源

  3. 初始化 PCB

    一般来说,是把父进程的一些属性复制给了子进程

  4. 如果进程的调度队列能够接纳新队列,那么就插入到就绪队列

进程的三种创建方式:fork、vfork 和 clone

  1. fork( ) 函数

    fork( ) 函数返回了两次:一次是在父进程,一次是在子进程。如果在子进程中返回,会返回0;如果在父进程中返回会返回子进程的 PID。

    // arch/i386/kernel/process.c
    asmlinkage int sys_fork(struct pt_regs regs){
         
      return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
    }
    -------------------------------------
    // arch/ppc/kernel/process.c
    int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs){
         
      CHECK_FULL_REGS(regs);
      return do_fork(SIGCHLD, regs->gpr[1], regs, 0, NULL, NULL);
    }
    
  2. vfork( ) 函数

    vfork( ) 函数的父进程可以一直阻塞,直到子进程调用 exit( ) 或者 exec( ) 为止。

    // arch/i386/kernel/process.c
    asmlinkage int sys_vfork(struct pt_regs regs){
         
      return do_fork(CLONE_VFORK | SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
    }
    -------------------------------------
    // arch/ppc/kernel/process.c
    int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs){
         
      CHECK_FULL_REGS(regs);
      return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0, NULL, NULL);
    }
    

    可以看出和 fork( ) 唯一的区别就是调用 do_fork( ) 传入的标志不相同

  3. clone( ) 函数

    clone 函数把一个指向函数的指针和该函数的参数作为自己的参数

    // arch/i386/kernel/process.c
    asmlinkage int sys_vfork(struct pt_regs regs){
         
      unsigned long clone_flags
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值