为了管理进程,操作系统必须对每个进程所做的事情进行清楚地描述,为此,操作系统使用数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符或进程控制块(PCB)。
在linux系统中,这就是task_struct结构,
所属头文件include\linux\sched.h。
每个进程都会被分配一个task_struct结构,它包含了这个进程的所有信息,
在任何时候操作系统都能跟踪这个结构的信息。
这个结构是linux内核中最重要的数据结构,下面我们会详细的说。
这个结构的主要信息:
1、调度信息, 由哪个调度函数调度,怎样调度等
2、信号处理
3、进程队列指针
4、进程标识
5、时间数据成员
6、信号量数据成员
7、进程上下文环境
8、文件系统数据成员
9、内存数据成员
10、页面管理
11、支持对称多处理器方式(SMP)时的数据成员
12、其它数据成员
13、进程队列的全局变量
1. 调度数据成员
(1) volatile long states;
表示进程的当前状态:
TASK_RUNNING:正在运行或在就绪队列run-queue中准备运行的进程,实际参与进程调度。
TASK_INTERRUPTIBLE:处于等待队列中的进程,待资源有效时唤醒,也可由其它进程通过信号(signal)或定时中断唤醒后进入就绪队列run-queue。
TASK_UNINTERRUPTIBLE:处于等待队列中的进程,待资源有效时唤醒,不可由其它进程通过信号(signal)或定时中断唤醒。
TASK_ZOMBIE:表示进程结束但尚未消亡的一种状态(僵死状态)。此时,进程已经结束运行且释放大部分资源,但尚未释放进程控制块。
TASK_STOPPED:进程被暂停,通过其它进程的信号才能唤醒。导致这种状态的原因有二,或者是对收到SIGSTOP、SIGSTP、SIGTTIN或SIGTTOU信号的反应,或者是受其它进程的ptrace系统调用的控制而暂时将CPU交给控制进程。
TASK_SWAPPING: 进程页面被交换出内存的进程。
(2) unsigned long flags;
进程标志:
PF_ALIGNWARN 打印“对齐”警告信息。
PF_PTRACED 被ptrace系统调用监控。
PF_TRACESYS 正在跟踪。
PF_FORKNOEXEC 进程刚创建,但还没执行。
PF_SUPERPRIV 超级用户特权。
PF_DUMPCORE dumped core。
PF_SIGNALED 进程被信号(signal)杀出。
PF_STARTING 进程正被创建。
PF_EXITING 进程开始关闭。
PF_USEDFPU 该进程使用FPU(SMP only)。
PF_DTRACE delayed trace (used on m68k)。
(3) unsigned long rt_priority;
rt_priority给出实时进程的优先级,rt_priority+1000给出进程每次获取CPU后可使用的时间(同样按jiffies计)。实时进程的优先级可通过系统调用sys_sched_setscheduler()改变
(4) long counter;
在轮转法调度时表示进程当前还可运行多久。在进程开始运行是被赋为priority的值,以后每隔一个tick(时钟中断)递减1,减到0时引起新一轮调度。重新调度将从run_queue队列选出counter值最大的就绪进程并给予CPU使用权,因此counter起到了进程的动态优先级的作用(priority则是静态优先级)。
(5) unsigned long policy;
该进程的进程调度策略,可以通过系统调用sys_sched_setscheduler()更改(见kernel/sched.c)。调度策略有:
SCHED_OTHER 0 非实时进程,基于优先权的轮转法(round robin)。
SCHED_FIFO 1 实时进程,用先进先出算法。
SCHED_RR 2 实时进程,用基于优先权的轮转法。
2. 信号处理
(1) unsigned long signal;
进程接收到的信号。每位表示一种信号,共32种。置位有效。
(2) unsigned long blocked;
进程所能接受信号的位掩码。置位表示屏蔽,复位表示不屏蔽。
(3) struct signal_struct *sig;
因为signal和blocked都是32位的变量,Linux最多只能接受32种信号。对每种信号,各进程可以由PCB的sig属性选择使用自定义的处理函数,或是系统的缺省处理函数。指派各种信息处理函数的结构定义在include/linux/sched.h中。对信号的检查安排在系统调用结束后,以及“慢速型”中断服务程序结束后。
3. 进程队列指针
(1) struct task_struct *next_task,*prev_task;
所有进程(以PCB的形式)组成一个双向链表。next_task和就是链表的前后指针。链表的头和尾都是init_task(即0号进程)。
(2) struct task_struct *next_run,*prev_run;
由正在运行或是可以运行的,其进程状态均为TASK_RUNNING的进程所组成的一个双向循环链表,即run_queue就绪队列。该链表的前后向指针用next_run和prev_run,链表的头和尾都是init_task(即0号进程)。
(3) struct task_struct *p_opptr,*p_pptr;和structtask_struct *p_cptr,*p_ysptr,*p_osptr;
以上分别是指向原始父进程(original parent)、父进程(parent)、子进程(youngest child)及新老兄弟进程(younger sibling,older sibling)的指针。
4. 进程标识
(1) unsigned short uid,gid;
uid和gid是运行进程的用户标识和用户组标识。
(2) int groups[NGROUPS];
与多数现代UNIX操作系统一样,Linux允许进程同时拥有一组用户组号。在进程访问文件时,这些组号可用于合法性检查。
(3) unsigned short euid,egid;
euid和egid又称为有效的uid和gid。出于系统安全的权限的考虑,运行程序时要检查euid和egid的合法性。通常,uid等于euid,gid等于egid。有时候,系统会赋予一般用户暂时拥有root的uid和gid(作为用户进程的euid和egid),以便于进行运作。
(4) unsigned short fsuid,fsgid;
fsuid和fsgid称为文件系统的uid和gid,用于文件系统操作时的合法性检查,是Linux独特的标识类型。它们一般分别和euid和egid一致,但在NFS文件系统中NFS服务器需要作为一个特殊的进程访问文件,这时只修改客户进程的fsuid和fsgid。
(5) unsigned short suid,sgid;
suid和sgid是根据POSIX标准引入的,在系统调用改变uid和gid时,用于保留真正的uid和gid。
(6) int pid,pgrp,session;
进程标识号、进程的组织号及session标识号,相关系统调用(kernel/sys.c中)有sys_setpgid、sys_getpgid、sys_setpgrp、sys_getpgrp、sys_getsid及sys_setsid几种。
(7) int leader;
是否是session的主管,布尔量。
5. 时间数据成员
(1) unsigned long timeout;
用于软件定时,指出进程间隔多久被重新唤醒。采用tick为单位。
(2) unsigned long it_real_value,it_real_iner;
用于itimer(interval timer)软件定时。采用jiffies为单位,每个tick使it_real_value减到0时向进程发信号SIGALRM,并重新置初值。初值由it_real_incr保存。具体代码见kernel/itimer.c中的函数it_real_fn()。
(3) struct timer_list real_timer;
一种定时器结构(Linux共有两种定时器结构,另一种称作old_timer)。数据结构的定义在include/linux/timer.h中,相关操作函数见kernel/sched.c中add_timer()和del_timer()等。
(4) unsigned long it_virt_value,it_virt_incr;
关于进程用户态执行时间的itimer软件定时。采用jiffies为单位。进程在用户态运行时,每个tick使it_virt_value减1,减到0时向进程发信号SIGVTALRM,并重新置初值。