【Linux】进程控制块PCB,task_struct源码成员解析

2019-07-14 08:11发布

进程的含义

首先什么是进程?
1>进程是程序的一个执行的实例;
2>进程是正在执行的程序
3>进程是能分配处理器并由处理器执行的实体按内核的观点来谈进程:它所担当分配系统资源(CPU时间,内存)的实体。
进程的两个基本的元素如下
一、程序代码(可能被执行相同程序的其它进程共享)
二、和代码相关联的数据集。这里和代码相关联的数据集指的是数据段和进程控制块PCB。进程是一种动态描述,但是并不代表所有的进程都在运行。(进程在内存中因策略或调度需求,会处于各种状态)

进程控制块PCB

说到PCB大家并不陌生,先来看一下PCB中都含有哪些元素:
  • 标识符:与进程相关的唯一标识符,用来区别正在执行的进程和其他进程。
  • 状态:描述进程的状态,因为进程有挂起,阻塞,运行等好几个状态,所以都有个标识符来记录进程的执行状态。
  • 优先级:如果有好几个进程正在执行,就涉及到进程被执行的先后顺序的问题,这和进程优先级这个标识符有关。
  • 程序计数器:程序中即将被执行的下一条指令的地址
  • 内存指针:程序代码和进程相关数据的指针。
  • 上下文数据:进程执行时处理器的寄存器中的数据。
  • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表等。
  • 记账信息:包括处理器的时间总和,记账号等等。
这个数据结构是一种结构体,由操作系统创建和管理,下面我们就通过了解一下task_struct来看一看操作系统是怎么通过进程控制块来对进程进行控制和调度的吧。

task_struct源码成员解析

这个数据结构体,它定义在linux-2.6.38.8/include/linux/sched.h文件中。下面我们就来介绍一下tast_struct中复杂的成员吧:

进程状态

volatile long state;/* -1 unrunnable, 0 runnable, >0 stopped */ 上面这个变量就是描述进程状态的成员,结合C语言我们学到的知识volatile关键字是降低编译器对代码的优化,是state变量一直从变量的内存中读取内容而不是寄存器;从而保证对操作系统状态实时访问的稳定性。
state成员的可能取值如下 #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 #define TASK_TRACED 8 /*in tsk->exit_state */ #define EXIT_DEAD 16 #define EXIT_ZOMBIE 32 #define EXIT_TRACE ( EXIT_ZOMBIE | EXIT_DEAD ) /* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 /**wake on signals that are deadly**/ #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_NOLOAD 1024 #define TASK_STATE_MAX 2048 /* Convenience macros for the sake of set_task_state */ #define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED) #define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED) 我们可以看到变量定义后面的注释,它说明变量内容<0是不运行的,=0是运行状态,>0是停止状态。下面我们介绍几个常用的取值: 状态 描述 TASK_RUNNING 表示进程正在执行或者处于准备执行的状态 TASK_INTERRUPTIBLE 进程因为等待某些条件处于阻塞(挂起的状态),一旦等待的条件成立,进程便会从该状态转化成就绪状态 TASK_UNINTERRUPTIBLE 意思与TASK_INTERRUPTIBLE类似,但是我们传递任意信号等不能唤醒他们,只有它所等待的资源可用的时候,他才会被唤醒。 TASK_STOPPED 进程被停止执行 TASK_TRACED 进程被debugger等进程所监视。 EXIT_ZOMBIE 进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息,此时进程成为僵尸进程 EXIT_DEAD 进程被杀死,即进程的最终状态。 TASK_KILLABLE 当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE,只不过可以响应致命信号

进程标识符

pid_t pid; //进程的标识符 pid_t tgid //线程组标识符 进程标识符就是为了区别每个进程;tgid的引入是由于Unix程序员希望同一组线程具有相同的pid所以就引入了tgid。

进程标记符

unsigned int flags; /* per process flags,defined below*/ flags反应进程的状态信息,用于内核识别当前进程的状态
常用的状态如下: 状态 描述 PF_FORKNOEXEC 表示进程刚被创建,但还没有执行 PF_SUPERPRIV 表示进程拥有超级用户特权 PF_SIGNALED 表示进程被信号杀出 PF_EXITING 表示进程开始关闭

表示进程亲属关系的成员

/* * 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 __rcu *real_parent; /* real parent process */ struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */ /*children/sibling forms the list of my natural children*/ struct list_head children; /* list of my children */ struct list_head sibling; /* linkage in my parent's children list */ struct task_struct *group_leader; /* threadgroup leader */ 成员 描述 real_parent 指向当前操作系统执行进程的父进程,如果父进程不存在,指向pid为1的init进程 paren 指向当前进程的父进程,当当前进程终止时,需要向它发送wait4()的信号 children 位于链表的头部,链表的所有元素都是children的子进程 group_leader 指向进程组的领头进程

ptrace系统调用

Ptrace提供了一种父进程,它可以被用来控制子进程的运行,常被用来进行断点调试,当它被设置为0时表示不需要追踪。

进程调度

优先级 int prio, static_prio, normal_prio; unsigned int rt_priority; 成员 描述 static_prio 用来保存静态优先级,可以调用nice系统直接来修改取值范围为100~139 rt_priority 用来保存实时优先级,取值范围为0~99 prio 用来保存动态优先级 normal_prio 它的值取决于静态优先级和调度策略 静态优先级:
定义:他不随时间改变,内核不会主动修改它,只能通过系统调用nice去修改static_prio
动态优先级:
定义:调度程序通过或减少进程静态优先级来奖励IO消耗型进程或惩罚CPU消耗进程,调整后的优先级为动态优先级(prio)
实时优先级:
定义:实时优先级只对实时进程有效
实时优先级和静态优先级的取值范围中,值越大,优先级越低