当前位置:文档之家› Linux 2.6 调度系统分析

Linux 2.6 调度系统分析

Linux 2.6 调度系统分析
Linux 2.6 调度系统分析

1.前言

Linux 的市场非常广阔,从桌面工作站到低端服务器,它都是任何商用操作系统的有力竞争对手。目前,Linux 正全力进军嵌入式系统和高端服务器系统领域,但它的技术缺陷限制了它的竞争力:缺乏对实时任务的支持,多处理机可扩展性差。在 2.4 内核中,造成这两个弱项的关键原因之一就是调度器设计上的缺陷。

2.6 调度系统从设计之初就把开发重点放在更好满足实时性和多处理机并行性上,并且基本实现了它的设计目标。主要设计者,传奇式人物 Ingo Molnar 将新调度系统的特性概括为如下几点:

?继承和发扬 2.4 版调度器的特点:

o交互式作业优先

o轻载条件下调度/唤醒的高性能

o公平共享

o基于优先级调度

o高 CPU 使用率

o SMP 高效亲和

o实时调度和 cpu 绑定等调度手段

?在此基础之上的新特性:

o O(1)调度算法,调度器开销恒定(与当前系统负载无关),实时性能更好

o高可扩展性,锁粒度大幅度减小

o新设计的 SMP 亲和方法

o优化计算密集型的批处理作业的调度

o重载条件下调度器工作更平滑

o子进程先于父进程运行等其他改进

2.4 的就绪队列是一个简单的以 runqueue_head 为头的双向链表,在 2.6 中,就绪队列定义为一个复杂得多的数据结构 struct runqueue,并且,尤为关键的是,每一个 CPU 都将维护一个自己的就绪队列,--这将大大减小竞争。

O(1)算法中很多关键技术都与 runqueue 有关,所以,我们对调度器的分析就先从 runqueue 结构开始。

1) prio_array_t *active, *expired, arrays[2]

runqueue 中最关键的数据结构。每个 CPU 的就绪队列按时间片是否用完分为两部分,分别通过 active 指针和 expired 指针访问,active 指向时间片没用完、当前可被调度的就绪进程,expired 指向时间片已用完的就绪进程。每一类就绪进程都用一个 struct prio_array 的结构表示:

struct prio_array {

int nr_active; /* 本进程组中的进程数 */

struct list_head queue[MAX_PRIO];

/* 以优先级为索引的 HASH 表,见下 */

unsigned long bitmap[BITMAP_SIZE];

/* 加速以上HASH 表访问的位图,见下 */

};

图1:active、expired 数组示例

图中的 task 并不是 task_struct 结构指针,而是 task_struct::run_list,这是一个小技巧,详见下文 run_list 的解释。

在 2.4 版的内核里,查找最佳候选就绪进程的过程是在调度器 schedule() 中进行的,每一次调度都要进行一次(在 for 循环中调用 goodness()),这种查找过程与当前就绪进程的个数相关,因此,查找所耗费的时间是 O(n) 级的,n 是

当前就绪进程个数。正因为如此,调度动作的执行时间就和当前系统负载相关,无法给定一个上限,这与实时性的要求相违背。

在新的 O(1) 调度中,这一查找过程分解为 n 步,每一步所耗费的时间都是 O(1) 量级的。

prio_array 中包含一个就绪队列数组,数组的索引是进程的优先级(共 140 级,详见下 "static_prio" 属性的说明),相同优先级的进程放置在相应数组元素的链表 queue 中。调度时直接给出就绪队列 active 中具有最高优先级的链表中的第一项作为候选进程(参见"调度器"),而优先级的计算过程则分布到各个进程的执行过程中进行(见"优化了的优先级计算方法")。

为了加速寻找存在就绪进程的链表,2.6 核心又建立了一个位映射数组来对应每一个优先级链表,如果该优先级链表非空,则对应位为 1,否则为 0。核心还要求每个体系结构都构造一个 sched_find_first_bit() 函数来执行这一搜索操作,快速定位第一个非空的就绪进程链表。

采用这种将集中计算过程分散进行的算法,保证了调度器运行的时间上限,同时在内存中保留更加丰富的信息的做法也加速了候选进程的定位过程。这一变化简单而又高效,是 2.6 内核中的亮点之一。

arrays 二元数组是两类就绪队列的容器,active 和 expired 分别指向其中一个。active 中的进程一旦用完了自己的时间片,就被转移到 expired 中,并设置好新的初始时间片;而当 active 为空时,则表示当前所有进程的时间片都消耗完了,此时,active 和 expired 进行一次对调,重新开始下一轮的时间片递减过程(参见"调度器")。

回忆一下 2.4 调度系统,进程时间片的计算是比较耗时的,在早期内核版本中,一旦时间片耗尽,就在时钟中断中重新计算时间片,后来为了提高效率,减小时钟中断的处理时间,2.4 调度系统在所有就绪进程的时间片都耗完以后在调度器中一次性重算。这又是一个 O(n) 量级的过程。为了保证 O(1) 的调度器执行时间,2.6 的时间片计算在各个进程耗尽时间片时单独进行,而通过以上所述简单的对调来完成时间片的轮转(参见"调度器")。这又是 2.6 调度系统的一个亮点。

2) spinlock_t lock

runqueue 的自旋锁,当需要对 runqueue 进行操作时,仍然应该锁定,但这个锁定操作只影响一个 CPU 上的就绪队列,因此,竞争发生的概率要小多了。

3) task_t *curr

本 CPU 正在运行的进程。

4) tast_t *idle

指向本 CPU 的 idle 进程,相当于 2.4 中 init_tasks[this_cpu()] 的作用。

5) int best_expired_prio

记录 expired 就绪进程组中的最高优先级(数值最小)。该变量在进程进入expired 队列的时候保存(schedule_tick()),用途见 "expired_timestamp"的解释)。

6) unsigned long expired_timestamp

当新一轮的时间片递减开始后,这一变量记录着最早发生的进程耗完时间片事件的时间(jiffies 的绝对值,在 schedule_tick() 中赋),它用来表征 expired 中就绪进程的最长等待时间。它的使用体现在 EXPIRED_STARVING(rq) 宏上。

上面已经提到,每个 CPU 上维护了两个就绪队列,active 和 expired。一般情况下,时间片结束的进程应该从 active 队列转移到 expired 队列中(schedule_tick()),但如果该进程是交互式进程,调度器就会让其保持在active 队列上以提高它的响应速度。这种措施不应该让其他就绪进程等待过长时间,也就是说,如果 expired 队列中的进程已经等待了足够长时间了,即使是交互式进程也应该转移到 expired 队列上来,排空 active。这个阀值就体现在EXPIRED_STARVING(rq) 上:在 expired_timestamp 和 STARVATION_LIMIT 都不等于 0 的前提下,如果以下两个条件都满足,则 EXPIRED_STARVING() 返回真:

?(当前绝对时间 - expired_timestamp) >= (STARVATION_LIMIT * 队列中所有就绪进程总数 + 1),也就是说 expired 队列中至少有一个进

程已经等待了足够长的时间;

?正在运行的进程的静态优先级比 expired 队列中最高优先级要低(best_expired_prio,数值要大),此时当然应该尽快排空 active 切

换到expired 上来。

7) struct mm_struct *prev_mm

保存进程切换后被调度下来的进程(称之为 prev)的 active_mm 结构指针。因为在 2.6 中 prev 的 active_mm 是在进程切换完成之后释放的(mmdrop()),而此时 prev 的 active_mm 项可能为 NULL,所以有必要在 runqueue 中预先保留。

8) unsigned long nr_running

本 CPU 上的就绪进程数,该数值是 active 和 expired 两个队列中进程数的总和,是说明本 CPU 负载情况的重要参数(详见"调度器相关的负载平衡")。

9) unsigned long nr_switches

记录了本 CPU 上自调度器运行以来发生的进程切换的次数。

10) unsigned long nr_uninterruptible

记录本 CPU 尚处于 TASK_UNINTERRUPTIBLE 状态的进程数,和负载信息有关。

11) atomic_t nr_iowait

记录本 CPU 因等待 IO 而处于休眠状态的进程数。

12) unsigned long timestamp_last_tick

本就绪队列最近一次发生调度事件的时间,在负载平衡的时候会用到(见"调度器相关的负载平衡")。

13) int prev_cpu_load[NR_CPUS]

记录进行负载平衡时各个 CPU 上的负载状态(此时就绪队列中的 nr_running 值),以便分析负载情况(见"调度器相关的负载平衡")。

14) atomic_t *node_nr_running; int prev_node_load[MAX_NUMNODES]

这两个属性仅在 NUMA 体系结构下有效,记录各个 NUMA 节点上的就绪进程数和上一次负载平衡操作时的负载情况(见"NUMA 结构下的调度")。

15) task_t *migration_thread

