昨天做了一个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.重新计算时间片
4.Schedule()
5.计算优先级和时间片
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.
F.
G. 当条件满足后,进程将自己设置为TASK_RUNNING并调用remove_wait_queue把自己移出等待队列。
6.上下文切换和抢占
上下文切换,也就是从一个可执行进程切换到另一个可执行进程,有context_switch函数负责处理。每当一个新的进程被选出来准备投入运行的时候,schedule就会调用该函数。它完成了两项基本工作:
调用switch_mm(),该函数负责把虚拟内存从上一个进程映射切换到新进程中。
调用switch_to(),该函数负责从上一个进程的处理器状态切换到新进程的处理器状态,这包括保存、恢复栈信息和寄存器信息。
内 核必须知道在什么时候调用schedule,如果仅靠用户程序代码调用schedule,它们可能就会永远执行下去。相反,内核提供了 need_resched标志来表明是否需要重新执行一次调度。当某个进程耗尽它的时间片,scheduler_tick就会设置这个标志,当一个优先级 高的进程进入可执行状态时候,try_to_wake_up也会设置这个标志。