本帖旨在从另一个角度探讨一下其中所提到的“长延时”的三个实现方式,这些延时方式都试图让出处理器,换句话说都是基于非忙等待的实现(因为长延时若是忙等待,将极大浪费处理器的资源)与书中的原理分析不同之处在于,我希望在本帖中以实际的代码验证的方式来使那种理论上的分析更加直观化:我们在一个内核模块,比如demodev.ko的初始化函数中调用这些延时函数,然后通过ps命令来查看延时期间insmod进程的状态。
首先我们看第一种实现(为了有足够的时间让我们去查看进程的状态,我将这种“长延时”延时间隔设定30S,这样足够我们在此延时的窗口期间观察insmod进程的状态):
- void delay_30s(void){
-
unsigned long j = jiffies + 30 * HZ;
-
while(time_before(jiffies, j))
-
schedule();
- }
root@build-server:/home/dennis/book_2nd_edition/book_sourcecode# ps aux | grep insmod
root 6491 100 0.0 4420 576 pts/2 R+ 21:55 0:28 insmod demodev.ko
root 6514 0.0 0.0 13448 872 pts/3 S+ 21:56 0:00 grep --color=auto insmod
insmod进程的R+状态意味着当前进程处在后台一个运行的状态下。
再来看一个改进版:
- void delay_30s(void){
-
set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(30 * HZ);
- }
root@build-server:/home/dennis/book_2nd_edition/book_sourcecode# ps aux | grep insmod
root 6300 0.0 0.0 4420 580 pts/2 S+ 21:53 0:00 insmod demodev.ko
root 6306 0.0 0.0 13448 868 pts/3 S+ 21:53 0:00 grep --color=auto insmod
insmod进程的S+状态意味着当前进程处在后台一个睡眠的状态下。如果你的调用上下文允许当前进程进入睡眠,那么这是一个非常完美的实现:睡眠的进程不占用处理器资源,同时延时的目的也达到了。
最后,我们来试图改进第一种的那个方案,代码如下:
- void delay_30s(void){
-
unsigned long j = jiffies + 30 * HZ;
- while(time_before(jiffies, j)){
- set_current_state(TASK_UNINTERRUPTIBLE);
-
schedule();
- }
最后我想提一下那个schedule_timeout函数,如果调用该函数前没有用改变进程的状态,那么这个函数基本上是瞬间返回(其实取决于当前处理器运行队列及调度器行为),虽然它初始化并提交了一个定时器结点,但是它调用schedule时基本上是很快会被调度器再次调度,然后它有个del_singleshot_timer_sync(&timer)调用,后者将把之前提交的定时器结点从定时器管理队列中摘除,因为如果当前进程一旦结束,放到此前提交的定时器结点中的current将指向无意义的空间。