指向本 CPU 的迁移进程。每个 CPU 都有一个核心线程用于执行进程迁移操作(见"调度器相关的负载平衡")。

16) struct list_head migration_queue

需要进行迁移的进程列表(见"调度器相关的负载平衡")。

2.6 版的内核仍然用 task_struct 来表征进程,尽管对线程进行了优化,但线程的内核表示仍然与进程相同。随着调度器的改进,task_struct 的内容也有了改进,交互式进程优先支持、内核抢占支持等新特性,在 task_struct 中都有所体现。在 task_struct 中,有的属性是新增加的,有的属性的值的含义发生了变化,而有的属性仅仅是改了一下名字。

1) state

进程的状态仍然用 state 表示,不同的是,2.6 里的状态常量重新定义了,以方便位操作:

/* 节选自[include/linux/sched.h] */

#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) timestamp

进程发生调度事件的时间(单位是 nanosecond,见下)。包括以下几类:?被唤醒的时间(在 activate_task() 中设置);

?被切换下来的时间(schedule());

?被切换上去的时间(schedule());

?负载平衡相关的赋值(见"调度器相关的负载平衡")。

从这个值与当前时间的差值中可以分别获得"在就绪队列中等待运行的时长"、"运行时长"等与优先级计算相关的信息(见"优化了的优先级计算方法")。

3) prio

优先级,相当于 2.4 中 goodness() 的计算结果,在 0~MAX_PRIO-1 之间取值(MAX_PRIO 定义为 140),其中 0~MAX_RT_PRIO-1 (MAX_RT_PRIO 定义为100)属于实时进程范围,MAX_RT_PRIO~MX_PRIO-1 属于非实时进程。数值越大,表示进程优先级越小。

2.6 中,动态优先级不再统一在调度器中计算和比较,而是独立计算,并存储在进程的 task_struct 中,再通过上面描述的 priority_array 结构自动排序。

prio 的计算和很多因素相关,在"优化了的优先级计算方法"中会详细讨论。

4) static_prio

静态优先级,与 2.4 的 nice 值意义相同,但转换到与 prio 相同的取值区间。

nice 值沿用 Linux 的传统,在 -20 到 19 之间变动,数值越大,进程的优先级越小。nice 是用户可维护的,但仅影响非实时进程的优先级。2.6 内核中不再存储 nice 值,而代之以 static_prio。进程初始时间片的大小仅决定于进程的静态优先级,这一点不论是实时进程还是非实时进程都一样,不过实时进程的static_prio 不参与优先级计算。

nice 与 static_prio 之间的关系如下:

static_prio = MAX_RT_PRIO + nice + 20

内核定义了两个宏用来完成这一转换:PRIO_TO_NICE()、NICE_TO_PRIO()。

5) activated

表示进程因什么原因进入就绪态,这一原因会影响到调度优先级的计算。activated 有四个值:

?-1,进程从 TASK_UNINTERRUPTIBLE 状态被唤醒;

?0,缺省值,进程原本就处于就绪态;

?1,进程从 TASK_INTERRUPTIBLE 状态被唤醒,且不在中断上下文中;

?2,进程从 TASK_INTERRUPTIBLE 状态被唤醒,且在中断上下文中。

activated 初值为 0,在两个地方修改,一是在 schedule() 中,被恢复为 0,另一个就是 activate_task(),这个函数由 try_to_wake_up() 函数调用,用于激活休眠进程:

?如果是中断服务程序调用的 activate_task(),也就是说进程由中断激活,则该进程最有可能是交互式的,因此,置 activated=2;否则置

activated=1。

?如果进程是从 TASK_UNINTERRUPTIBLE 状态中被唤醒的,则activated=-1(在try_to_wake_up()函数中)。

activated 变量的具体含义和使用见"优化了的优先级计算方式"。

6) sleep_avg

进程的平均等待时间(以 nanosecond 为单位),在 0 到 NS_MAX_SLEEP_AVG 之间取值,初值为 0,相当于进程等待时间与运行时间的差值。sleep_avg 所代表的含义比较丰富,既可用于评价该进程的"交互程度",又可用于表示该进程需要运行的紧迫性。这个值是动态优先级计算的关键因子,sleep_avg 越大,计算出来的进程优先级也越高(数值越小)。在下文"进程平均等待时间 sleep_avg" 中会详细分析 sleep_avg 的变化过程。

7) interactive_credit

这个变量记录了本进程的"交互程度",在 -CREDIT_LIMIT 到 CREDIT_LIMIT+1 之间取值。进程被创建出来时,初值为 0,而后根据不同的条件加 1 减 1,一旦超过 CREDIT_LIMIT(只可能等于 CREDIT_LIMIT+1),它就不会再降下来,表示进程已经通过了"交互式"测试,被认为是交互式进程了。interactive_credit 具体的变化过程在"更精确的交互式进程优先"中会详细描述。

8) nvcsw/nivcsw/cnvcsw/cnivcsw

进程切换计数。

9) time_slice

进程的时间片余额,相当于 2.4 的 counter,但不再直接影响进程的动态优先级。在"新的运行时间片表现"中专门分析了 time_slice 的行为。

10) first_time_slice

0 或 1,表示是否是第一次拥有时间片(刚创建的进程)。这一变量用来判断进程结束时是否应当将自己的剩余时间片返还给父进程(见"新的运行时间片表现")。

11) run_list

前面提到,优先级数组 prio_array 结构中按顺序排列了各个优先级下的所有进程,但实际上数组中每一个元素都是 list_head 结构,以它为表头的链表中的每一个元素也是 list_head,其中链接的就是 task_struct 中的 run_list 成员。这是一个节省空间、加速访问的小技巧:调度器在 prio_array 中找到相应的 run_list,然后通过 run_list 在 task_struct 中的固定偏移量找到对应的task_struct(参见 enqueue_task()、dequeue_task() 和 list.h 中的操作)。

12) array

记录当前 CPU 的活跃就绪队列(runqueue::active)。

13) thread_info

当前进程的一些运行环境信息,其中有两个结构成员与调度关系紧密:?preempt_count:初值为 0 的非负计数器,大于 0 表示核心不宜被抢占;

?flags:其中有一个 TIF_NEED_RESCHED 位,相当于 2.4 中的need_resched 属性,如果当前运行中的进程此位为 1,则表示应该尽快

启动调度器。

在 2.4 中,每个进程的 task_struct 都位于该进程核心栈的顶端(低址部分),内核可以通过栈寄存器 ESP 轻松访问到当前进程的 task_struct。在 2.6 中,仍然需要频繁访问这个名为 current 的数据结构,但现在,进程核心栈顶保存的是其中的 thread_info 属性,而不是完整的 task_struct 了。这样做的好处是仅将最关键的、访问最频繁的运行环境保存在核心栈里(仍然是两个页大小),而将 task_struct 大部分内容通过 thread_info::task 指针保存在栈外,以方便扩充。thread_info 的分配方式和访问方式与 2.4 中的 task_struct 完全相同,现在的 current 需要这样来访问:

/* 节选自[include/asm-i386/current.h] */

static inline struct task_struct * get_current(void)

{

return current_thread_info()->task;

}

#define current get_current()

其中current_thread_info()定义为:

/* 节选自[include/asm-i386/thread_info.h] */

static inline struct thread_info *current_thread_info(void)

{

struct thread_info *ti;

__asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~8191UL));

return ti;

}

MIN_TIMESLICE + ((MAX_TIMESLICE -

MIN_TIMESLICE) *

(MAX_PRIO-1 - (p)->static_prio) /

(MAX_USER_PRIO-1))

代入各个宏的值后,结果如图所示:

可见,核心将 100~139 的优先级映射到 200ms~10ms 的时间片上去,优先级数值越大,则分配的时间片越小。

和 2.4 中进程的缺省时间片比较,当 nice 为 0 时,2.6 的基准值 100ms 要

大于 2.4 的 60ms。

2) time_slice 的变化

进程的 time_slice 值代表进程的运行时间片剩余大小,在进程创建时与父进程平分时间片,在运行过程中递减,一旦归 0,则按 static_prio 值重新赋予上述基准值,并请求调度。时间片的递减和重置在时钟中断中进行(sched_tick()),除此之外,time_slice 值的变化主要在创建进程和进程退出过程中:

a) 进程创建

和 2.4 类似,为了防止进程通过反复 fork 来偷取时间片,子进程被创建时并不分配自己的时间片,而是与父进程平分父进程的剩余时间片。也就是说,fork 结束后,两者时间片之和与原先父进程的时间片相等。

b) 进程退出

进程退出时(sched_exit()),根据 first_time_slice 的值判断自己是否从未重新分配过时间片,如果是,则将自己的剩余时间片返还给父进程(保证不超过MAX_TIMESLICE)。这个动作使进程不会因创建短期子进程而受到惩罚(与不至于因创建子进程而受到"奖励"相对应)。如果进程已经用完了从父进程那分得的时间片,就没有必要返还了(这一点在 2.4 中没有考虑)。

3) time_slice 对调度的影响

在 2.4 中,进程剩余时间片是除 nice 值以外对动态优先级影响最大的因素,并且休眠次数多的进程,它的时间片会不断叠加,从而算出的优先级也更大,调度器正是用这种方式来体现对交互式进程的优先策略。但实际上休眠次数多并不表示该进程就是交互式的,只能说明它是 IO 密集型的,因此,这种方法精度很低,有时因为误将频繁访问磁盘的数据库应用当作交互式进程,反而造成真正的用户终端响应迟缓。

2.6 的调度器以时间片是否耗尽为标准将就绪进程分成 active、expired 两大类,分别对应不同的就绪队列,前者相对于后者拥有绝对的调度优先权--仅当active 进程时间片都耗尽,expired 进程才有机会运行。但在 active 中挑选进程时,调度器不再将进程剩余时间片作为影响调度优先级的一个因素,并且为了满足内核可剥夺的要求,时间片太长的非实时交互式进程还会被人为地分成好几段(每一段称为一个运行粒度,定义见下)运行,每一段运行结束后,它都从cpu 上被剥夺下来,放置到对应的 active 就绪队列的末尾,为其他具有同等优先级的进程提供运行的机会。

这一操作在 schedule_tick() 对时间片递减之后进行。此时,即使进程的时间片没耗完,只要该进程同时满足以下四个条件,它就会被强制从 cpu 上剥夺下来,重新入队等候下一次调度:

?进程当前在 active 就绪队列中;

?该进程是交互式进程(TASK_INTERACTIVE()返回真,见"更精确的交互式进程优先",nice 大于 12 时,该宏返回恒假);

?该进程已经耗掉的时间片(时间片基准值减去剩余时间片)正好是运行粒度的整数倍;

?剩余时间片不小于运行粒度

(NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / MAX_SLEEP_AVG) -

MAX_BONUS/2

如下图所示:

再用这个 bonus 去减静态优先级就得到进程的动态优先级(并限制在

MAX_RT_PRIO和MAX_PRIO 之间),bonus 越小,动态优先级数值越大,优先级越低。也就是说,sleep_avg 越大,优先级也越高。

MAX_BONUS 定义为 MAX_USER_PRIO*PRIO_BONUS_RATIO/100,也就是说,

sleep_avg 对动态优先级的影响仅在静态优先级的用户优先级区(100~140)的1/4区间(±5)之内,相对而言,静态优先级,也就是用户指定的 nice 值在优先级计算的比重要大得多。这也是 2.6 调度系统中变化比较大的一个地方,调度器倾向于更多地由用户自行设计进程的执行优先级。

sleep_avg 反映了调度系统的两个策略:交互式进程优先和分时系统的公平共享,在下一节中我们还要专门分析。

2) 优先级计算时机

优先级的计算不再集中在调度器选择候选进程的时候进行了,只要进程状态发生改变,核心就有可能计算并设置进程的动态优先级:

a) 创建进程

在wake_up_forked_process()中,子进程继承了父进程的动态优先级,并添加到父进程所在的就绪队列中。

如果父进程不在任何就绪队列中(例如它是 IDLE 进程),那么就通过

effect_prio() 函数计算出子进程的优先级,而后根据计算结果将子进程放置到相应的就绪队列中。

b) 唤醒休眠进程

核心调用 recalc_task_prio() 设置从休眠状态中醒来的进程的动态优先级,再根据优先级放置到相应就绪队列中。

c) 调度到从 TASK_INTERRUPTIBLE 状态中被唤醒的进程

实际上此时调度器已经选定了候选进程,但考虑到这一类型的进程很有可能是交互式进程,因此此时仍然调用 recalc_task_prio() 对该进程的优先级进行修正(详见"进程平均等待时间 sleep_avg"),修正的结果将在下一次调度时体现。

d) 进程因时间片相关的原因被剥夺 cpu

在 schedule_tick() 中(由时钟中断启动),进程可能因两种原因被剥夺 cpu,一是时间片耗尽,一是因时间片过长而分段。这两种情况都会调用effect_prio() 重新计算优先级,重新入队。

e) 其它时机

这些其它时机包括 IDLE 进程初始化(init_idle())、负载平衡

(move_task_away(),详见"调度器相关的负载平衡")以及修改 nice 值

(set_user_nice())、修改调度策略(setscheduler())等主动要求改变优先级的情况。

由上可见,2.6 中动态优先级的计算过程在各个进程运行过程中进行,避免了类似 2.4 系统中就绪进程很多时计算过程耗时过长,从而无法预计进程的响应时间的问题。同时,影响动态优先级的因素集中反映在 sleep_avg 变量上。

回页首

6.进程平均等待时间 sleep_avg

进程的 sleep_avg 值是决定进程动态优先级的关键,也是进程交互程度评价的关键,它的设计是 2.6 调度系统中最为复杂的一个环节,可以说,2.6 调度系统的性能改进,很大一部分应该归功于 sleep_avg 的设计。这一节,我们将专门针对 sleep_avg 的变化和它对调度的影响进行分析。

内核中主要有四个地方会对 sleep_avg 进行修改:休眠进程被唤醒时

(activate_task()调用 recalc_task_prio() 函数)、TASK_INTERRUPTIBLE 状态的进程被唤醒后第一次调度到(schedule()中调用 recalc_task_prio())、进程从 CPU 上被剥夺下来(schedule()函数中)、进程创建和进程退出,其中recalc_task_prio() 是其中复杂度最高的,它通过计算进程的等待时间(或者是在休眠中等待,或者是在就绪队列中等待)对优先级的影响来重置优先级。

1) 休眠进程被唤醒时

此时 activate_task() 以唤醒的时间作为参数调用 recalc_task_prio(),计算休眠等待的时间对优先级的影响。

在 recalc_task_prio() 中,sleep_avg 可能有四种赋值,并最终都限制在

NS_MAX_SLEEP_AVG 以内:

a) 不变

从 TASK_UNINTERRUPTIBLE 状态中被唤醒(activated==-1)、交互程度不够高(!HIGH_CREDIT(p))的用户进程(p->mm!=NULL)),如果它的 sleep_avg 已经不小于 INTERACTIVE_SLEEP(p) 了,则它的 sleep_avg 不会因本次等待而改变。

b) INTERACTIVE_SLEEP(p)

从 TASK_UNINTERRUPTIBLE 状态中被唤醒(activated==-1)、交互程度不够高(!HIGH_CREDIT(p))的用户进程(p->mm!=NULL)),如果它的 sleep_avg 没有达到 INTERACTIVE_SLEEP(p),但如果加上本次休眠时间 sleep_time 就达到了,则它的 sleep_avg 就等于 INTERACTIVE_SLEEP(p)。

c) MAX_SLEEP_AVG-AVG_TIMESLICE

用户进程(p->mm!=NULL),如果不是从 TASK_UNINTERRUPTIBLE 休眠中被唤醒的(p->activated!=-1),且本次等待的时间(sleep_time)已经超过了INTERACTIVE_SLEEP(p),则它的 sleep_avg 置为

JIFFIES_TO_NS(MAX_SLEEP_AVG-AVG_TIMESLICE)。

d) sleep_avg+sleep_time

如果不满足上面所有情况,则将 sleep_time 叠加到 sleep_avg 上。此时,sleep_time 要经过两次修正:

i. 根据 sleep_avg 的值进行修正,sleep_avg 越大,修正后的 sleep_time 越小:

sleep_time =

sleep_time * MAX_BONUS * (1-NS_TO_JIFFIES(sleep_avg)/MAX_SLEEP_AVG)

ii. 如果进程交互程度很低(LOW_CREDIT()返回真,见"更精确的交互式进程优先"),则将 sleep_time 限制在进程的基准时间片以内,以此作为对 cpu-bound 的进程的优先级惩罚。

总的来说,本次等待时间越长,sleep_avg 就应该更大,但核心排除了两种情况:

?从 TASK_UNINTERRUPTIBLE 状态醒来的进程,尤其是那些休眠时间比较长的进程,很有可能是等待某种资源而休眠,它们不应该受到等待时间的优

先级奖励,它的 sleep_avg 被限制在 INTERACTIVE_SLEEP(p) 范围内(或不超过太多)(INTERACTIVE_SLEEP(p) 的含义见后),--这一限制对已

经认定为是交互式的进程无效。

?不是从 TASK_UNITERRUPTIBLE 状态中醒来的进程,如果本次等待时间过长,也不应该受到等待时间的优先级奖励。

INTERACTIVE_SLEEP(p) 与 nice 的关系

NS_TO_JIFFIES(INTERACTIVE_SLEEP(p))=

