代码如下:
#include <linux/module.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #include<linux/jiffies.h> #include<linux/timer.h> #define SECOND_MAJOR 260 static int second_major=SECOND_MAJOR; module_param(second_major,int,S_IRUGO); struct second_dev{ struct cdev cdev; atomic_t counter; struct timer_list s_timer; }; static struct second_dev *second_devp; static void second_timer_handler(struct timer_list *s) { mod_timer(&second_devp->s_timer,jiffies+HZ); atomic_inc(&second_devp->counter); printk(KERN_INFO "current jiffies is %ld ",jiffies); } static int second_open(struct inode *ins_timerode,struct file *filep) { timer_setup(&second_devp->s_timer,second_timer_handler,0); //second_devp->s_timer.function=&second_timer_handler; second_devp->s_timer.expires=jiffies+HZ; add_timer(&second_devp->s_timer); atomic_set(&second_devp->counter,0); return 0; } static int second_release(struct inode *inode,struct file *filep) { del_timer(&second_devp->s_timer); return 0; } static ssize_t second_read(struct file *filep,char __user *buf,size_t count,loff_t *ppos) { int counter; counter=atomic_read(&second_devp->counter); if(put_user(counter,(int *)buf)) return -EFAULT; else { return sizeof(unsigned int ); } } static const struct file_operations second_fops={ .owner=THIS_MODULE, .open=second_open, .release=second_release, .read=second_read, }; static void second_setup_cdev(struct second_dev *dev,int index) { int err; int devno=MKDEV(second_major,index); cdev_init(&dev->cdev,&second_fops); dev->cdev.owner=THIS_MODULE; err=cdev_add(&dev->cdev,devno,1); if(err) { printk(KERN_ERR "Failed to add second device "); } } static int __init second_init(void) { int ret; dev_t devno=MKDEV(second_major,0); if(second_major) { ret=register_chrdev_region(devno,1,"second"); } else { ret=alloc_chrdev_region(&devno,0,1,"second"); second_major=MAJOR(devno); } if(ret <0) { return ret; } second_devp=kzalloc(sizeof(*second_devp),GFP_KERNEL); if(!second_devp){ ret=-ENOMEM; goto fail_malloc; } second_setup_cdev(second_devp,0); return 0; fail_malloc: unregister_chrdev_region(devno,1); return ret; } module_init(second_init); static void __exit second_exit(void) { cdev_del(&second_devp->cdev); kfree(second_devp); unregister_chrdev_region(MKDEV(second_major,0),1); } module_exit(second_exit); MODULE_AUTHOR("maple"); MODULE_LICENSE("GPL v2");
1second_dev结构中增加 timer_list的定时器结构.在调用 second_setup_cdev进行创建设备
2second_open中调用timer_setup进行定时器创建.在内核4.15以前使用的是init_timer.在4.15以后删除掉了init_timer.
其源码分析如下:
#define timer_setup(timer, callback, flags)
__init_timer((timer), (callback), (flags))
原来这个是一个宏,其实还是调用的是__init_timer
#ifdef CONFIG_LOCKDEPz
#define __init_timer(_timer, _fn, _flags)
do {
static struct lock_class_key __key;
init_timer_key((_timer), (_fn), (_flags), #_timer, &__key);
} while (0)
#define __init_timer_on_stack(_timer, _fn, _flags)
do {
static struct lock_class_key __key;
init_timer_on_stack_key((_timer), (_fn), (_flags),
#_timer, &__key);
} while (0)
#else
#define __init_timer(_timer, _fn, _flags)
init_timer_key((_timer), (_fn), (_flags), NULL, NULL)
#define __init_timer_on_stack(_timer, _fn, _flags)
init_timer_on_stack_key((_timer), (_fn), (_flags), NULL, NULL)
#endif
config_lockedp用于检测死锁,常用debug,这里假定没有开这个宏
所以这里的又会转调init_timer_key
void init_timer_key(struct timer_list *timer,
void (*func)(struct timer_list *), unsigned int flags,
const char *name, struct lock_class_key *key)
{
#如果没有打开CONFIG_DEBUG_OBJECTS_TIMERS的话,debug_init 为null
debug_init(timer);
#所以主要调用这个函数初始化timer
do_init_timer(timer, func, flags, name, key);
}
static void do_init_timer(struct timer_list *timer,
void (*func)(struct timer_list *),
unsigned int flags,
const char *name, struct lock_class_key *key)
{
#pprev 为null,说明这个timer处于pending状态,当然了刚开始创建timer的时候当然要pending了,毕竟
#还没有开始执行
timer->entry.pprev = NULL;
#时间到期要执行的回调函数
timer->function = func;
#看来flags中保存了当前的cpu id
timer->flags = flags | raw_smp_processor_id();
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
从上面的代码可以看到在timer_setup中已经将对timer的回调函数和参数做了一个初始化.在second_open中初始化了second_devp->counter以及定时器超时的参数
3 每当读取设备的时候的操作就是读取second_devp->counter值并放到用户空间.
makfile文件:
obj-m:=irq_test.o #产生irq_test模块的目标文件
#目标文件 文件 要与模块名字相同
CURRENT_PATH:=$(shell pwd) #模块所在的当前路径
LINUX_KERNEL:=$(shell uname -r) #linux内核代码的当前版本
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
CONFIG_MODULE_SIG=n
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理模块
执行sudo insmod irq_test.ko报如下错误
insmod: ERROR: could not insert module irq_test.ko: Device or resource busy
执行cat /proc/devices 查看已存在的设备发现有deviceno=248的设备
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
10 misc
13 input
21 sg
29 fb
81 video4linux
89 i2c
99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 ttyMAX
216 rfcomm
226 drm
241 media
242 aux
243 mei
244 hidraw
245 ttyDBC
246 bsg
247 hmm_device
248 watchdog
249 rtc
250 dax
251 dimmctl
252 ndctl
253 tpm
254 gpiochip
在代码中将SECOND_MAJOR修改成260. 编译后再插入就没问题了.
执行命令sudo mknod /dev/second c 260 0 创建一个设备
然后另外创建一个文件创建一个while循环不停的读/dev/second设备.这样就能不停的触发second_read获取econd_devp->counter的值
int main() { int fd; int counter=0; int old_counter=0; fd=open("/dev/second",O_RDONLY); if(fd != -1) { while(1) { read(fd,&counter,sizeof(unsigned int)); if(counter != old_counter){ printf("seconds after open /dev/second:%d ",counter); old_counter=counter; } } } else{ printf("open /dev/second failure "); } return 1; }
test@test:~/vs_code_prj$ ./test
seconds after open /dev/second:1
seconds after open /dev/second:2
seconds after open /dev/second:3
seconds after open /dev/second:4
seconds after open /dev/second:5
seconds after open /dev/second:6
seconds after open /dev/second:7
seconds after open /dev/second:8
seconds after open /dev/second:9
seconds after open /dev/second:10
seconds after open /dev/second:11
seconds after open /dev/second:12
seconds after open /dev/second:13