Linux 内核设计与实现(一)

本文深入探讨了Linux内核中的进程管理与调度机制,包括进程概念、优先级、调度策略、时间片、上下文切换及抢占机制。详细解释了内核如何通过任务队列、优先级数组和调度算法来管理进程生命周期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天做了一个linux最简单的驱动和应用程序实例,在板子上还是跑了起来,不过呢,至是熟悉了linux下驱动的框架,和最简单的一些操作。为了进一步学习linux驱动,还得再回到linux内核设计上来,再一次复习。当然了,这次复习的速度就要比前几次的快了。

Linux内核设计与实现

一、     进程管理

1.进程概念,进程、线程、任务。

2.Task list:内核吧进程存放在任务队列(task list)的双向循环链表中,链表中的每一项都是类型为task_struct、称为进程描述符的结构。

3.Pid

4.进程状态 :TASK_STOPPED。TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE、TASK_ZOMBLE。

 

二、    调度

A.调度策略

1、I/O消耗型和处理器消耗型的进程

2、进程优先级,nice的值(-20,19)

3、时间片:当一个进程的时间片耗尽时,就认为进程到期了。没有时间片的进程不会再投入运行,除非等到其它所有的进程都耗尽了它们的时间片。然后所有进程的时间片会被重新计算。

4、进程抢占(Linux系统是抢占式的)。

 

B.调度算法

1. 可执行队列(runqueue)是调度程序中最基本的数据结构。可执行队列是给定处理器上的可执行进程的链表,每个处理器一个。每个可投入运行的进程都唯 一的属于一个可执行队列。此外,可执行队列中还包含每个处理器的调度信息。因此,可执行队列也是每个处理器最重要的数据结构。 cpu_rq(processor)宏用于返回给顶处理器可执行队列的指针。This_rq()宏用来返回当前处理器的可执行队列。宏 task_rq(task)返回给定任务所在的队列指针。

在对可执行队列进行操作以前,应该先锁住它:task_rq_lock(),在适当的时候释放task_rq_unlock()。

 

2.优先级数组

 

3.重新计算时间片

       新的linux调度程序减少了对循环的依赖。取而代之的是它为每个处理器维护两个优先级数组。活动数组内的可执行队列上的进程都还有时间片剩余,而过期数 组内的可执行队列上的进程都耗尽了时间片。当一个进程的时间片耗尽时,它会被移至过期数组,但在此之前,时间片已经给它重新计算好了。重新计算时间片变得 非常简单,只要在活动和过期数组之间来回切换就行了。因为数组是通过指针进行访问的,所以交换它们用的时间就是交换指针需要的时间。

4.Schedule()

       选定下一个进程并切换到它去执行是通过schedule函数来实现的。当内核代码想要休眠,或者如果有哪个进程将被抢占,Schedule就会被唤起,该函数独立于每个处理器运行。因此,每个CPU都要对下一次运行哪个进程作出自己的判断。

 

5.计算优先级和时间片

       静态优先级nice

       动态优先级可以通过effective_prio()函数返回,这个函数以nice为基数,加上-5到+5之间的进程交互性的奖励或罚分。

       调度程序为了了解一个进程交互性到底强不强。通过其程序的休眠时间来判断其为I/O消耗型还是处理器消耗型。

       Linux记录了一个进程在用于记录休眠和执行的时间,该值存放在task_struct的sleep_avg中。

       如果一个进程的交互性非常强,那儿当它时间片用完后,它会被再放置到活动数组而不是过期数组中。

 

5.睡眠和唤醒

休 眠(被阻塞)的进程处于一个特殊的不可执行状态。如果没有这种状态的话,调度程序就可能选出一个本不愿意执行的进程。进程休眠有各种原因,但肯定都是为了 等待一些事件。无论那种情况,内核的操作都相同:进程把自己标记成休眠状态,把自己从可执行队列移出,放入等待队列,然后调用schedule函数选择和 执行一个其它进程。

休眠通过等待队列进行处理。等待队列是由等待某些事件发生的 进程组成的简单链表。内核用wake_queue_head_t来代表等待队列。等待队列可以通过DECLARE_WAITQUEUE()静态创建,也可 以由init_waitqueue_head()动态创建。进程把自己放入等待队列中并设置成不可执行状态。当与等待队列相关的事件发生的时候,队列上的 进程就会被唤醒。

进程通过执行下面几个步骤将自己加入到一个等待队列中:

A. 调用DECLARE_WAITQUEUE创建一个等待队列的项。

B. 调用add_wait_queue()把自己加入到队列中。

C. 将进程状态设置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE

D. 如果状态被设置为 TASK_INTERRUPTIBLE,则信号唤醒进程,这就是所谓的伪唤醒(唤醒不是因为事件的发生),因此检查并处理信号。

E.  检查条件是否为真,如果是的话,就没必要休眠了。如果条件不为真,调用schedule。

F.  当进程被唤醒的时候,它会再次检查条件是否为真。如果是,它就退出循环,如果不是,它再次调用schedule并一直重复这步操作。

G. 当条件满足后,进程将自己设置为TASK_RUNNING并调用remove_wait_queue把自己移出等待队列。

 

 

 

6.上下文切换和抢占

上下文切换,也就是从一个可执行进程切换到另一个可执行进程,有context_switch函数负责处理。每当一个新的进程被选出来准备投入运行的时候,schedule就会调用该函数。它完成了两项基本工作:

调用switch_mm(),该函数负责把虚拟内存从上一个进程映射切换到新进程中。

调用switch_to(),该函数负责从上一个进程的处理器状态切换到新进程的处理器状态,这包括保存、恢复栈信息和寄存器信息。

内 核必须知道在什么时候调用schedule,如果仅靠用户程序代码调用schedule,它们可能就会永远执行下去。相反,内核提供了 need_resched标志来表明是否需要重新执行一次调度。当某个进程耗尽它的时间片,scheduler_tick就会设置这个标志,当一个优先级 高的进程进入可执行状态时候,try_to_wake_up也会设置这个标志。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值