TASK_NICE(p)*MAX_SLEEP_AVG/40+

(INTERACTIVE_DELTA+1+MAX_BONUS/2)*MAX_SLEEP_AVG/MAX_BONUS -1

-->

这个宏与进程 nice 值成正比,说明优先级越低的进程,允许它在"交互式休眠"状态下的时间越长。

2) TASK_INTERRUPTIBLE 状态的进程被唤醒后第一次被调度到时

调度器挑选出候选进程之后,如果发现它是从 TASK_INTERRUPTIBLE 休眠中醒来后第一次被调度到(activated>0),调度器将根据它在就绪队列上等待的时长调用 recalc_task_prio() 重新调整进程的 sleep_avg。

recalc_task_prio() 调整的过程与"休眠进程被唤醒时"的情况是一模一样的,所不同的是,此时作为等待时间 sleep_time 参与计算的不是进程的休眠时间,而是进程在就绪队列上等待调度的时间。并且,如果进程不是被中断唤醒的(activated==1),sleep_time 还将受到约束

(delta=delta*ON_RUNQUEUE_WEIGHT/100),因为此时该进程不是交互式进程的可能性很大。从上面对 recalc_task_prio() 的分析可知,sleep_time 减小一般来说就意味着优先级会相应降低,所以,这一奖励说明调度器在进一步减小进程的响应时间,尤其是交互式进程。

3) 被切换下来的进程

前面说过,sleep_avg 是进程的"平均"等待时间,recalc_task_prio() 计算了等待时间,在 schedule() 中,被切换下来的进程的 sleep_avg 需要减去进程本次运行的时间 run_time(并保证结果不小于 0),这就是对"平均"的体现:等待得越久,sleep_avg 越大,进程越容易被调度到;而运行得越久,sleep_avg 越小,进程越不容易调度到。

run_time 可以用系统当前时间与进程 timestamp(上一次被调度运行的时间)的差值表示,但不能超过 NS_MAX_SLEEP_AVG。对于交互式进程(HIGHT_CREDIT(p) 为真,见"更精确的交互式进程优先"),run_time 还要根据 sleep_avg 的值进行调整:

run_time = run_time / (sleep_avg*MAX_BONUS/MAX_SLEEP_AVG)

这样调整的结果是交互式进程的 run_time 小于实际运行时间,sleep_avg 越大,则 run_time 减小得越多,因此被切换下来的进程最后计算所得的

sleep_avg 也就越大,动态优先级也随之变大。交互式进程可以借此获得更多被执行的机会。

4) fork 后

在 wake_up_forked_process() 中,父进程的 sleep_avg 要乘以

PARENT_PENALTY/100,而子进程的 sleep_avg 则乘以 CHILD_PENALTY/100。实际上PARENT_PENALTY 为 100,CHILD_PENALTY 等于 95,也就是说父进程的sleep_avg 不会变,而子进程从父进程处继承过来的 sleep_avg 会减小 5%,因此子进程最后的优先级会比父进程稍低(但子进程仍然会置于与父进程相同的就绪队列上,位置在父进程之前--也就是"前言"所说"子进程先于父进程运行")。

5) 进程退出时

一个进程结束运行时,如果它的交互程度比父进程低(sleep_avg 较小),那么核心将在 sched_exit() 中对其父进程的 sleep_avg 进行调整,调整公式如下(以 child_sleep_avg 表示子进程的 sleep_avg):

sleep_avg=

sleep_avg*EXIT_WEIGHT/(EXIT_WEIGHT+1) +

超过了 CREDIT_LIMIT 的阀值(HIGH_CREDIT()返回真),该进程就被认为是交互式进程。

interactive_credit 的初始值为 0,在两种情况下它会加 1,这两种场合都在recalc_task_prio() 函数中:

?用户进程(p->mm!=NULL),如果不是从 TASK_UNINTERRUPTIBLE 休眠中被唤醒的(p->activated!=-1),且等待的时间(包括在休眠中等待和在就绪队列中等待,)超过了一定限度(sleep_time>INTERACTIVE_SLEEP(p));

?除以上情况外,sleep_avg 经过 sleep_time 调整后,如果大于NS_MAX_SLEEP_AVG。

无论哪种情况,一旦 interactive_credit 超过(大于)CREDIT_LIMIT 了,它都不再增加,因此 interactive_credit 最大值就是 CREDIT_LIMIT+1。

interactive_credit 的递减发生在 schedule() 函数中。当调度器用运行时间修正被切换下来的进程的 sleep_avg 之后,如果 sleep_avg 小于等于 0,且interactive_credit 在 -CREDIT_LIMIT 和 CREDIT_LIMIT 之间

(-100<=interactive_credit<=100),则 interactive_credit 减 1。可见interactive_credit 最小值为 -101,且一旦它达到 CREDIT_LIMIT+1 的最大值就不会再被减下来--它将保持在 CREDIT_LIMIT+1 的高值上。

这就是说,只有进程多次休眠,且休眠的时间足够长(长于运行的时间,长于"交互式休眠"时间),进程才有可能被列为交互式进程;而一旦被认为是交互式进程,则永远按交互式进程对待。

采用 HIGH_CREDIT() 标准断言的交互式进程主要在以下两处得到优先级计算上的奖励:

?当进程从 cpu 上调度下来的时侯,如果是交互式进程,则它参与优先级计算的运行时间会比实际运行时间小,以此获得较高的优先级(见"进程

平均等待时间 sleep_avg");

?交互式进程处于 TASK_UNINTERRUPTIBLE 状态下的休眠时间也会叠加到sleep_avg 上,从而获得优先级奖励(见"进程平均等待时间

sleep_avg");

3) TASK_INTERACTIVE()

核心另有一处不采用 HIGH_CREDIT() 这种累积方式来判断的交互式进程优先机制,那里使用的是 TASK_INTERACTIVE() 宏(布尔值):

prio <= static_prio-DELTA(p)

当进程时间片耗尽时,如果该宏返回真(同时 expired 队列没有等待过长的时间,见"新的数据结构 runqueue""expired_timestamp"条),则该进程不进入expired 队列而是保留在 active 队列中,以便尽快调度到这一交互式进程。动态优先级在调度到该进程时在 effective_prio() 中算出:

prio=static_prio-bonus(sleep_avg)(bonus(sleep_avg) 表示 bonus 是关于sleep_avg 的函数,见"优先级计算过程"),而 DELTA(p) 与进程的 nice 值有关,可表示为delta(nice)。bonus 与 sleep_avg 的关系在"优先级计算过程"一节中已经用图说明了,delta 和 nice 之间的关系见下图:

nice 值的范围是 -20~19,DELTA(p)将其转换到 -5~+5 再加上一个INTERACTIVE_DELTA常量的范围内:TASK_NICE(p) * MAX_BONUS/40 + INTERACTIVE_DELTA,其中 INTERACTIVE_DELTA 等于 2。

经过转换,TASK_INTERACTIVE(p) 变为 "delta(nice) 是否不大于

bonus(sleep_avg)"。将 sleep_avg 表示为 JIFFIES 的形式,并代入常数,delta(nice)<=bonus(sleep_avg) 可以表示为:

换下来的进程重新调度到另一个 cpu 上的动作也省略了。调度器的基本流程仍然可以概括为相同的五步:

?清理当前运行中的进程(prev)

?选择下一个投入运行的进程(next)

?设置新进程的运行环境

?执行进程上下文切换

?后期整理

2.6 的调度器工作流程保留了很多 2.4 系统中的动作,进程切换的细节也与2.4 基本相同(由 context_switch() 开始)。为了不与 2.4 系统的调度器分析重复,我们按照调度器对各个数据结构的影响来分析调度器的工作流程,重点在与 2.4 调度器不同的部分,与之相同或相似的部分相信读者结合代码和上文的技术分析很容易理解。同时,2.6 的调度器中还增加了对负载平衡和内核抢占运行的支持,因为内容比较独立,我们也将其放在单独的章节中。

1) 相关锁

主要是因为就绪队列分布到各个 cpu 上了,2.6 调度器中仅涉及两个锁的操作:就绪队列锁 runqueue::lock,全局核心锁 kernel_flag。对就绪队列锁的操作保证了就绪队列的操作唯一性,核心锁的意义与 2.4 中相同:调度器在执行切换之前应将核心锁解开(release_kernel_lock()),完成调度后恢复锁状态(reacquire_kernel_lock())。进程的锁状态依然保存在

task_struct::lock_depth属性中。

因为调度器中没有任何全局的锁操作,2.6 调度器本身的运行障碍几乎不存在了。

2) prev

调度器主要影响 prev 进程的两个属性:

?sleep_avg 减去了本进程的运行时间(详见"进程平均等待时间sleep_avg"的"被切换下来的进程");

?timestamp 更新为当前时间,记录被切换下去的时间,用于计算进程等待时间。

prev被切换下来后,即使修改了 sleep_avg,它在就绪队列中的位置也不会改变,它将一直以此优先级参加调度直至发生状态改变(比如休眠)。

3) next

在前面介绍 runqueue 数据结构的时候,我们已经分析了 active/expired 两个按优先级排序的就绪进程队列的功能,2.6 的调度器对候选进程的定位有三种可能:

linux系统故障与分析

由于管理员忘记密码,或者由于系统受到黑客的入侵,系统密码文件被修改。导致管理员可能无法用帐号登录系统。 解决方法: 方法一,在系统启动时,进入单用户模式(linux single),然后用passwd命令重新设置密码,或修改密码文件即可恢复正常。 方法二,用急救启动盘组启动系统,然后将硬盘的文件系统安装到/mnt目录下,编辑/mnt/etc/passwd文件进行恢复。 方法三、将安装系统的硬盘拆下来,放在另一个Linux系统中,然后(mount)挂载此硬盘的系统安装区,将次硬盘分区中的/etc/passwd, /etc/shadow,/etc/group文件覆盖或修改,也可以恢复。 案例二:硬盘扇区错乱 在启动过程中最容易遇到的问题就是硬盘可能有坏道或扇区错乱(数据损坏)的情况,这种情况多由于异常断电、不正常关机导致。此种问题发生,在系统启动的时候,屏幕会显示: Press root password or ctrl+D:此时输入root密码系统自动进入单用户模式,输入“fsck -y /dev/hda6”(fsck为文件系统检测修复命令,“-y”设定检测到错误自动修复,/dev/hda6为发生错误的硬盘分区,请依据具体情况更改此参数),系统修复完成后,用命令“reboot”重新启动即可。 案例三、GRUB选项设置错误 “Error 15”显示系统无法找到grub.conf中指定的内核。 GRUB引导错误信息,我们观察发现因为打字错误,内核文件的“vmlinuz”打成了“vmlinux”,所以系统无法找到内核的可执行文件。我们可以按任意键回到GRUB编辑界面,修改此错误,回车保存后按“b”键即可正常引导,当然不要忘记进入系统后修改grub.conf文件中此处错误。这是很多初学Linux的用户在修改GRUB设置时很容易犯的错误,出现此黑屏提示时注意观察报错信息,即可针对性修复。 (二)GRUB引导故障排除 我发现有时Linux启动后会直接进入GRUB命令行界面(只有“grub>”提示符),此时很多用户就选择了重新安装GRUB甚至重新安装系统。其实一般而言此故障的原因最常见的有两个:一是GRUB配置文件中选项设置错误;二是GRUB配置文件丢失(还有少数原因,如内核文件或镜像文件损坏、丢失,/boot目录误删除等),如果是第一种情况,可以首先通过GRUB命令引导系统后修复;若是第二种情况,则要使用Linux救援模式修复了(本文后续有描述)。

fork函数和子进程

Fork函数 函数pid_t fork(void) 正确返回:在父进程中返回子进程的进程号,在子进程中返回0 错误返回:-1 子进程是父进程的一个拷贝。即,子进程从父进程得到了数据段和堆栈段的拷贝,这些需要分配新的内存;而对于只读的代码段,通常使用共享内存的方式访问。fork返回后,子进程和父进程都从调用fork函数的下一条语句开始执行。父进程与子进程的不同之处在于:fork的返回值不同——父进程中的返回值为子进程的进程号,而子进程为0。 以下是fork的两个示例程序: //fork.c #include #include void main () { int pid; //printf("Process [%d] begin",getpid()); //print twice printf("Process [%d] begin\n",getpid()); //print once //由于fork时pc等值的拷贝,子进程只会从fork处开始执行 pid = fork(); if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("I'm child process, my pid is %d\n", getpid()); else printf("I'm parent process, my pid is %d\n", getpid()); printf("Process [%d] end\n",getpid()); return; } 输出结果: 使用printf("Process [%d] begin\n",getpid())时 Process [11155] begin I'm parent process, my pid is 11155

结构动力特性测试方法及原理

