您的位置:首页 > 运维架构 > Linux

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

下次和软中断一起整理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: