进程内核栈结构:union task_union
在../include/linux/sched.h中定义了如下一个联合结构用来创建内核栈空间。
//../include/linux/sched.h
union task_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
}:
线程描述符:struct thread_info
每一个进程都有一个进程描述符task_struct,且有一个用来定位它的结构thread_info,thread_info位于其进程内核栈中(有些实现没有用到thread_info,而是使用一个寄存器来记录进程描述符的地址),操作系统使用这个结构中的task指针字段找到进程的进程描述符,从而得到执行一个进程所需的全部信息。
//../arch/xtensa/include/asm
struct thread_info {
struct task_struct *task; //指向当前进程内核栈对应的进程的进程描述符
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
void *sysenter_return;
int uaccess_err;
};
内核使用
alloc_thread_info
和free_thread_info
宏分配和释放存储thread_info结构和内核栈的内存区。使用current_thread_info()
函数获取当前操作的进程的thread_info 。为了获得当前在CPU上运行的进程的进程描述符指针,内核要调用current
宏,该宏本质上等价于current_thread_info()->task
。详见【深入理解Linux内核】中文第三版91页。
进程描述符:task_struct
从内核观点看,进程的目的就是担当分配系统资源(CPU 时间、内存等)的实体。为此目的,操作系统为每个进程维持着一个进程描述符。
task_struct 示意图:
源代码
在../include/linux/sched.h中定义了task_struct,其中包含了一个进程所需的全部信息。其结构体实例在内存中的大小一般在1KB以上。
//../include/linux/sched.h
//---------------------------------------------------进程描述符结构定义---------------------------------------------------
struct task_struct
{
//---------------------------------------------------------进程状态------------------------------------------------------------
long state; //任务的运行状态
//---------------------------------------------------------进程标识信息---------------------------------------------------------
pid_t pid; //进程ID
pid_t pgrp; //进程组标识,表示进程所属的进程组,等于进程组的领头进程的pid
pid_t tgid; //进程所在线程组的ID,等于线程组的领头线程的pid,getpid()系统调用返回tgid值。
pid_t session; //进程的登录会话标识,等于登录会话领头进程的pid。
struct pid pids[PIDTYPE_MAX]; //PIDTYPE_MAX=4,一共4个hash表。
char comm[TASK_COMM_LEN]; //记录进程的名字,即进程正在运行的可执行文件名
int leader; //标志,表示进程是否为会话主管(会话领头进程)。
//-------------------------------------------------------进程调度相关信息-------------------------------------------------------
long nice;//进程的初始优先级,范围[-20,+19],默认0,nice值越大优先级越低,分配的时间片
//可能越少。
int static_prio;//静态优先级。
int prio;//存放调度程序要用到的优先级。
/*
0-99 -> Realtime process
100-140 -> Normal process
*/
unsigned int rt_priority;//实时优先级,默认情况下范围[0,99]
/*
0 -> normal
1-99 -> realtime
*/
unsigned long sleep_avg;//这个字段的值用来支持调度程序对进程的类型(I/O消耗型 or CPU消耗型)进行
//判断,值越大表示睡眠的时候更多,更趋向于I/O消耗型,反之,更趋向于CPU消耗型。
unsigned long sleep_time;//进程的睡眠时间
unsigned int time_slice;//进程剩余时间片,当一个任务的时间片用完之后,要根据任务的静态优先级
//static_prio重新计算时间片。task_timeslice()为给定的任务返回一个新的时间片。对于交互性强的进程,时间片用完之后,它
//会被再放到活动数组而不是过期数组,该逻辑在scheduler_tick()中实现。
#if defined(CONFIG_SCHEDSTATS)||define(CONFIG_TASK_DELAY_ACCT)
unsigned int policy;//表示该进程的进程调度策略。调度策略有:
//SCHED_NORMAL 0, 非实时进程, 用基于优先权的轮转法。
//SCHED_FIFO 1, 实时进程, 用先进先出算法。
//SCHED_RR 2, 实时进程, 用基于优先权的轮转法
#endif
struct list_head tasks;//任务队列,通过这个寄宿于PCB(task_struct)中的字段构成的双向循环链表将宿主
//PCB链接起来。
struct list_head run_list;//该进程所在的运行队列。这个队列有一个与之对应的优先级k,所有位于这个队列中
//的进程的优先级都是k,这些k优先级进程之间使用轮转法进行调度。k的取值是0~139。这个位于宿主PCB中的struct list_head类
//型的run_list字段将构成一个优先级为k的双向循环链表,像一条细细的绳子一样,将所有优先级为k的处于可运行状态的进程的
//PCB(task_struct)链接起来。
prio_array_t *array; //typedef struct prio_array prio_array_t; 可以说,这个指针包含了操作
//系统现有的所有按PCB的优先级进行整理了的PCB的信息。
//---------------------------------------------------------进程链接信息---------------------------------------------------------
struct task_struct *real_parent;//指向创建了该进程的进程的进程描述符,如果父进程不再存在,就指向进程
//1(init)的进程描述符。
struct task_struct *parent;//recipient of SIGCHLD, wait4() reports. parent是该进程现在的父进程,
//有可能是“继父”
struct list_head children;//list of my children. children指的是该进程孩子的链表,使用
//list_for_each和list_entry,可以得到所有孩子的进程描述符。
struct lsit_head sibling;//linkage in my parent's children list.
//sibling为该进程的兄弟的链表,也就是其父亲的所有孩子的链表。用法与children相似。
struct task_struct *group_leader;//threadgroup leader,主线程描述符
struct list_head thread_group; //线程组链表,也就是该进程所有线程的链表。
//----------------------------------------------------------......------------------------------------------------------------
};
参考
进程描述符结构定义的部分注释
反斜杠作用“\”
Linux3.0.6内核task_struct注释
do {…} while (0) 在宏定义中的作用
进程状态:state
state字段的值通常用一个简单的赋值语句设置。例如:
p->state = TASK_RUNNING;
,内核也使用set_task_state
和set_current_state
宏,分别设置指定进程的状态和当前执行进程的状态。
//../include/linux/sched.h
#define set_task_state(tsk, state_value) \
set_mb((tsk)->state, (state_value))
#define set_current_state(state_value) \
set_mb(current->state, (state_value))
set_mb函数确保编译程序或CPU 控制单元不把赋值操作与其他指令混合。混合指令的顺序有时会导致灾难性的后果。
优先级数组:struct prio_array
//../kernel/sched.c
struct prio_array{
int nr_active;//各个优先级任务的总数目
unsigned