结构动力特性的测试方法及应用(讲稿) 一. 概述 每个结构都有自己的动力特性,惯称自振特性。了解结构的动力特性是进行结构抗震设 计和结构损伤检测的重要步骤。目前,在结构地震反应分析中,广泛采用振型叠加原理的反 应谱分析方法,但需要以确定结构的动力特性为前提。n 个自由度的结构体系的振动方程如 下: [][][]{}{})()()()(...t p t y K t y C t y M =+? ?????+?????? 式中[]M 、[]C 、[]K 分别为结构的总体质量矩阵、阻尼矩阵、刚度矩阵,均为n 维矩阵; {})(t p 为外部作用力的n 维随机过程列阵;{})(t y 为位移响应的n 维随机过程列阵;{} )(t y &为速度响应的n 维随机过程列阵;{})(t y && 为加速度响应的n 维随机过程列阵。 表征结构动力特性的主要参数是结构的自振频率f (其倒数即自振周期T )、振型Y(i)和 阻尼比ξ,这些数值在结构动力计算中经常用到。 任何结构都可看作是由刚度、质量、阻尼矩阵(统称结构参数)构成的动力学系统, 结构一旦出现破损,结构参数也随之变化,从而导致系统频响函数和模态参数的改变,这种 改变可视为结构破损发生的标志。这样,可利用结构破损前后的测试动态数据来诊断结构的破损,进而提出修复方案,现代发展起来的“结构破损诊断”技术就是这样一种方法。其最 大优点是将导致结构振动的外界因素作为激励源,诊断过程不影响结构的正常使用,能方便 地完成结构破损的在线监测与诊断。从传感器测试设备到相应的信号处理软件,振动模态测 量方法已有几十年发展历史,积累了丰富的经验,振动模态测量在桥梁损伤检测领域的发展 也很快。随着动态测试、信号处理、计算机辅助试验技术的提高,结构的振动信息可以在桥 梁运营过程中利用环境激振来监测,并可得到比较精确的结构动态特性(如频响函数、模态 参数等)。目前,许多国家在一些已建和在建桥梁上进行该方面有益的尝试。 测量结构物自振特性的方法很多,目前主要有稳态正弦激振法、传递函数法、脉动测试 法和自由振动法。稳态正弦激振法是给结构以一定的稳态正弦激励力,通过频率扫描的办法 确定各共振频率下结构的振型和对应的阻尼比。 传递函数法是用各种不同的方法对结构进 行激励(如正弦激励、脉冲激励或随机激励等),测出激励力和各点的响应,利用专用的分 析设备求出各响应点与激励点之间的传递函数,进而可以得出结构的各阶模态参数(包括振 型、频率、阻尼比)。脉动测试法是利用结构物(尤其是高柔性结构)在自然环境振源(如 风、行车、水流、地脉动等)的影响下,所产生的随机振动,通过传感器记录、经谱分析, 求得结构物的动力特性参数。自由振动法是:通过外力使被测结构沿某个主轴方向产生一定 的初位移后突然释放,使之产生一个初速度,以激发起被测结构的自由振动。 以上几种方法各有其优点和局限性。利用共振法可以获得结构比较精确的自振频率和阻 尼比,但其缺点是,采用单点激振时只能求得低阶振型时的自振特性,而采用多点激振需较 多的设备和较高的试验技术;传递函数法应用于模型试验,常常可以得到满意的结果,但对 于尺度很大的实际结构要用较大的激励力才能使结构振动起来,从而获得比较满意的传递函 数,这在实际测试工作中往往有一定的困难。 利用环境随机振动作为结构物激振的振源,来测定并分析结构物固有特性的方法,是近 年来随着计算机技术及FFT 理论的普及而发展起来的,现已被广泛应用于建筑物的动力分 析研究中,对于斜拉桥及悬索桥等大型柔性结构的动力分析也得到了广泛的运用。斜拉桥或 悬索桥的环境随机振源来自两方面:一方面指从基础部分传到结构的地面振动及由于大气变 化而影响到上部结构的振动(根据动力量测结果,可发现其频谱是相当丰富的,具有不同的

Linux操作系统源代码详细分析

linux源代码分析:Linux操作系统源代码详细分析 疯狂代码 https://www.doczj.com/doc/2a2116928.html,/ ?:http:/https://www.doczj.com/doc/2a2116928.html,/Linux/Article28378.html 内容介绍: Linux 拥有现代操作系统所有功能如真正抢先式多任务处理、支持多用户内存保护虚拟内存支持SMP、UP符合POSIX标准联网、图形用户接口和桌面环境具有快速性、稳定性等特点本书通过分析Linux内核源代码充分揭示了Linux作为操作系统内核是如何完成保证系统正常运行、协调多个并发进程、管理内存等工作现实中能让人自由获取系统源代码并不多通过本书学习将大大有助于读者编写自己新 第部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8 arch/i386/kernel/irq.h 19 arch/i386/kernel/process.c 22 arch/i386/kernel/signal.c 30 arch/i386/kernel/smp.c 38 arch/i386/kernel/time.c 58 arch/i386/kernel/traps.c 65 arch/i386/lib/delay.c 73 arch/i386/mm/fault.c 74 arch/i386/mm/init.c 76 fs/binfmt-elf.c 82 fs/binfmt_java.c 96 fs/exec.c 98 /asm-generic/smplock.h 107 /asm-i386/atomic.h 108 /asm- i386/current.h 109 /asm-i386/dma.h 109 /asm-i386/elf.h 113 /asm-i386/hardirq.h 114 /asm- i386/page.h 114 /asm-i386/pgtable.h 115 /asm-i386/ptrace.h 122 /asm-i386/semaphore.h 123 /asm-i386/shmparam.h 124 /asm-i386/sigcontext.h 125 /asm-i386/siginfo.h 125 /asm-i386/signal.h 127 /asm-i386/smp.h 130 /asm-i386/softirq.h 132 /asm-i386/spinlock.h 133 /asm-i386/system.h 137 /asm-i386/uaccess.h 139 //binfmts.h 146 //capability.h 147 /linux/elf.h 150 /linux/elfcore.h 156 /linux/errupt.h 157 /linux/kernel.h 158 /linux/kernel_stat.h 159 /linux/limits.h 160 /linux/mm.h 160 /linux/module.h 164 /linux/msg.h 168 /linux/personality.h 169 /linux/reboot.h 169 /linux/resource.h 170 /linux/sched.h 171 /linux/sem.h 179 /linux/shm.h 180 /linux/signal.h 181 /linux/slab.h 184 /linux/smp.h 184 /linux/smp_lock.h 185 /linux/swap.h 185 /linux/swapctl.h 187 /linux/sysctl.h 188 /linux/tasks.h 194 /linux/time.h 194 /linux/timer.h 195 /linux/times.h 196 /linux/tqueue.h 196 /linux/wait.h 198 init/.c 198 init/version.c 212 ipc/msg.c 213 ipc/sem.c 218 ipc/shm.c 227 ipc/util.c 236 kernel/capability.c 237 kernel/dma.c 240 kernel/exec_do.c 241 kernel/exit.c 242 kernel/fork.c 248 kernel/info.c 255 kernel/itimer.c 255 kernel/kmod.c 257 kernel/module.c 259 kernel/panic.c 270 kernel/prk.c 271 kernel/sched.c 275 kernel/signal.c 295 kernel/softirq.c 307 kernel/sys.c 307 kernel/sysctl.c 318 kernel/time.c 330 mm/memory.c 335 mm/mlock.c 345 mm/mmap.c 348 mm/mprotect.c 358 mm/mremap.c 361 mm/page_alloc.c 363 mm/page_io.c 368 mm/slab.c 372 mm/swap.c 394 mm/swap_state.c 395 mm/swapfile.c 398 mm/vmalloc.c 406 mm/vmscan.c 409

《Linux操作系统(第2版))》课后习题答案

《Linux操作系统(第2版)》课后习题答案 1.6 练习题 一、选择题 1.Linux最早是由计算机爱好者 B 开发的。 A.RichardPetersen B.LinusTorvalds C.RobPick D.LinuxSarwar 2. 下列C是自由软件。 A.WindowsXP B.UNIX C.Linux D.Windows2000 3. 下列 B 不是Linux 的特点。 A.多任务 B. 单用户 C.设备独立性 D.开放性 4. Linux的内核版本2.3.20 是 A 的版本。 A.不稳定 B.稳定的 C.第三次修订 D.第二次修订 5. Linux安装过程中的硬盘分区工具是 D 。 A.PQmagic B.FDISK C.FIPS D.DiskDruid 6. Linux的根分区系统类型是C。 A.FATl6 B.FAT32 C.ext4 D.NTFS 二、填空题 1.GNU的含义是:GNU'sNotUNIX。 2 . Linux一般有3个主要部分:内核(kernel)、命令解释层(Shell或其他操作环境)、实用工具。 3 . 安装Linux最少需要两个分区,分别是swap交换分区和/(根)分区。 4 . Linux默认的系统管理员账号是root 。 三、简答题(略) 1.简述RedHatLinux系统的特点,简述一些较为知名的Linux发行版本。 2.Linux有哪些安装方式 ?安装RedHatLinux系统要做哪些准备工作? 3.安装RedHatLinux系统的基本磁盘分区有哪些? 4.RedHatLinux系统支持的文件类型有哪些?

2.6 练习题 一、选择题 1. C 命令能用来查找在文件TESTFILE 中包含四个字符的行? A.grep ’????’TESTFILE B.grep ’?.’TESTFILE C.grep ’^????$’TESTFILE D.grep ’^?.$’TESTFILE 2. B 命令用来显示/home 及其子目录下的文件名。 A.ls-a/home B.ls-R/home C.ls-l/home D.ls-d/home 3. 如果忘记了ls 命令的用法,可以采用 C 命令获得帮助 A.?ls B.helpls C.manls D.getls 4. 查看系统当中所有进程的命令是 D 。 A.psall B.psaix C.psauf D.psaux 5. L inux 中有多个查看文件的命令,如果希望在查看文件内容过程中用光标可以上下移 动来查看文件内容,则符合要求的那一个命令是 C 。 A.cat B.more C.les s D.head 6. C 命令可以了解您在当前目录下还有多大空间。 https://www.doczj.com/doc/2a2116928.html,edf B. Usedu/ https://www.doczj.com/doc/2a2116928.html,edu. D. Usedf. 7. 假如需要找出 /etc/my.con f 文件属于哪个包( package ),可以执行 C 命令。 A. rpm-q/etc/my.conf B.rpm-requires/etc/my.conf C. rpm-qf/etc/my.conf D.rpm-q|grep/etc/my.conf 8. 在应用程序启动时, B 命令设置进程的优先级。 A. priori ty B. nice C. topD.、setpri 9. C 命令可以把 f1.txt 复制为f2.txt ? A.cpf1.txt|f2.t xt B. catf1.txt|f2.txt C.catf1.txt>f2.txt D. copyf1.txt|f2.txt 10. 使用B 命令可以查看Linux 的启动信息。 A. mesg –d B. dmesg C. cat/etc/mesg D. cat/var/mesg 二、填空题 1.在Linux 系统中命令区分大小写。在命令行中,可以使用 Tab 键来自动补齐命令。 2. 如果要在一个命令行上输入和执行多条命令,可以使用分号来分隔命令。 3. 断开一个长命令行,可以使用反斜杠“”,以将一个较长的命令分成多行表达, 增 强 命令的可读性。执行后,Shell 自动显示提示符“>”,表示正在输入一个长命令。 4. 要使程序以后台方式执行,只需在要执行的命令后跟上一个“ &”符号。 三、简答题

Linux常见死机原因分析介绍

Linux常见死机原因分析介绍 宕机,指操作系统无法从一个严重系统错误中恢复过来,或系统硬件层面出问题,以致系统长时间无响应,而不得不重新启动计算机的现象。Linux系统也一样。不过原因有所不同。在排除了硬件故障/firmware版本/BIOS等等问题之外,Linux死机通常可能碰到如下几种情况 方法步骤 1,如果你的Linux死机的时候控制台上有乱七八糟的字符。恭喜你,这种情况叫做oops. 通常是Linux kernel认为自己发生了异常造成的。 可以通过oops消息查找出错的地方。 2,如果你的机器僵死,那么问题麻烦了。这种时候,通常是Kernel出现了死锁。Kernel不会知道自己死锁了,所以不会在屏幕上显示任何咚咚。

如果运气好,此时Kernel也许可能能够响应中断。不管怎样,你都无法进一步操作了。 3,严格来说,这第三种情况不算死机,现象如下:输入命令回车之后命令不能返回任何结果,但是控制台对回车可能有相应。但是命令无法结束,也不会有输出结果。 但是可以换到下一个控制台,而且还可以继续输入命令,但是输入命令之后还是没有输出。这种情况会有很多可能。通常是应用程序的系统调用长时间没有返回或者是满足不了应用的要求。 以上三种情况,system log的作用都不大。通常日志都不会纪录。所以只能使用Kernel的Debug工具。 补充:预防死机的小技巧 1、不要在同个硬盘安装多个操作系统 2、不要一次性打开多个网页浏览 3、在关闭电脑时,不要直接按电源按钮,否则会导致文件

丢失,下次使用不能正常开机导致死机 4、别让CPU、显卡超频,电脑温度过高 5、及时清理机箱内的灰尘 6、更换电脑硬件配置时,一定要插牢固 7、不要使用来历不明的软件和光盘,以免传染病毒 8、对系统文件或重要文件,最好使用隐含属性,以免因误操作而删除或覆盖这些文件 9、在安装应用软件时,若提示是否覆盖当前文件,一定不要覆盖,通常当前的系统文件是最好的 10、在运行大型应用软件时,不要在运行状态下退出以前运行的程序,否则会引起整个Windows崩溃 相关阅读:死机宕机实例介绍 自2015年3月11日下午5点起,据苹果用户反应

fork函数实验总结

针对fork函数难以理解,根据网上的解释,参考他人代码,做了如下实验,并附以实验分析 2 #include 3 #include 4 #include 5 #include 6 #include 7 8 int main () 9 { 10 pid_t pc,pr; 11 pc=fork(); (gdb) 12 13 if (pc<0) 14 { 15 printf("error fork.\n"); 16 17 } 18 else if (pc==0) 19 { 20 printf("this is pc=%d\n",getpid()); 21 sleep(5); (gdb) 22 printf("5 s over\n"); 23 //exit(0); 24 } 25 pr=fork(); 26 if (pr==0) 27 { 28 printf("this is pr =%d\n",getpid()); 29 } 30 31 else if (pr>0&&pc>0) (gdb) 32 printf("this is main =%d",getpid()); 33 34 35 36 37 38 } (gdb) b 12

Breakpoint 1 at 0x804849d: file /home/lsp/fork3.c, line 12. (gdb) b 19 Breakpoint 2 at 0x80484b7: file /home/lsp/fork3.c, line 19. (gdb) b 24 Breakpoint 3 at 0x80484e4: file /home/lsp/fork3.c, line 24. (gdb) b 26 Breakpoint 4 at 0x80484ec: file /home/lsp/fork3.c, line 26. (gdb) run Starting program: /home/lsp/fork3 Detaching after fork from child process 13200. ---说明pc=fork()函数已经建立子进程 this is pc=13200 Breakpoint 1, main () at /home/lsp/fork3.c:13 13 if (pc<0) (gdb) 5 s over this is pr =13201 --说明pc=fork()进程13200启动了新的子进程pr 其pid=13201 next Breakpoint 3, main () at /home/lsp/fork3.c:25 25 pr=fork(); --父进程停在pr=fork()处, (gdb) next Detaching after fork from child process 13254. this is pr =13254 --此处pr的pid=13254 与上一个pr=13201不同,这说明此处的pr是由main创建的 Breakpoint 4, main () at /home/lsp/fork3.c:26 26 if (pr==0) (gdb) next 31 else if (pr>0&&pc>0) (gdb) next 32 printf("this is main =%d",getpid()); (gdb) next 38 } (gdb) next 0x00a6d5d6 in __libc_start_main () from /lib/libc.so.6 (gdb) next Single stepping until exit from function __libc_start_main, which has no line number information. this is main =13199 ---main函数退出,器pid=13199 Program exited with code 023. (gdb)

