实际系统中的调度
我们已经谈过调度算法以及线程状态,但是我们还没有说过线程等重新调度的原因与时间。有一个常见的误解就是:重新调度的发生是没有什么原因的。这在设计阶段是一个有用的概念。但是更重要的是你要知道产生重新调度的条件。
重新调度只会由于以下几个原因才会发生:
- 硬件中断
- 内核调用
- 出错
硬件中断引起的重新调度
硬件中断引起的重新调度有两种情况:定时计数器产生的和其他硬件产生的。
实时时钟为内核生成周期性的中断,并引发基于时间的重新调度。
例如,如果你调用了函数sleep(10);,就会产生数个实时时钟中断;内核在每个中断为时间时钟做递增操作。当时间时钟表示已经过了10秒了,内核就会将你的线程重新调度为就绪状态。
其他线程可能等待从外设传来的硬件中断,比如串口、硬盘或声卡。在这种情况下,这些线程在内核中阻塞并等待硬件中断,只有“事件”发生后,这些线程才会被重新调度。
内核调用引起的重新调度
假设重新调度是由于一个线程做了内核调用所引起的,那么重新调度就会立即执行并可以看作与定时计数器和其他中断是异步的。
例如,上面我们调用了sleep函数,这个C库函数最终被转换为一个内核调用。在那时,内核就做出重新调度的决策并将你的线程从对应优先级的就绪队列取出,之后调度另外一个已经是就绪状态的线程。
有很多内核调用可以让一个进程重新调度。很多调用是非常明显的,如下面所示:
- 定时计数器函数(例如sleep())
- 消息函数(例如MsgSendv())
- 线程操作函数(例如pthread_cancel()、pthreaf_join())
意外引起的重新调度
最后一个产生重新调度的原因,CPU错误,就是一个意外,它介于硬件中断与内核调用之间。它与内核(像中断)是异步执行的,可是与产生它的用户代码(像内核调用,比如除以0意外)是同步执行的。上面的两类重新调度也适用于由错误产生的重新调度。
总结
Neutrino提供了对于线程这个主要的调度元素的丰富的调度选项。进程则被定义为资源拥有权的一个单元并包含一个或多个线程。
线程可能会使用下面的任意一种同步方式:
- 互斥体(mutexes):在一个时刻只允许一个线程拥有互斥体
- 信号量(semaphores):允许规定数量的线程拥有信号量
- 睡眠锁(sleepons):允许多个线程对一定数量的对象阻塞,在底层则是动态的为阻塞线程分配条件变量
- 条件变量(condvars):与睡眠锁类似,不过条件变量的分配由编程者控制
- 连接(joining):允许一个线程与其他线程同步结束
- 壁垒(barriers):运行线程等待,直到一定数目的线程到达同步点
注意的是互斥体、信号量以及条件变量可以在同进程或不同进程的线程间使用,睡眠锁只能在同一进程中的线程之间使用。
除了同步之外,线程可以被重新调度(使用优先级以及调度算法),并且它们可以在单处理器系统或SMP系统上自动运行。
每次我们创建进程,实际上就是创建有一个进程在其中运行的地址空间,这个线程可能以来调用的函数由main()、fork()或vfork()启动。
Related posts:
近期评论