Linux内核移植 part4:内核timer
2016-04-19 12:29
501 查看
kernel timer
标签: 定时器 工作队列测试代码
用来演示timer和workqueue的工作方式,由timer每隔500ms触发一个event,该event用来在终端打印log。代码如下
Makefile
#ifneq ($(KERNELRELEASE),) obj-m := myphone.o myphone-objs := workqueue.o #else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions rm -rf Module.* modules.* .PHONY: default clean #endif
test module (workqueue.c)
#include <linux/module.h> #define PHONE_VBUS_POLL_INTERVAL msecs_to_jiffies(500) struct phone_core { struct work_struct events; struct timer_list vbus_timer; spinlock_t lock; }; static struct phone_core core; static struct workqueue_struct *phone_wq; static void phone_event(struct work_struct *work); //static struct work_struct events; static void phone_vbus_poll(unsigned long data) { struct phone_core *core = (struct phone_core *)data; mod_timer(&core->vbus_timer, jiffies + PHONE_VBUS_POLL_INTERVAL); printk(KERN_INFO "%s:%d.\n", __func__, __LINE__); if (queue_work(phone_wq, &core->events)) return; } static void phone_event(struct work_struct *work) { printk(KERN_INFO "events happened.\n"); } int phone_init(void) { printk(KERN_INFO "%s:%d.\n", __func__, __LINE__); INIT_WORK(&core.events, phone_event); setup_timer(&core.vbus_timer, phone_vbus_poll, (unsigned long)&core); add_timer(&core.vbus_timer); phone_wq = alloc_workqueue("phone_wq", WQ_FREEZABLE, 0); if (phone_wq) { printk(KERN_INFO "init my phone successfully.\n"); return 0; } else pr_err("can't alloc work queue.\n"); return -ENOMEM; } void phone_cleanup(void) { destroy_workqueue(phone_wq); del_timer(&core.vbus_timer); printk(KERN_INFO "remove my phone.\n"); } static int __init work_init(void) { int retval; printk(KERN_INFO "start working in GalaxyCore.\n"); retval = phone_init(); if (retval) goto phone_init_fail; goto out; phone_init_fail: phone_cleanup(); out: return retval; } static void __exit work_exit(void) { phone_cleanup(); } subsys_initcall(work_init); module_exit(work_exit); MODULE_LICENSE("GPL");
执行
$make $insmod myphone.ko $dmesg
查看kernel log可以看到如下信息:结果就是每隔500ms打印一句 “events happened”.
[ 7106.403102] start working in GalaxyCore. [ 7106.403105] phone_init:39. [ 7106.403110] init my phone successfully. [ 7106.406779] phone_vbus_poll:27. [ 7106.406821] events happened. [ 7106.905355] phone_vbus_poll:27. [ 7106.905366] events happened. [ 7107.403956] phone_vbus_poll:27. [ 7107.403994] events happened.
移除模块
$rmmod myphone
kernel log>>
[ 7377.632309] phone_vbus_poll:27. [ 7377.632330] events happened. [ 7378.130887] phone_vbus_poll:27. [ 7378.130906] events happened. [ 7378.455502] remove my phone.
执行流程
初始化timer,workqueue和work struct在timer回调函数中将work struct放到workqueue中,交由CPU调度
原理分析
问题分解内核如何管理timer
关于jiffies
解剖timer
工作队列如何调度
jiffies
extern unsigned long __msecs_to_jiffies(const unsigned int m); #if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ) /* * HZ is equal to or smaller than 1000, and 1000 is a nice round * multiple of HZ, divide with the factor between them, but round * upwards: */ static inline unsigned long _msecs_to_jiffies(const unsigned int m) { return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ); } #elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC) /* * HZ is larger than 1000, and HZ is a nice round multiple of 1000 - * simply multiply with the factor between them. * * But first make sure the multiplication result cannot overflow: */ static inline unsigned long _msecs_to_jiffies(const unsigned int m) { if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET)) return MAX_JIFFY_OFFSET; return m * (HZ / MSEC_PER_SEC); } #else /* * Generic case - multiply, round and divide. But first check that if * we are doing a net multiplication, that we wouldn't overflow: */ static inline unsigned long _msecs_to_jiffies(const unsigned int m) { if (HZ > MSEC_PER_SEC && m > jiffies_to_msecs(MAX_JIFFY_OFFSET)) return MAX_JIFFY_OFFSET; return (MSEC_TO_HZ_MUL32 * m + MSEC_TO_HZ_ADJ32) >> MSEC_TO_HZ_SHR32; } #endif ... static __always_inline unsigned long msecs_to_jiffies(const unsigned int m) { if (__builtin_constant_p(m)) { if ((int)m < 0) return MAX_JIFFY_OFFSET; return _msecs_to_jiffies(m); } else { return __msecs_to_jiffies(m); } }
以毫秒转换为jiffies为例
系统接口:
msecs_to_jiffies
jiffies含义:从代码里的换算公式中可以看出, jiffies其实就是时钟的节拍数,频率f=节拍数/s,单位为HZ。
jiffies维护:和timer相关
2. timer
下次和软中断一起整理
相关文章推荐
- Linux共享Windows文件夹
- Linux系统下mysql中文乱码问题
- Linux 如何查看修改DNS配置
- Linux中的编码转换程序convmv的安装和使用教程
- 阅读Linux0.11——main.c
- Centos6.5搭建Wiki
- Linux 下java 中添加c++
- Linux 更改目录及子目录权限
- 笔记:构建嵌入式Linux系统(第二版)—— 第一章 概述
- linux 红帽7忘记root密码
- linux下的内存分布
- centos ab压力测试工具安装
- 鸟哥私房菜linux基础学习笔记 3
- Linux用户与用户组详解
- linux下mysql开启远程访问权限及防火墙开放3306端口
- Linux search file path
- linux 关于MYSQL数据库命令(查看,备份等操作)
- PostgreSQL(Linux)安装、启动、停止、重启
- Java程序执行Linux命令
- Linux修改系统时间命令