本文内容概述:
1.进程的概念
2.进程控制块
3.剖析Linux下的PCB—task_struct
说到task_struct,或许你是非常陌生的。如果我说他其实就是Linux下的对进程控制块PCB定义的一个结构体,你或许就会懂那么一点。下边开始今天分享的主题:
1.进程的概念:
说到进程控制块PCB,不得不说一下进程。在之前的操作系统的课程学习中,给出的定义:进程是程序的一次动态的执行过程。(这样,你真的是能想明白吗?概念确实是比较抽象的,反正之前学习的时候我不是很清楚的)。进程=程序段+数据段+进程控制块。当我再次学习操作系统学习进程的时候,却有了更深层次的认识:
从操作系统的层次:进程是程序的一个执行实例;进程是正在执行的程序;进程是能分配处理机并且由处理机执行的实体。这么一说,没有正在执行的程序就一定不是进程吗?不是。假如在单处理机的系统中,一次只能执行一个进程(也就是说,一次只能有一个进程处于运行状态),那么其他的被加载到内存的程序(已经获得了除处理机之外的所需的全部资源),也是进程。
从内核的层次:担当分配系统资源(包括内存等)的实体。
进程的两个基本元素是程序代码(有可能被其他进程所共享)和与代码相关联的数据集。其实这里,与“进程=程序段+数据段+进程控制块 ”是一样的。数据集就是指的是数据段和进程控制块。
2.进程控制块:
为了描述进程的信息,我们引入了进程控制块这个数据结构。那么,为什么需要进程控制块呢?它起着什么作用?
在单处理机系统,我们每次只能执行一个进程,我们如何知道是哪个进程在执行?执行完这个进程之后,又需要去执行哪些进程?假如一个进程由于种种原因,需要被中断(不是被杀死),那么之后再来执行此进程的时候,我们怎么会知道之前执行到哪(不可能从头开始执行),等等情形,所以就需要进程控制块。
通过分析以上的种种情况,我们得出:进程控制块至少应该包含进程标识(是进程的唯一标识,PID),还有进程的优先级,记录进程的上下文信息,记录进程下一次下一条指令的地址,进程中的程序的地址,等等(下文将会给出task_struct结构体的成员)。
当操作系统要调度某进程去执行时,要从该进程的PCB中查询进程的优先级和现行状态;
当系统调度到某个进程时,要根据PCB中保存的现行信息先去回复现场,然后再去修改进程的状态,根据程序的地址,找到程序的位置,并开始执行;
当进程由于某个原因需要暂停时,就必须将现行状态保存在PCB中,并记录下一条指令的地址。
可见,在进程的整个执行过程中,进程控制块都起着非常重要的作用。下边我们就来剖析Linux下的PCB—task_struct结构体。
3.剖析Linux下的PCB—task_struct
关于task_struct的定义, 位于/usr/include/linux/sched.h文件(centos6.5下)中。
下边我就结构体中的数据成员进行分类:
(1)进程的状态:
一个进程是执行状态还是睡眠状态还是阻塞状态,将在下边的成员中进行描述。
volatile long 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_ZOMBIE 16 //僵尸状态的进程,表示进程被终止,但是父进程还没 有获取它的终止信息,比如进程有没有执行完等信息。
#define EXIT_DEAD 32 //进程的最终状态,进程死亡。
/* in tsk->state again */
#define TASK_DEAD 64 //死亡
#define TASK_WAKEKILL 128 //唤醒并杀死的进程
#define TASK_WAKING 256 //唤醒进程
(2)进程的唯一标识
pid_t pid;//进程的唯一标识
pid_t tgid;// 线程组的领头线程的pid成员的值
pid是进程的唯一表示,范围是0~32767,可以表示32768个进程。
在Linux系统中,一个线程组的所有线程使用和该线程组的领头线程相同的PID,并被存放在tgid成员中。(线程是程序运行的最小单位,进程是程序运行的基本单位。)
(3)进程的内核栈
void *stack;//用来维护进程的内核栈。
Linux内核是通过以下的结构体来表示进程的内核栈:
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
注明:关于内核栈,之后再来补充。
(4)进程的标记:
unsigned int flags;
//进程的标志信息
#