linux内核IMQ源码实现分析

本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。 E-mail: wwwlkk@https://www.doczj.com/doc/2a2116928.html, 来源: https://www.doczj.com/doc/2a2116928.html,/?business&aid=6&un=wwwlkk#7 linux2.6.35内核IMQ源码实现分析 (1)数据包截留并重新注入协议栈技术 (1) (2)及时处理数据包技术 (2) (3)IMQ设备数据包重新注入协议栈流程 (4) (4)IMQ截留数据包流程 (4) (5)IMQ在软中断中及时将数据包重新注入协议栈 (7) (6)结束语 (9) 前言:IMQ用于入口流量整形和全局的流量控制,IMQ的配置是很简单的,但很少人分析过IMQ的内核实现,网络上也没有IMQ的源码分析文档,为了搞清楚IMQ的性能,稳定性,以及借鉴IMQ的技术,本文分析了IMQ的内核实现机制。 首先揭示IMQ的核心技术: 1.如何从协议栈中截留数据包,并能把数据包重新注入协议栈。 2.如何做到及时的将数据包重新注入协议栈。 实际上linux的标准内核已经解决了以上2个技术难点,第1个技术可以在NF_QUEUE机制中看到,第二个技术可以在发包软中断中看到。下面先介绍这2个技术。 (1)数据包截留并重新注入协议栈技术

(2)及时处理数据包技术 QoS有个技术难点:将数据包入队,然后发送队列中合适的数据包,那么如何做到队列中的数

激活状态的队列是否能保证队列中的数据包被及时的发送吗?接下来看一下,激活状态的队列的 证了数据包会被及时的发送。 这是linux内核发送软中断的机制,IMQ就是利用了这个机制,不同点在于:正常的发送队列是将数据包发送给网卡驱动,而IMQ队列是将数据包发送给okfn函数。

结构动力特性测试方法及原理

