说明:
/******************
* linux内核的时间管理
******************/
(1)内核中的时间概念
时间管理在linux内核中占有非常重要的作用。相对于事件驱动而言,内核中有大量函数是基于时间驱动的。
有些函数是周期执行的,比如每10毫秒刷新一次屏幕;有些函数是推后一定时间执行的,比如内核在500毫秒后执行某项任务。
要区分:
*绝对时间和相对时间
*周期性产生的事件和推迟执行的事件
周期性事件是由系统系统定时器驱动的
(2)HZ值
内核必须在硬件定时器的帮助下才能计算和管理时间。定时器产生中断的频率称为节拍率(tick rate)。
在内核中指定了一个变量HZ,内核初始化的时候会根据这个值确定定时器的节拍率。
HZ定义在<asm/param.h>,在i386平台上,目前采用的HZ值是1000。
也就是时钟中断每秒发生1000次,周期为1毫秒。即:#define HZ 1000
注意!HZ不是个固定不变的值,它是可以更改的,可以在内核源代码配置的时候输入。
不同的体系结构其HZ值是不一样的,比如arm就采用100。
如果在驱动中要使用系统的中断频率,直接使用HZ,而不要用100或1000。
a.理想的HZ值
i386的HZ值一直采用100,直到2.5版后才改为1000。提高节拍率意味着时钟中断产生的更加频繁,中断处理程序也会更频繁地执行。
带来的好处有:
*内核定时器能够以更高的频率和更高的准确度运行
*依赖定时器执行的系统调用,比如poll()和select(),运行的精度更高
*提高进程抢占的准确度(缩短了调度延时,如果进程还剩2ms时间片,在10ms的调度周期下,进程会多运行8ms。由于耽误了抢占,对于一些对时间要求严格的任务会产生影响)
坏处有:
*节拍率越高,系统负担越重。中断处理程序将占用更多的处理器时间。
(3)jiffies
全局变量jiffies用于记录系统启动以来产生的节拍的总数。
启动时,jiffies初始化为0,此后每次时钟中断处理程序都会增加该变量的值。这样,系统启动后的运行时间就是jiffies/HZ秒。
jiffies定义于<linux/jiffies.h>中:extern unsigned long volatile jiffies;
jiffies变量总是为unsigned long型。因此在32位体系结构上是32位,而在64位体系上是64位。
对于32位的jiffies,如果HZ为1000,49.7天后会溢出。虽然溢出的情况不常见,但程序在检测超时时仍然可能因为回绕而导致错误。
linux提供了4个宏来比较节拍计数,它们能正确地处理节拍计数回绕。
#include <linux/jiffies.h>
#define time_after(unknown, known) // unknow > known
#define time_before(unknown, known) // unknow < known
#define time_after_eq(unknown, known) // unknow >= known
#define time_before_eq(unknown, known) // unknow <= known
unknown通常是指jiffies,known是需要对比的值(常常是一个jiffies加减后计算出的相对>值)。例如:
unsigned long timeout = jiffies + HZ/2; /* 0.5秒后超时 */
...
if(time_before(jiffies, timeout)){
/* 没有超时,很好 */
}else{
/* 超时了,发生错误 */
}
time_before可以理解为如果在超时(timeout)之前(before)完成。
*系统中还声明了一个64位的值jiffies_64,在64位系统中jiffies_64和jiffies是一个值。可以通过get_jiffies_64()获得这个值。
*使用 u64 j2; j2 = get_jiffies_64();
(4)获得当前时间
驱动程序中一般不需要知道墙钟时间(也就是年月日的时间)。但驱动可能需要处理绝对时间。
为此,内核提供了两个结构体,都定义在<linux/time.h>:
a.较老,但很流行。采用秒和毫秒值,保存了1970年1月1日0点以来的秒数。
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
b.较新,采用秒和纳秒值保存时间。
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
c.函数do_gettimeofday():该函数用通常的秒或微秒来填充一个指向struct timeval的指针变量,原型如下:
#include <linux/time.h>
void do_gettimeofday(struct timeval *tv);
d.函数current_kernel_time():该函数可用于获得timespec,原型如下:
#include <linux/time.h>
struct timespec current_kernel_time(void);
/********************
*确定时间的延迟执行
*******************/
设备驱动程序经常需要将某些特定代码延迟一段时间后执行,通常是为了让硬件能完成某些任务。
长于定时器周期(也称为时钟嘀嗒)的延迟可以通过使用系统时钟完成,而非常短的延时则通过软件循环的方式完成。
(1)短延时
对于那些最多几十个毫秒的延迟,无法借助系统定时器。系统通过软件循环提供了下面的延迟函数:
#include <linux/delay.h> /* 实际在<asm/delay.h> */
void ndelay(unsigned long nsecs); /*延迟纳秒 */
void udelay(unsigned long usecs); /*延迟微秒 */
void mdelay(unsigned long msecs); /*延迟毫秒 */
这三个延迟函数均是忙等待函数,在延迟过程中无法运行其他任务。实际上,当前所有平台都无法达到纳秒精度。
(2)长延时
a.在延迟到期前让出处理器
while(time_before(jiffies, j1))
schedule();
在等待期间可以让出处理器,但系统无法进入空闲模式(因为这个进程始终在进行调度),不利于省电。
b.超时函数
#include <linux/sched.h>
signed long schedule_timeout(signed long timeout);
使用方式:
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ); /* 睡2秒 */
进程经过2秒后会被唤醒。如果不希望被用户空间打断,可以将进程状态设置为TASK_UNINTERRUPTIBLE。
(3)等待队列
使用等待队列也可以实现长延迟。在延迟期间,当前进程在等待队列中睡眠。进程在睡眠时,需要根据所等待的事件链接到某一个等待队列。
a.声明等待队列
等待队列实际上就是一个进程链表,链表中包含了等待某个特定事件的所有进程。
#include <linux/wait.h>
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
想把进程加入等待队列,驱动首先要在模块中声明一个等待队列头,并将它初始化。
b.静态初始化
DECLARE_WAIT_QUEUE_HEAD(name);
c.动态初始化
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
d.等待函数
进程通过调用下面函数可以在某个等待队列中休眠固定的时间:
#include <linux/wait.h>
long wait_event_timeout(wait_queue_head_t q,condition, long timeout);
long wait_event_interruptible_timeout(wait_queue_head_t q, condition, long timeout);
调用这两个函数后,进程会在给定的等待队列q上休眠,但会在超时(timeout)到期时返回。
如果超时到期,则返回0,如果进程被其他事件唤醒,则返回剩余的时间数。
如果没有等待条件,则将condition设为0
e.使用方式:
wait_queue_head_t wait;
init_waitqueue_head(&wait);
wait_event_interruptible_timeout(wait, 0, 2*HZ); /*当前进程在等待队列wait中睡2秒 */
(4)内核定时器
还有一种将任务延迟执行的方法是采用内核定时器。
与前面几种延迟方法不同,内核定时器并不会阻塞当前进程,启动一个内核定时器只是声明了要在未来的某个时刻执行一项任务,当前进程仍然继续执行。
不要用定时器完成硬实时任务!
定时器由结构timer_list表示,定义在<linux/timer.h>
struct timer_list{
struct list_head entry; /* 定时器链表 */
unsigned long expires; /* 以jiffies为单位的定时值 */
spinlock_t lock;
void(*function)(unsigned long); /* 定时器处理函数 */
unsigned long data; /* 传给定时器处理函数的参数 */
};
内核在<linux/timer.h>中提供了一系列管理定时器的接口。
a.创建定时器
struct timer_list my_timer;
b.初始化定时器
init_timer(&my_timer); /* 填充数据结构 */
my_timer.expires = jiffies + delay;
my_timer.data = 0;
my_timer.function = my_function; /*定时器到期时调用的函数*/
c.定时器的执行函数
超时处理函数的原型如下:
void my_timer_function(unsigned long data);
可以利用data参数用一个处理函数处理多个定时器。可以将data设为0。
d.激活定时器
add_timer(&my_timer);
定时器一旦激活就开始运行。
e.更改已激活的定时器的超时时间
mod_timer(&my_timer, jiffies+ney_delay);
可以用于那些已经初始化但还没激活的定时器,如果调用时定时器未被激活则返回0,否则返回1。
一旦mod_timer返回,定时器将被激活。
f.删除定时器
del_timer(&my_timer);
被激活或未被激活的定时器都可以使用,如果调用时定时器未被激活则返回0,否则返回1。
不需要为已经超时的定时器调用,它们被自动删除
g.同步删除
del_time_sync(&my_timer);
在smp系统中,确保返回时,所有的定时器处理函数都退出。不能在中断上下文使用!
/********************
*不确定时间的延迟执行
*******************/
(1)什么是不确定时间的延迟
前面介绍的是确定时间的延迟执行,但在写驱动的过程中经常遇到这种情况:
用户空间程序调用read函数从设备读数据,但设备中当前没有产生数据。
此时,驱动的read函数默认的操作是进入休眠,一直等待到设备中有了数据为止。
这种等待就是不定时的延迟,通常采用休眠机制来实现。
(2)休眠
休眠是基于等待队列实现的,前面我们已经介绍过wait_event系列函数,但现在我们将不会有确定的休眠时间。
当进程被置入休眠时,会被标记为特殊状态并从调度器的运行队列中移走。
直到某些事件发生后,如设备接收到数据,则将进程重新设为运行态并进入运行队列进行调度。
休眠函数的头文件是<linux/wait.h>,具体的实现函数在kernel/wait.c中。
a.休眠的规则
*永远不要在原子上下文中休眠
*当被唤醒时,我们无法知道睡眠了多少时间,也不知道醒来后是否获得了我们需要的资源
*除非知道有其他进程会在其他地方唤醒我们,否则进程不能休眠
b.等待队列的初始化
见前文
c.休眠函数
linux最简单的睡眠方式为wait_event宏。该宏在实现休眠的同时,检查进程等待的条件。
1. void wait_event(wait_queue_head_t q, int condition);
2. int wait_event_interruptible(wait_queue_head_t q, int condition);
q: 是等待队列头,注意是采用值传递。
condition: 任意一个布尔表达式,在条件为真之前,进程会保持休眠。
注意!进程需要通过唤醒函数才可能被唤醒,此时需要检测条件。
如果条件满足,则被唤醒的进程真正醒来;如果条件不满足,则进程继续睡眠。
d.唤醒函数
当我们的进程睡眠后,需要由其他的某个执行线程(可能是另一个进程或中断处理例程)唤醒。
唤醒函数:
#include <linux/wait.h>
1. void wake_up(wait_queue_head_t *queue);
2. void wake_up_interruptible(wait_queue_head_t *queue);
wake_up会唤醒等待在给定queue上的所有进程。而wake_up_interruptible唤醒那些执行可中断休眠的进程。
实践中,约定做法是在使用wait_event时使用wake_up,而使用wait_event_interruptible>时使用wake_up_interruptible。
参考文件:
http://wenku.baidu.com/view/cc8c677b1711cc7931b71699.html
http://blog.chinaunix.net/uid-23037472-id-2565394.html
http://blog.youkuaiyun.com/heanyu/article/details/6552578
程序一:获取现在系统内核中的HZ值。
创建文件夹/nfsroot/kern/2012-05-11/01/。
创建文件/nfsroot/kern/2012-05-11/01/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/param.h> 4 5 MODULE_LICENSE("GPL"); 6 7 static int __init test_init(void) 8 { 9 printk("HZ = %d\n", HZ); 10 return 0; 11 } 12 13 static void __exit test_exit(void) 14 { 15 } 16 17 module_init(test_init); 18 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/01/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

在开发板端载入模块、查看输出、卸载模块,过程如下:

程序二:使用jiffies,获得系统启动时间。
创建文件夹/nfsroot/kern/2012-05-11/02/。
创建文件/nfsroot/kern/2012-05-11/02/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/param.h> 4 #include <linux/jiffies.h> 5 6 MODULE_LICENSE("GPL"); 7 8 static int __init test_init(void) 9 { 10 unsigned long now; 11 int h, m, s; 12 13 printk("jiffies = %lu\n", jiffies); 14 15 now = jiffies / HZ; 16 h = now / (60 * 60); 17 m = (now % (60 * 60)) / 60; 18 s = now % 60; 19 20 printk("system up %d:%d:%d\n", h, m, s); 21 22 return 0; 23 } 24 25 static void __exit test_exit(void) 26 { 27 } 28 29 module_init(test_init); 30 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/02/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 02]# pwd 2 /nfsroot/kern/2012-05-11/02 3 [root@localhost 02]# ls 4 Makefile test.c 5 [root@localhost 02]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/02/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/02/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/02/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 02]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 02]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 02]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 02]# pwd 2 /kern/2012-05-11/02 3 [root@timkyle 02]# ls 4 Makefile modules.order test.ko test.mod.o 5 Module.symvers test.c test.mod.c test.o 6 [root@timkyle 02]# modinfo test.ko 7 filename: test.ko 8 license: GPL 9 vermagic: 2.6.28.6 mod_unload ARMv6 10 [root@timkyle 02]# lsmod 11 [root@timkyle 02]# insmod test.ko ; uptime 12 jiffies = 4294913459 13 system up 5965:9:27 14 21:44:00 up 0 min, load average: 0.11, 0.02, 0.00 15 [root@timkyle 02]# rmmod test 16 [root@timkyle 02]# lsmod 17 [root@timkyle 02]# uptime 18 21:49:35 up 6 min, load average: 0.00, 0.00, 0.00 19 [root@timkyle 02]# insmod test.ko ; uptime 20 jiffies = 14491 21 system up 0:1:12 22 21:49:42 up 6 min, load average: 0.00, 0.00, 0.00 23 [root@timkyle 02]# rmmod test 24 [root@timkyle 02]# lsmod 25 [root@timkyle 02]#
由测试结果可知,系统启动时jiffies并不是初始化为0值!而且经过5分钟后,时间相对准确,但是误差是5分钟!原因如下:

1 /* 2 * Have the 32 bit jiffies value wrap 5 minutes after boot 3 * so jiffies wrap bugs show up earlier. 4 */ 5 #define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))
程序三:利用jiffies及相关宏,实现定时5秒。
创建文件夹/nfsroot/kern/2012-05-11/03/。
创建文件/nfsroot/kern/2012-05-11/03/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/param.h> 4 #include <linux/jiffies.h> 5 6 MODULE_LICENSE("GPL"); 7 8 static void show_uptime(void) 9 { 10 unsigned long now; 11 int h, m, s; 12 13 now = jiffies / HZ; 14 h = now / (60 * 60); 15 m = (now % (60 * 60)) / 60 + 5; 16 s = now % 60; 17 printk("system up %d:%d:%d\n", h, m, s); 18 } 19 20 static int __init test_init(void) 21 { 22 unsigned long pos; 23 24 pos = jiffies + 5 * HZ; 25 26 if (time_before(jiffies, pos)) 27 { 28 printk("Please wait ...\n"); 29 } 30 31 show_uptime(); 32 33 while (!time_after(jiffies, pos)) 34 ; // nothing, just loop! 35 36 show_uptime(); 37 38 return 0; 39 } 40 41 static void __exit test_exit(void) 42 { 43 } 44 45 module_init(test_init); 46 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/03/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 03]# pwd 2 /nfsroot/kern/2012-05-11/03 3 [root@localhost 03]# ls 4 Makefile test.c 5 [root@localhost 03]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/03/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/03/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/03/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 03]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 03]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 03]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 03]# pwd 2 /kern/2012-05-11/03 3 [root@timkyle 03]# ls 4 Makefile modules.order test.ko test.mod.o 5 Module.symvers test.c test.mod.c test.o 6 [root@timkyle 03]# modinfo test.ko 7 filename: test.ko 8 license: GPL 9 vermagic: 2.6.28.6 mod_unload ARMv6 10 [root@timkyle 03]# lsmod 11 [root@timkyle 03]# insmod test.ko ; uptime 12 Please wait ... 13 system up 0:27:27 14 system up 0:27:32 15 22:11:02 up 27 min, load average: 0.29, 0.11, 0.02 16 [root@timkyle 03]# rmmod test 17 [root@timkyle 03]# lsmod 18 [root@timkyle 03]#
程序四:获取系统当前的绝对时间(自1970年经过的秒数)。
创建文件夹/nfsroot/kern/2012-05-11/04/。
创建文件/nfsroot/kern/2012-05-11/04/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/param.h> 4 #include <linux/jiffies.h> 5 #include <linux/time.h> 6 7 MODULE_LICENSE("GPL"); 8 9 static struct timeval tv; 10 static struct timespec ts; 11 12 /* 13 static void show_uptime(void) 14 { 15 unsigned long now; 16 int h, m, s; 17 18 now = jiffies / HZ; 19 h = now / (60 * 60); 20 m = (now % (60 * 60)) / 60 + 5; 21 s = now % 60; 22 printk("system up %d:%d:%d\n", h, m, s); 23 } 24 */ 25 26 static int __init test_init(void) 27 { 28 do_gettimeofday(&tv); 29 printk("timeval.tv_sec = %ld; timeval.tv_usec = %ld\n", tv.tv_sec, tv.tv_usec); 30 31 ts = current_kernel_time(); 32 printk("timespec.tv_set = %ld; timespec.tv_nsec = %ld\n", ts.tv_sec, ts.tv_nsec); 33 34 return 0; 35 } 36 37 static void __exit test_exit(void) 38 { 39 } 40 41 module_init(test_init); 42 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/04/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 04]# pwd 2 /nfsroot/kern/2012-05-11/04 3 [root@localhost 04]# ls 4 Makefile test.c 5 [root@localhost 04]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/04/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/04/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/04/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 04]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 04]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 04]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 04]# pwd 2 /kern/2012-05-11/04 3 [root@timkyle 04]# ls 4 Makefile modules.order test.ko test.mod.o 5 Module.symvers test.c test.mod.c test.o 6 [root@timkyle 04]# modinfo test.ko 7 filename: test.ko 8 license: GPL 9 vermagic: 2.6.28.6 mod_unload ARMv6 10 [root@timkyle 04]# lsmod 11 [root@timkyle 04]# insmod test.ko 12 timeval.tv_sec = 1370558041; timeval.tv_usec = 904708 13 timespec.tv_set = 1370558041; timespec.tv_nsec = 903603000 14 [root@timkyle 04]# rmmod test 15 [root@timkyle 04]# lsmod 16 [root@timkyle 04]#
程序五:短延时及长延时。
创建文件夹/nfsroot/kern/2012-05-11/05/。
创建文件/nfsroot/kern/2012-05-11/05/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/delay.h> 4 #include <linux/jiffies.h> 5 #include <linux/param.h> 6 #include <linux/sched.h> 7 8 MODULE_LICENSE("GPL"); 9 10 static unsigned long dly = 100; 11 12 static int __init test_init(void) 13 { 14 unsigned long end; 15 16 printk("start delay %ld nsecs\n", dly); 17 ndelay(dly); 18 printk("end delay %ld nsecs\n", dly); 19 20 printk("start delay %ld usecs\n", dly); 21 udelay(dly); 22 printk("end delay %ld usecs\n", dly); 23 24 printk("start delay %ld msecs\n", dly); 25 mdelay(dly); 26 printk("end delay %ld msecs\n", dly); 27 28 printk("start delay 5 secs\n"); 29 end = jiffies + 5 * HZ; 30 while (time_before(jiffies, end)) 31 schedule(); 32 printk("end delay 5 secs\n"); 33 34 printk("start delay 10 secs\n"); 35 set_current_state(TASK_INTERRUPTIBLE); 36 schedule_timeout(10 * HZ); 37 printk("end delay 10 secs\n"); 38 39 return 0; 40 } 41 42 static void __exit test_exit(void) 43 { 44 } 45 46 module_init(test_init); 47 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/05/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 05]# pwd 2 /nfsroot/kern/2012-05-11/05 3 [root@localhost 05]# ls 4 Makefile test.c 5 [root@localhost 05]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/05/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/05/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/05/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 05]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 05]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 05]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 05]# ls 2 Makefile modules.order test.ko test.mod.o 3 Module.symvers test.c test.mod.c test.o 4 [root@timkyle 05]# modinfo test.ko 5 filename: test.ko 6 license: GPL 7 vermagic: 2.6.28.6 mod_unload ARMv6 8 [root@timkyle 05]# insmod test.ko 9 start delay 100 nsecs 10 end delay 100 nsecs 11 start delay 100 usecs 12 end delay 100 usecs 13 start delay 100 msecs 14 end delay 100 msecs 15 start delay 5 secs 16 end delay 5 secs 17 start delay 10 secs 18 end delay 10 secs 19 [root@timkyle 05]# lsmod 20 test 1440 0 - Live 0xbf054000 21 [root@timkyle 05]# rmmod test 22 [root@timkyle 05]# lsmod 23 [root@timkyle 05]#
程序六:使用等待队列延时。
创建文件夹/nfsroot/kern/2012-05-11/06/。
创建文件/nfsroot/kern/2012-05-11/06/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/wait.h> 4 #include <linux/param.h> 5 #include <linux/sched.h> 6 7 MODULE_LICENSE("GPL"); 8 9 static wait_queue_head_t wait; 10 11 static int __init test_init(void) 12 { 13 init_waitqueue_head(&wait); 14 15 printk("start to wait 10 secs\n"); 16 wait_event_interruptible_timeout(wait, 0, 10 * HZ); 17 printk("end to wait 10 secs\n"); 18 19 return 0; 20 } 21 22 static void __exit test_exit(void) 23 { 24 } 25 26 module_init(test_init); 27 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/06/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 06]# pwd 2 /nfsroot/kern/2012-05-11/06 3 [root@localhost 06]# ls 4 Makefile test.c 5 [root@localhost 06]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/06/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/06/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/06/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 06]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 06]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 06]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 06]# pwd 2 /kern/2012-05-11/06 3 [root@timkyle 06]# ls 4 Makefile modules.order test.ko test.mod.o 5 Module.symvers test.c test.mod.c test.o 6 [root@timkyle 06]# modinfo test.ko 7 filename: test.ko 8 license: GPL 9 vermagic: 2.6.28.6 mod_unload ARMv6 10 [root@timkyle 06]# lsmod 11 [root@timkyle 06]# insmod test.ko 12 start to wait 10 secs 13 end to wait 10 secs 14 [root@timkyle 06]# lsmod 15 test 1400 0 - Live 0xbf060000 16 [root@timkyle 06]# rmmod test 17 [root@timkyle 06]# lsmod 18 [root@timkyle 06]#
程序七:使用内核定时器延时,并执行相应任务。
创建文件夹/nfsroot/kern/2012-05-11/07/。
创建文件/nfsroot/kern/2012-05-11/07/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/timer.h> 4 #include <linux/jiffies.h> 5 #include <linux/param.h> 6 7 MODULE_LICENSE("GPL"); 8 9 static struct timer_list mytimer; 10 static unsigned long count = 1; 11 12 static void my_func(unsigned long data) 13 { 14 printk("\nafter 10 secs, data = %ld\n", data); 15 16 printk("set timer of 10 secs again.\n"); 17 mytimer.data = count++; 18 mod_timer(&mytimer, jiffies + 10 * HZ); 19 printk("after mod_timer()\n"); 20 } 21 22 static int __init test_init(void) 23 { 24 init_timer(&mytimer); 25 mytimer.expires = jiffies + 10 * HZ; 26 mytimer.data = count++; 27 mytimer.function = my_func; 28 29 printk("set timer of 10 secs.\n"); 30 add_timer(&mytimer); 31 printk("after add_timer()\n"); 32 33 return 0; 34 } 35 36 static void __exit test_exit(void) 37 { 38 printk("del the timer\n"); 39 del_timer(&mytimer); 40 printk("after del_timer()\n"); 41 } 42 43 module_init(test_init); 44 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/07/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 07]# pwd 2 /nfsroot/kern/2012-05-11/07 3 [root@localhost 07]# ls 4 Makefile test.c 5 [root@localhost 07]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/07/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/07/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/07/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 07]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 07]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 07]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 05]# pwd 2 /kern/2012-05-11/05 3 [root@timkyle 05]# ls 4 [root@timkyle 07]# pwd 5 /kern/2012-05-11/07 6 [root@timkyle 07]# ls 7 Makefile modules.order test.ko test.mod.o 8 Module.symvers test.c test.mod.c test.o 9 [root@timkyle 07]# modinfo test.ko 10 filename: test.ko 11 license: GPL 12 vermagic: 2.6.28.6 mod_unload ARMv6 13 [root@timkyle 07]# lsmod 14 [root@timkyle 07]# insmod test.ko 15 set timer of 10 secs. 16 after add_timer() 17 [root@timkyle 07]# lsmod 18 test 1720 0 - Live 0xbf078000 19 [root@timkyle 07]# 20 after 10 secs, data = 1 21 set timer of 10 secs again. 22 after mod_timer() 23 24 after 10 secs, data = 2 25 set timer of 10 secs again. 26 after mod_timer() 27 28 after 10 secs, data = 3 29 set timer of 10 secs again. 30 after mod_timer() 31 32 after 10 secs, data = 4 33 set timer of 10 secs again. 34 after mod_timer() 35 36 [root@timkyle 07]# rmmod test 37 del the timer 38 after del_timer() 39 [root@timkyle 07]# lsmod 40 [root@timkyle 07]#
程序八:使用等待队列实现休眠(不确定延时)。
创建文件夹/nfsroot/kern/2012-05-11/08/。
创建文件/nfsroot/kern/2012-05-11/08/test.c,内容如下:

