内核定时器是管理内核时间的基础。内核经常要推迟执行一些代码,如底半部机制就是为了将工作推后执行。时钟中断由系统的定时硬件以周期性的时间间隔产生,这个间隔由内核根据HZ来确定。每当时钟中断发生时,全局变量jiffies(unsigned long)就加1,因此jiffies记录了自linux启动后时钟中断发生的次数。内核定时器用于控制定时器处理函数在未来的某个特定时间执行。内核定时器注册的处理函数只执行一次,它在超时后就自行销毁,动态定时器不断地创建和销毁,而且它的运行次数也不受限制。 定时器的使用只须执行一些初始化工作,设置一个超时时间,指定超时发生后执行的函数,然后激活定时器就可以了。我们从一个简单的秒定时器驱动实例开始。 编写一个简单的秒定时器驱动,应用从驱动中读取定时器值并打印出来。 在kernel/drivers/char/x4412目录下新建x4412-second.c文件,编辑内容如下: - #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/cdev.h>
- #include <linux/uaccess.h>
- #include <linux/timer.h>
- #include <asm/atomic.h>
- #include <linux/slab.h>
- #include <linux/device.h>
-
- static int x4412_second_major;
- static struct class *cdev_class;
- struct x4412_second_dev{
- struct cdev cdevp;
- atomic_t counter;
- struct timer_list s_timer;
- };
- struct x4412_second_dev *second_devp;
- static void second_timer_handle(unsigned long arg)
- {
- mod_timer(&second_devp->s_timer,jiffies+HZ);
- atomic_inc(&second_devp->counter);
- }
- static int x4412_second_open(struct inode *inode,struct file *filp)
- {
- init_timer(&second_devp->s_timer);
- second_devp->s_timer.function=&second_timer_handle;
- second_devp->s_timer.expires=jiffies+HZ;
- add_timer(&second_devp->s_timer);
- atomic_set(&second_devp->counter,0);
- return 0;
- }
- int x4412_second_release(struct inode *inode,struct file *filp)
- {
- del_timer(&second_devp->s_timer);
- return 0;
- }
- static ssize_t x4412_second_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
- {
- int counter;
- counter=atomic_read(&second_devp->counter);
- if(copy_to_user(buf,&counter,0x4))
- return -EFAULT;
- else
- return sizeof(unsigned int);
- }
- static const struct file_operations second_fops={
- .owner=THIS_MODULE,
- .open=x4412_second_open,
- .release=x4412_second_release,
- .read=x4412_second_read,
- };
- static void second_setup_cdev(struct x4412_second_dev *second_devp,int index)
- {
- int err,devno=MKDEV(x4412_second_major,index);
- cdev_init(&second_devp->cdevp,&second_fops);
- second_devp->cdevp.owner=THIS_MODULE;
- second_devp->cdevp.ops=&second_fops;
- err=cdev_add(&second_devp->cdevp,devno,1);
- if(err)
- printk("add the second cdev failly");
- }
- static void x4412_clear_cdev(void)
- {
- cdev_del(&second_devp->cdevp);
- unregister_chrdev_region(MKDEV(x4412_second_major, 0), 1);
- }
- int __init x4412_second_init(void)
- {
- int ret;
- dev_t devno;
- ret=alloc_chrdev_region(&devno,0,1,"x4412-second");
- x4412_second_major=MAJOR(devno);
- if(ret<0)
- return ret;
- second_devp=kmalloc(sizeof(struct x4412_second_dev),GFP_KERNEL);
- if(!second_devp)
- {
- ret=-ENOMEM;
- goto fail_malloc;
- }
- memset(second_devp,0,sizeof(struct x4412_second_dev));
- second_setup_cdev(second_devp,0);
- cdev_class = class_create(THIS_MODULE,"x4412-second");
- if(IS_ERR(cdev_class))
- {
- x4412_clear_cdev();
- return PTR_ERR(cdev_class);
- }
- device_create(cdev_class,NULL,MKDEV(x4412_second_major,0),NULL,"x4412-second");
- return 0;
- fail_malloc:
- unregister_chrdev_region(devno,1);
- return -1;
- }
- void x4412_second_exit(void)
- {
- device_destroy(cdev_class,MKDEV(x4412_second_major,0));
- class_destroy(cdev_class);
- cdev_del(&second_devp->cdevp);
- kfree(second_devp);
- unregister_chrdev_region(MKDEV(x4412_second_major,0),1);
- }
- MODULE_AUTHOR("www.9tripod.com");
- MODULE_LICENSE("Dual BSD/GPL");
- module_init(x4412_second_init);
- module_exit(x4412_second_exit);
复制代码 编辑kernel/drivers/char/x4412/Kconfig文件,添加如下语句: - config X4412_SECOND_DRIVER
- tristate "x4412 second timer driver"
- default m
- help
- compile for x4412 second timer driver,y for kernel,m for module.
复制代码 编辑kernel/drivers/char/x4412/Makefile文件,添加如下语句: - obj-$(CONFIG_X4412_SECOND_DRIVER) += x4412-second.o
复制代码 编译内核,在kernel/drivers/char/x4412目录下将会生成目标文件x4412-second.ko。 在ubuntu用户目录或samba目录下新建应用程序x4412-second-app.c文件,编辑内容如下: - #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
-
- main()
- {
- int fd;
- int counter = 0;
- int old_counter = 0;
- /*打开/dev/second 设备文件*/
- fd = open("/dev/x4412-second", O_RDONLY);
- if (fd != - 1)
- {
- while (1)
- {
- read(fd,&counter, sizeof(unsigned int));//读目前经历的秒数
- if(counter!=old_counter)
- {
- printf("seconds after open /dev/x4412-second :%d\n",counter);
- old_counter = counter;
- }
- }
- }
- else
- {
- printf("Device open failure\n");
- }
- }
复制代码 执行如下指令编译应用程序: - arm-none-linux-gnueabi-gcc x4412-second-app.c -o x4412-second-app
复制代码 加载驱动模块后,运行应用程序,观察测试效果: - [root@x4412 mnt]# insmod x4412-second.ko
- [root@x4412 mnt]# ./x4412-second-app
- [ 77.851276] CPU2: Booted secondary processor
- [ 77.855022] Switched to NOHz mode on CPU #2
- seconds after open /dev/x4412-second :1
- seconds after open /dev/x4412-second :2
- seconds after open /dev/x4412-second :3
- seconds after open /dev/x4412-second :4
- seconds after open /dev/x4412-second :5
- seconds after open /dev/x4412-second :6
-
- [root@x4412 mnt]#
复制代码 通过实例,回过头来分析定时器的使用方法。 第一步:定义一个定时器: - struct timer_list s_timer;
复制代码 第二步:在open函数中初始化定时器,并指定定时器服务函数,设置定时器到期时间,然后将定时器添加到内核定时器链表。 - static int x4412_second_open(struct inode *inode,struct file *filp)
- {
- ……
- init_timer(&second_devp->s_timer);//打开驱动时初始化定时器
- second_devp->s_timer.function=&second_timer_handle;//指定定时器服务函数
- second_devp->s_timer.expires=jiffies+HZ;//设定定时器到期时间为1秒
- add_timer(&second_devp->s_timer);//将定时器添加到内核定时器链表
- ……
- }
复制代码 第三步:编写定时器服务函数: - static void second_timer_handle(unsigned long arg)
- {
- mod_timer(&second_devp->s_timer,jiffies+HZ);//修改定时器的expire并再次启动
- ……
- }
复制代码 第四步:在release函数中删除定时器: - int x4412_second_release(struct inode *inode,struct file *filp)
- {
- del_timer(&second_devp->s_timer);//关闭驱动时删除定时器
- ……
- }
复制代码 在本示例中还定义了一个原子变量,用于记录定时器一共经历了多少秒。在open函数中,初始化原子变量为0,然后每发生一次定时器中断,原子变量加1。在read函数中,通过atomic_read函数读出原子变量值,并通过copy_to_user函数上报给应用。在open函数中定义的定时器到期时间为jiffies+HZ,这里HZ表示1秒,jiffies记录了系统启动后所经历的时间,jiffies+HZ表示从当前时间开始,再过1秒触发定时器中断,即定时器时间为1秒。由于定时器的工作是不连续的,因此在中断服务函数中通过mod_timer函数重新修改定时器到期时间,并再次启动。修改后的到期时间仍然为jiffies+HZ,这时的jiffies和之前的jiffies值已经不同了,它记录的是当前时间,不断的会增加。正是利用了jiffies的特性,巧妙的实现了间隔一秒循环定时的功能。 编译好的映像:
x4412-second.ko
(4.33 KB, 下载次数: 5)
x4412-second-app
(6.06 KB, 下载次数: 4)
|