结构动力特性的测试方法及应用(讲稿) 一. 概述 每个结构都有自己的动力特性,惯称自振特性。了解结构的动力特性就是进行结构抗震设 计与结构损伤检测的重要步骤。目前,在结构地震反应分析中,广泛采用振型叠加原理的反应谱分析方法,但需要以确定结构的动力特性为前提。n 个自由度的结构体系的振动方程如下: [][][]{}{})()()()(...t p t y K t y C t y M =+??????+?????? 式中[]M 、[]C 、[]K 分别为结构的总体质量矩阵、阻尼矩阵、刚度矩阵,均为n 维矩阵;{} )(t p 为外部作用力的n 维随机过程列阵;{})(t y 为位移响应的n 维随机过程列阵;{})(t y &为速度响应的n 维随机过程列阵;{})(t y && 为加速度响应的n 维随机过程列阵。 表征结构动力特性的主要参数就是结构的自振频率f (其倒数即自振周期T )、振型Y(i)与阻尼比ξ,这些数值在结构动力计算中经常用到。 任何结构都可瞧作就是由刚度、质量、阻尼矩阵(统称结构参数)构成的动力学系统,结构一旦出现破损,结构参数也随之变化,从而导致系统频响函数与模态参数的改变,这种改变可视为结构破损发生的标志。这样,可利用结构破损前后的测试动态数据来诊断结构的破损,进而提出修复方案,现代发展起来的“结构破损诊断”技术就就是这样一种方法。其最大优点就是将导致结构振动的外界因素作为激励源,诊断过程不影响结构的正常使用,能方便地完成结构破损的在线监测与诊断。从传感器测试设备到相应的信号处理软件,振动模态测量方法已有几十年发展历史,积累了丰富的经验,振动模态测量在桥梁损伤检测领域的发展也很快。随着动态测试、信号处理、计算机辅助试验技术的提高,结构的振动信息可以在桥梁运营过程中利用环境激振来监测,并可得到比较精确的结构动态特性(如频响函数、模态参数等)。目前,许多国家在一些已建与在建桥梁上进行该方面有益的尝试。 测量结构物自振特性的方法很多,目前主要有稳态正弦激振法、传递函数法、脉动测试法与自由振动法。稳态正弦激振法就是给结构以一定的稳态正弦激励力,通过频率扫描的办法确定各共振频率下结构的振型与对应的阻尼比。 传递函数法就是用各种不同的方法对结构进行激励(如正弦激励、脉冲激励或随机激励等),测出激励力与各点的响应,利用专用的分析设备求出各响应点与激励点之间的传递函数,进而可以得出结构的各阶模态参数(包括振型、频率、阻尼比)。脉动测试法就是利用结构物(尤其就是高柔性结构)在自然环境振源(如风、行车、水流、地脉动等)的影响下,所产生的随机振动,通过传感器记录、经谱分析,求得结构物的动力特性参数。自由振动法就是:通过外力使被测结构沿某个主轴方向产生一定的初位移后突然释放,使之产生一个初速度,以激发起被测结构的自由振动。 以上几种方法各有其优点与局限性。利用共振法可以获得结构比较精确的自振频率与阻尼比,但其缺点就是,采用单点激振时只能求得低阶振型时的自振特性,而采用多点激振需较多的设备与较高的试验技术;传递函数法应用于模型试验,常常可以得到满意的结果,但对于尺度很大的实际结构要用较大的激励力才能使结构振动起来,从而获得比较满意的传递函数,这在实际测试工作中往往有一定的困难。 利用环境随机振动作为结构物激振的振源,来测定并分析结构物固有特性的方法,就是近年来随着计算机技术及FFT 理论的普及而发展起来的,现已被广泛应用于建筑物的动力分析研究中,对于斜拉桥及悬索桥等大型柔性结构的动力分析也得到了广泛的运用。斜拉桥或悬索桥的环境随机振源来自两方面:一方面指从基础部分传到结构的地面振动及由于大气变化而影响到上部结构的振动(根据动力量测结果,可发现其频谱就是相当丰富的,具有不同的脉动卓越周期,反应了不同地区地质土壤的动力特性);另一方面主要来自过桥车辆的随机振动。

《Linux操作系统》部分习题答案

第一章Linux系统简介 一、思考题 1.UNIX的大部分代码是用一种流行的程序设计语言编写的,该语言是什么? C语言 2.UNIX系统的特点有哪些? ·多任务 ·多用户 ·并行处理能力 ·设备无关性 ·工具 ·错误处理 ·强大的网络功能 ·开放性 3.什么是Linux?其创始人是谁? Linux是一个功能强大的操作系统,同时它也是一个自由软件,是免费的、源代码开放的、可以自由使用的UNIX兼容产品。其创始人是Linus 4.Linux操作系统的诞生、发展和成长过程始终依赖者的重要支柱都有哪些? ·UNIX操作系统 ·MINIX操作系统 ·GNU计划 ·POSIX标准 ·Internet 5.简述Linux系统的特点。 ·自由软件 ·良好的兼容性 ·多用户、多任务 ·良好的界面 ·丰富的网络功能 ·可靠地安全性、稳定性 ·支持多种平台 6.常见的Linux的发行版本有哪些? ·Red Hat Linux ·Caldera OpenLinux ·SuSE Linux ·TurboLinux ·红旗Linux ·中软Linux 二、选择题 1.Linux最初是以MINIX 操作系统为模板而开发出来的。 2.关于Linux内核版本的说法,下列选项中错误的是(C)。 A.表示为主版本号.次版本号.修正号B.1.2.3表示稳定的发行版 C.1.3.3表示稳定的发行版D.2.2.5表示对内核2.2的第5次修正 (补充:次版本号为偶数的是稳定版本;为奇数的则是测试版本。)

3.Linux属于自由软件。 4.自由软件的含义是软件可以自由修改和发布。 5.一下不具有多任务性的操作系统是DOS 第二章Linux系统入门 一、思考题 1.Linux系统有哪些运行级别?其含义是什么? 可用级别为0~6,其中0:关闭系统;6:重新启动,其他略。 2.Linux系统下经常使用的两种桌面环境是什么? GNOME和KDE 3.什么是X-Window系统?它有什么特点? 图形界面(X-Window)是在Linux操作系统中提供的图形化用户界面(GUI),其支持的视窗系统也称为X,它的特点有:它采用了“客户端-服务器”模式;它是一个跨平台的操作环境。 7.默认情况下,超级用户和普通用户的登录提示符分别是什么? # 和$ 二、选择题 1.系统引导的过程一般包括如下的几步:①MBR中的引导装载程序启动。②用户登录。③Linux内核运行。④BIOS自检。正确的顺序是④①③②。 2.Linux中使用Ctrl+Alt+BackSpace 组合键可以关闭X-Window图形用户界面。 3.字符界面下使用init命令关机所用的参数是0 。(参数6是重新启动) 4.字符界面下使用shutdown命令重启计算机时所用的参数是–r 。 5.使用man命令调阅相关的帮助信息时,用于逐页地下翻的功能键是Space 。 第三章shell与shell命令 一、思考题 1.shell的基本功能有哪些? 命令解释执行、文件名替换、输入/输出重定向、连同管道建立、系统环境设置和shell编程。 2.Linux系统中的主要目录有哪些? /:系统的根目录 /dev:系统的设备目录 /home:用户主目录 /root:root用户主目录 /boot:Linux的启动目录 /usr:用户级目录 3.工作目录及其父目录课分别用什么表示? . 和.. 5.常用的shell环境变量有哪些? ·HOME:用户家目录的完全路径名 ·LOGNAME:登录用户名 ·IFS:命令行内部域分割符 ·PATH:由冒号分隔的目录路径名

Linux 常见故障诊断说明

Q:Linux 常见故障诊断说明 A:对大多数用户来说,由于 Linux 操作系统的易用性和 Windows 操作系统相比较起来还是存在一定差别的。在 Windows 操作系统下,如果系统发生一些故障或问题的时候,我们经常会用一些补救的措施来进行故障的排除,这些方法很多,而且简单易用。但是,这种在Linux操作系统进行故障排除的工作却要复杂一些,而且故障的发现不像在Windows操作系统那样可以及时预见,需要手工运行一些系统和网络的管理命令。下面,就对系统和网络两种类型在Linux操作系统中的常见故障诊断进行说明。 系统故障诊断 1.启动故障 这是在Linux操作系统中经常会遇到的问题。系统不能启动的原因主要有:在安装Linux 操作系统的过程中,LILO配置信息错误,导致安装完毕后,系统不能正常启动;重新安装其他的操作系统,也经常会导致原有的Linux不能启动。因为,这些新安装的操作系统默认为计算机中没有其他的操作系统,因而改写了硬盘的主引导记录(MBR),覆盖了Linux操作系统中的LILO系统引导程序,致使最后无法启动LILO;在操作linux操作系统过程中,由于运行错误的Linux命令,使系统重新启动时,出现异常。 解决方法: 如果在Linux操作系统安装过程中或安装过程后,制作了Linux系统的急救启动盘组,使用这些急救盘启动系统即可进入系统,然后对相应错误,进行配置即可解决问题;如果没有制作急救启动盘组,Linux系统不能启动,该怎么办呢?下面介绍三种解决方法: (1)进入Linux操作系统单用户模式,在boot提示符后,敲入:linux single,此模式下启动Linux,LILO配置和网络配置信息不加载在启动过程中。 (2)光盘启动,用第一张安装Linux操作系统的光盘(启动光盘)启动硬盘的Linux系统,主板BIOS里要设置光盘启动,重启机器后,出现 boot: 提示符后键入: vmlinuz root=/dev/linuxrootpartition noinitrd 其中,root=后面填入您的Linux root分区的分区号,也就是Linux系统的root文件系统所在的硬盘分区位置,例如:vmlinuz root=/dev/hda3 noinitrd。回车之后,即可进Linux系统。如果想恢复被破坏的LILO系统引导程序,可以编辑/etc/lilo.conf 之后, 运行/sbin/lilo即可。这种方法也适合其它原因对Linux操作系统造成的破坏。(注:软盘启动操作系统的过程也同上)。 (3)在DOS下运行loadlin程序启动系统。在个人计算机使用 Linux系统时,通常都是Linux和Windows 9x或Windows 2000并存的。如果知道Linux系统在硬盘上的确切安装分区,并且有loadlin程序(在 Red Hat Linux 光盘的 dosutil目录下就有这个程序),也可以启动Linux系统。loadlin是DOS系统下的程序,运行它可以从DOS系统下直接启动Linux系统,快速进入Linux环境。除loadlin程序之外,还需要一个 Linux启动内核的映像文件vmlinuz,在 Red Hat linux光盘的 images目录下有这个文件。例如,如果在Windows 2000系统下面,进入DOS的命令模式,然后运行下述的loadlin命令,即可重新进入Linux系统: loadlin vmlinuz root=/dev/linuxrootpartition 命令执行后,就开始引导Linux系统。用root身份登录后,编辑/etc/lilo.conf 之后,运行/sbin/lilo即可,这样操作后则重新将LILO系统引导程序装入MBR。 2.文件系统故障

Linux的fork、exec、wait函数的分析

Linux 的fork 、exec 、wait 函数分析 I 数学与计算机学院 课程设计说明书 课 程 名 称: 操作系统原理-课程设计 课 程 代 码: 8404061 题 目: Linux 的fork 、exec 、wait 函数的分析 年级/专业/班: 学 生 姓 名: 学 号: 3 开 始 时 间: 2010 年 12 月 12 日 完 成 时 间: 2011 年 01 月 09 日 课程设计成绩: 指导教师签名: 年 月 日

Linux 的fork 、exec 、wait 函数分析 II 目 录 1 引 言 ................................................................. 1 1.1 问题的提出 ...................................................................................................................... 1 1.2国内外研究的现状 ........................................................................................................... 1 1.3任务与分析 ....................................................................................................................... 1 2代码分析结果 ............................................................ 2 2.1 数据结构 ......................................................................................................................... 2 2.1.1 struct task_struct ............................................................................................. 2 2.1.2 task ......................................................................................................................... 3 2.1.3 tarray_freelist ................................................................................................... 3 2.1.4 struct--linux_binprm ......................................................................................... 3 2.1.5进程状态 .................................................................................................................. 4 2.2常量和出错信息的意义 .................................................................................................. 4 2.3调用关系图 ...................................................................................................................... 4 2.4各模块/函数的功能及详细框图 .................................................................................... 5 2.4.1 do_fork 模块 .......................................................................................................... 5 2.4.2 get_pid 模块 .......................................................................................................... 8 2.4.3 do_execve 模块 ........................................................................................................ 10 3.4.4 do_exit 模块 ........................................................................................................ 14 3.4.5 sys_wait4模块 .................................................................................................. 18 3 总结与体会 ............................................................ 20 4 参考文献 .. (20)

相关主题
相关文档 最新文档