代碼如下:
#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\n",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\n"); } } 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\n",counter); old_counter=counter; } } } else{ printf("open /dev/second failure\n"); } 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
內核打印中也可以看到定時器定時的輸出
[19294.462157] second_dev: loading out-of-tree module taints kernel.
[19294.462159] second_dev: module license 'GPL V2' taints kernel.
[19294.462160] Disabling lock debugging due to kernel taint
[19401.456462] current jiffies is 4299742872
[19402.480465] current jiffies is 4299743128
[19403.504256] current jiffies is 4299743384
[19404.528596] current jiffies is 4299743640
[19405.552647] current jiffies is 4299743896
[19406.577182] current jiffies is 4299744152
[19407.600567] current jiffies is 4299744408
[19408.624409] current jiffies is 4299744664
[19409.649629] current jiffies is 4299744920
[19410.673428] current jiffies is 4299745176
[19411.696759] current jiffies is 4299745432
[19412.721520] current jiffies is 4299745688
[19413.744562] current jiffies is 4299745944