linux驱动开发学习四:中断与时钟
代码如下:
#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的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <br> 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2019-02-24 linux c编程:互斥锁条件变量
2018-02-24 自己动手开发网络服务器(三):实现多线程