作者:Aningsk ,本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。
在这里简单写一下学习第七章的知识。主要是为了驱动中的一些延时操作之类的。
Jiffies与HZ
Linux中记录时间使用jiffies变量,这个应该是一个全局变量吧,包含<linux/sched.h>就可以读取这个变量来获取时间。这个变量记录了系统启动后的节拍数,所以我们,除了能够看出不同的数字的大小代表不同的时间先后外,并不能得到更为详尽的信息了:比如一秒钟的时间,jiffies是多少。但是Linux提供了HZ宏,虽然我不怎么关心这个宏的值,但这个的确好用:一个单位的HZ就是代表1秒。这样结合jiffies变量,就可以容易的表示我们的时间需求(比如,当前时间后的1秒、0.5秒等等)。书中184页有使用的例子。
延迟执行
书中列出了很多种延迟方法,但不是所有的方法都是非常适合使用的。所以在这里我只记录一下比较好用的方法。
-
使用超时的等待队列
等待队列可以让进程阻塞等待在wait_event处,我们可以使用它的超时版本,来达到让应用在此处阻塞特定的时长。例如wait_event_timeout(wait, 0, delay);其中wait是等待队列头;0是等待条件,因为我们希望在这里等待特定的时长,所以干脆把条件置为假,我们不需要它因为什么条件而唤醒;delay是超时的时长,它是一个jiffies值,这个函数阻塞等待达到jiffies时长后,将会退出阻塞,进程继续运行。
-
带有超时的进程调度
当我们在延迟执行的时候,我们把CPU让给其他的进程使用,也就是进程调度。内核提供了一个带有超时特点的调度函数版本schedule_timeout(delay)。使用这个调度函数,可以让进程调度出去,并且进程不占有CPU的时间是delay(同样是jiffies值)。从而达到让该进程延迟delay时间的功能。使用这个方法非常方便,仅仅需要两行代码:一行是设置进程状态为可中断休眠,另一行就是调度进程了。在书中194页有代码。
上面两种方法都是处理比较长的延时,时间的长度大多是以"秒"为单位来衡量的。当我们需要处理一些硬件上的延时,这些延时常常只有几十"毫秒",所以上面的方法并不适用。内核提供了ndelay, udelay, mdelay来分别实现纳秒、微秒、毫秒的延时。
内核定时器
所谓定时器就是在一个指定的时间,执行指定的动作。内核使用struct timer_list结构体来表示一个定时器,其中的成员expires, function, data是提供给驱动使用的,分别表示jiffies时间、要执行的函数、给函数的参数。参见书中198页。可以通过add_timer添加某个定时器到内核,内核就会在指定的时间执行定时器结构中指定的函数。定时器只会生效一次,再次使用必须修改时间,再次添加定时器。例子参见199~200页。
Tasklet
这是内核提供的另一种延迟操作的机制,tasklet常常用于中断,应该就是在中断处理函数进行了必须快速的操作后,调用使能tasklet的函数,然后中断返回。内核会在中断后的一段时间执行tasklet中任务。它的使用方法和timer_list类似。书中在此处没有更加详细的说明。
工作队列
这个功能看起来类似于tasklet,但tasklet适用于很快的操作,以原子模式执行,不允许休眠;而工作队列执行的操作里是可以休眠的。工作队列的使用流程:创建工作队列,初始化工作,将工作提交给工作队列。参见书中205页。队列的工作函数和定时器的工作函数中都可以将自己所属的工作或定时器再次添加。
至于共享队列,就是不用我们自己建工作队列,而是使用内核中的工作队列(keventd_wq),这个队列是共享的。使用schedule_work来将一个工作提交给共享队列,但这个函数的名字看起来有点迷惑人,似乎它发生的进程调度,可是里面只是使用了queue_work将工作添加到工作队列,我觉得这不会引起进程调度。
在书中207页,有一段使用共享队列的例子,其中使用了手工休眠,我理解使用等待队列就是为了测试现象(就是使用cat的那些);但是使用手工休眠,在那4行代码里看起来并非必要:我觉得schedule_work放在preoare_to_wait之前就可以,那就不用非要手工休眠了,可以直接wait_event_interruptible吧~?
Aningsk
2015-03-23