进程
进程是程序执行的基本单位,而程序是若干个函数组成的可执行文件。
进程由当前已有进程调用 fork 创建,分叉的进程叫做子进程,创建者叫做父进程。子进程和父进程并发运行。如果父进程有多个子进程,那么这些子进程间是兄弟进程。
进程被创建后是就绪状态,表示内核已经为进程建立了所有结构并且获取了必要的信息。当它被 CPU 选中时,就会进入运行状态。在运行状态,进程可以被取消,进入就绪状态;被中断,进入阻塞状态;僵死,调用 exit 来销毁进程。
进程描述符
内核中有个 task_struct 结构体表示进程描述符,用于存放进程的属性和信息。并且内核为了进行内存管理和进程调度,还采用了 task_list 结构体来存放所有进程的状态,并且借助全局变量 current 来存放当前进程的 task_struct 引用。
-
进程属性相关字段
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
-
进程调度相关字段
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;
}
-
进程间相互关系
struct task_struct{ struct task_struct *real_parent; // 指向当前进程的父进程的描述符 struct task_struct *parent; // 指向父进程描述符的指针 struct list_head children; // 指向当前进程的子进程列表 struct list_head sibling; // 指向当前进程的兄弟进程列表 }
-
进程信任状相关
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; }
进程的创建
进程的创建过程:
-
为新进程分配一个唯一的进程标识号,并申请空白的PCB,若申请失败则返回。
在 Linux 中表现为分配 task_struct,其中的 pid 是一定的
-
为进程分配资源,如果资源不足则等待资源
-
初始化 PCB
一般来说,是把父进程的一些属性复制给了子进程
-
如果进程的调度队列能够接纳新队列,那么就插入到就绪队列
进程的三种创建方式:fork、vfork 和 clone
-
fork( ) 函数
fork( ) 函数返回了两次:一次是在父进程,一次是在子进程。如果在子进程中返回,会返回0;如果在父进程中返回会返回子进程的 PID。
// arch/i386/kernel/process.c asmlinkage int sys_fork(struct pt_regs regs){ return do_fork(SIGCHLD, regs.esp, ®s, 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); }
-
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, ®s, 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( ) 传入的标志不相同
-
clone( ) 函数
clone 函数把一个指向函数的指针和该函数的参数作为自己的参数
// arch/i386/kernel/process.c asmlinkage int sys_vfork(struct pt_regs regs){ unsigned long clone_flags