1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/timer.h> 4 #include <linux/jiffies.h> 5 #include <linux/param.h> 6 #include <linux/wait.h> 7 #include <linux/sched.h> 8 9 MODULE_LICENSE("GPL"); 10 11 #define INT 1 12 13 static struct timer_list mytimer; 14 static wait_queue_head_t mywait; 15 static unsigned long count = 1; 16 17 static void my_func(unsigned long data) 18 { 19 printk("\nafter 10 secs, data = %ld, count = %ld\n", data, count); 20 21 #if INT 22 printk("start wake_up_interruptible()\n"); 23 wake_up_interruptible(&mywait); 24 printk("after wake_up_interruptible()\n"); 25 #else 26 printk("start wake_up()\n"); 27 wake_up(&mywait); 28 printk("after wake_up()\n"); 29 #endif 30 31 printk("set timer of 10 secs again.\n"); 32 mytimer.data = count++; 33 mod_timer(&mytimer, jiffies + 10 * HZ); 34 printk("after mod_timer()\n"); 35 } 36 37 static int __init test_init(void) 38 { 39 init_timer(&mytimer); 40 mytimer.expires = jiffies + 10 * HZ; 41 mytimer.data = count++; 42 mytimer.function = my_func; 43 44 init_waitqueue_head(&mywait); 45 46 printk("set timer of 10 secs.\n"); 47 add_timer(&mytimer); 48 printk("after add_timer()\n"); 49 50 #if INT 51 printk("start wait_event_interruptible()\n"); 52 wait_event_interruptible(mywait, count == 4); 53 printk("after wait_event_interruptible()\n"); 54 #else 55 printk("start wait_event()\n"); 56 wait_event(mywait, count == 4); 57 printk("after wait_event()\n"); 58 #endif 59 60 printk("\ndel the timer\n"); 61 del_timer(&mytimer); 62 printk("after del_timer()\n"); 63 64 return 0; 65 } 66 67 static void __exit test_exit(void) 68 { 69 } 70 71 module_init(test_init); 72 module_exit(test_exit);
创建文件/nfsroot/kern/2012-05-11/08/Makefile,内容如下:

1 obj-m := test.o 2 3 KERN := /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 4 5 all: 6 make -C $(KERN) M=`pwd` modules 7 8 clean: 9 make -C $(KERN) M=`pwd` modules clean 10 rm -f modules.order
在主机端编译模块,过程如下:

1 [root@localhost 08]# pwd 2 /nfsroot/kern/2012-05-11/08 3 [root@localhost 08]# ls 4 Makefile test.c 5 [root@localhost 08]# make 6 make -C /timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410 M=`pwd` modules 7 make[1]: Entering directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 8 CC [M] /nfsroot/kern/2012-05-11/08/test.o 9 Building modules, stage 2. 10 MODPOST 1 modules 11 CC /nfsroot/kern/2012-05-11/08/test.mod.o 12 LD [M] /nfsroot/kern/2012-05-11/08/test.ko 13 make[1]: Leaving directory `/timkyle-dev/my/arm/newnfs/home/linux-2.6.28_smdk6410' 14 [root@localhost 08]# ls 15 Makefile Module.symvers test.ko test.mod.o 16 modules.order test.c test.mod.c test.o 17 [root@localhost 08]# modinfo test.ko 18 filename: test.ko 19 license: GPL 20 depends: 21 vermagic: 2.6.28.6 mod_unload ARMv6 22 [root@localhost 08]#
在开发板端载入模块、查看输出、卸载模块,过程如下:

1 [root@timkyle 08]# pwd 2 /kern/2012-05-11/08 3 [root@timkyle 08]# ls 4 Makefile modules.order test.ko test.mod.o 5 Module.symvers test.c test.mod.c test.o 6 [root@timkyle 08]# modinfo test.ko 7 filename: test.ko 8 license: GPL 9 vermagic: 2.6.28.6 mod_unload ARMv6 10 [root@timkyle 08]# lsmod 11 [root@timkyle 08]# insmod test.ko 12 set timer of 10 secs. 13 after add_timer() 14 start wait_event_interruptible() 15 16 after 10 secs, data = 1, count = 2 17 start wake_up_interruptible() 18 after wake_up_interruptible() 19 set timer of 10 secs again. 20 after mod_timer() 21 22 after 10 secs, data = 2, count = 3 23 start wake_up_interruptible() 24 after wake_up_interruptible() 25 set timer of 10 secs again. 26 after mod_timer() 27 after wait_event_interruptible() 28 29 del the timer 30 after del_timer() 31 [root@timkyle 08]# lsmod 32 test 2128 0 - Live 0xbf090000 33 [root@timkyle 08]# rmmod test 34 [root@timkyle 08]# lsmod 35 [root@timkyle 08]#
由程序及测试结果可知,被唤醒的等待队列在检查条件是否满足时,有一定的延时效应。
<本文来至于:http://www.cnblogs.com/timkyle/archive/2012/05/12/2496752.html>