[修改回复]
删除回复
插入表情:
宋体
楷体
幼圆
黑体
隶书
华文行楷
方正舒体
Arial
Arial Black
Arial Narrow
Century Gothic
Comic Sans MS
#0000FF
#8A2BE2
#DEB887
#5F9EA0
#7FFF00
#000000
#D2691E
#FF7F50
#FF0000
#DC143C
#99ccff
字体颜色
#FFF8DC
#00FFFF
#EE82EE
#F5DEB3
#FFFFFF
#F5F5F5
#FFFF00
#9ACD32
使用帮助
3. 进程标识task_struct(include/linux/sched.h) Linux是一个多任务的操作系统,在多任务操作系统中每一个进程都由一个PCB程序控 制块来标识在Linux中PCB实际上是一个名为task_struct的结构体。 task_struct有上百个域,主要包括了10个方面的信息:1.进程状态;2.调度信息, 如调度策略,优先级,时间片,交互值等;3.进程的通讯状况;4.进程树中的父子兄 弟的指针;5.时间信息,如睡眠时间,上一次发生调度时间等;6.标号,决定该进程 归属;7.打开的一些文件信息;8.进程上下文和内核上下文;9.处理器上下文;10. 内存信息。 由于task_struct结构体比较复杂,因此我们只注意它与进程调度相关的重要部分。 (1) volatile long state 进程所处的状态。在include/linux/sched.h中包含6种状态: #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 #define TASK_ZOMBIE 8 #define TASK_DEAD 16 新增的TASK_DEAD是表示已经退出且不需父进程回收的进程的状态。 (2) struct thread_info *thread_info 当前进程运行的一些环境信息。其中有两个结构成员非常重要,与调度密切相关: __s32 preempt_count; unsigned long flags; preempt_count是用来表示内核能否被抢占的使能成员。如果它大于0,表示内核不能 被抢占;如果等于0,则表示内核处于安全状态(即没有加锁),可以抢占。 flags里面有一个TIF_NEED_RESCHED位,它和Kernel 2.4中need_resched作用一样。 如果此标志位为1,则表示应该尽快启动调度器。 (3) int prio, static_prio prio是进程的动态优先级,相当于Kernel2.4中用goodness()函数计算出来的结果; 在Kernel2.6 中不再是由调度器统一计算,而是独立计算;prio的计算和许多因素有 关,详见"进程优先级的计算"一节。 static_prio则是进程的静态优先级,与nice意义相同。nice的取值仍然是-20 ~ 19 ,数值越小,进程优先级越高。 kernel/sched.c中定义了两个宏来完成将nice转换到prio的取值区间和将prioity转 换到nice取值区间。 #define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) #define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) 可见prioity和nice的关系是: priority = MAX_RT_PRIO+nice+20 (4) struct list_head run_list 前面提到过,就绪进程都是按照优先级进行排列,prio_array中的queue[MAX_PRIO] 存放的是指向每个优先级队列的链头list_head;而同一优先级的进程则是通过run_list 链接在一起。 include/linux/list.h定义了一种抽象的双向链表struct list_head,通过它可以将 任意类型的结构体链接到一起。task_struct也是通过这种方式链接起来的。 (5) prio_array_t *array 指向当前CPU的active array的指针。在进程控制块里面又加了一个指向active array 的指针,看似重复,其实不然。比如说对于下面的代码(kernel/sched.c): array = next->array; dequeue_task(next, array); recalc_task_prio(next, next->timestamp + delta); enqueue_task(next, array); 对于单处理器(UP)的情况,我们确实可以通过runqueue::active直接得到当前的active array;但是对于SMP,就不是这样了,需要引用next的thread_info,再依靠thread_info 中的cpu找到next所在的处理器,找到以后再找到这个cpu上的runqueue,最后得到active 。对于schedule这样频繁调用的函数,这种浪费是不能容忍的。 (6) unsigned long sleep_avg 进程的平均等待时间,单位是纳秒(nanosecond),在0 ~ NS_MAX_SLEEP_AVG范围内 。它的实质是进程等待时间和运行时间的差值。当进程处于等待或者睡眠状态时,该 值变大;当进程运行时,该值变小。 sleep_avg是Kernel 2.6中衡量进程的一个关键指标,它既可以用来衡量进程的交互 程度,也可以用来衡量进程的紧急程度。具体内容将在"平均等待时间sleep_avg"一 节作详细介绍。 (7) long interactive_credit 表示进程交互程度,取值范围在-CREDIT_LIMIT ~ CREDIT_LIMIT+1之间。进程创建的 时候值为1,以后根据不同的情况进行不同的增1、减1;如果一个进程的 interactive_credit 超过CREDIT_LIMIT之后,这个进程就会被认为是交互式进程,同时interactive_credit 的值也就不再改变了(恒为CREDIT_LIMIT+1)。下面将在"交互进程优化"一节详细介 绍。 (8) unsigned long long timestamp 进程发生调度的时间,单位和sleep_avg一样,也是纳秒。它负责纪录以下四种情况 的时间: a. 进程被唤醒的时间: 在activate_task()(kernel/sched.c)中记录(p->timestamp = now)。 b. 进程被切换到expired array的时间: 在schedule()(kernel/sched.c)中记录,当准备进行进程切换的时候,记录下该进 程被切换到expired array的时间(prev->timestamp = now)。 c. 进程被切换到active array的时间: 在schedule()(kernel/sched.c)中记录,进行进程切换的开始,记录下下一个进程 被切换到active array的时间(next->timestamp = now)。 d. 负载均衡相关的赋值 在进行负载均衡的时候,当把一个进程从其他CPU上pull过来的时候需要将该进程的 timestamp设成sched_clock() - (src_rq->timestamp_last_tick - p->timestamp) ,即相对于本CPU被切换下来的时间。 (9) int activated 表示该进程被唤醒的类别: actived=-1 表示该进程并非自愿sleep,其先前状态是TASK_UNINTERRUPTIBLE。在 try_to_wake_up ()中设置。 actived=0 缺省值,表示进程本来就是处于就绪状态。 actived=1 进程先前状态是TASK_INTERRUPTIBLE,但是不是由中断唤醒;这 样的进 程在第一次运行时有credit,以后就没有了。在activate_task()中设置。 actived=2 进程先前状态是TASK_INTERRUPTIBLE,进程被中断唤醒。这样的 进程非 常像交互式进程。在activate_task()中设置。 (10) unsigned long policy 进程的调度策略和2.4一样,有以下几种: SCHED_FIFO 先进先出式调度,除非有更高优先级进程申请运行,否则该进程 将保持运行至退出才让出CPU SCHED_RR 轮转式调度,该进程被调度下来后将被置于运行队列的末尾,以 保证其他实时进程有机会运行) SCHED_OTHER 常规的分时调度策略 (11) unsigned int time_slice, first_time_slice ime_slice是进程剩余的时间片,相当于Kernel 2.4里面counter,但是时间片不再影 响进程的优先级。 first_time_slice用来记录时间片是否是第一次分配(进程创建时),如果值不为0 ,进程退出时将时间片交还给父进程。
不能为空
不能含有 ` 字符,字数8000以内
(CTRL+ENTER提交)
关闭窗口