Linux内核开发之中断与时钟[转]
来源:互联网 发布:50部网络禁书下载 编辑:程序博客网 时间:2024/05/22 05:04
资料地址:http://www.cnblogs.com/hanyan225/archive/2010/10/25/1860087.html
定时器分为硬件和软件定时器,软件定时器最终还是要依靠硬件定时器来完成。内核在时钟中断发生后检测各定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行。实质上,时钟中断处理程序执行update_process_timers函数,该函数调用run_local_timers函数,这个函数处理TIMER_SOFTIRQ软中断,运行当前处理上到期的所有定时器。 struct timer_list{ structlist_head entry; //定时器列表 unsigned long expires; //定时器到期时间 void(*function)(unsigned long) ;//定时器处理函数 unsigned long data; //作为参数被传入定时器处理函数 struct timer_base_s *base; }
struct cdev cdev; ... struct timer_list xxx_timer;
struct xxx_dev *dev =filp->private_data; ... init_timer(&dev->xxx_timer); dev->xxx_timer.function =&xxx_do_handle; dev->xxx_timer.data =(unsigned long)dev; dev->xxx_timer.expires =jiffies + delay; add_timer(&dev->xxx_timer); ... return 0; ... del_timer(&second_devp->s_timer); ... struct xxx_device *dev = (struct xxx_device*)(arg); ... //调度定时器再执行 dev->xxx_timer.expires =jiffies + delay; add_timer(&dev->xxx_timer);
unsigned long timeout =msecs_to_jiffies(msecs) + 1; while(timeout) timeout =schedule_timeout_uninterruptible(timeout); unsigned long timeout =msecs_to_jiffies(msecs) + 1; while(timeout&& !signal_pending(current)) timeout =schedule_timeout_interruptible(timeout); returnjiffies_to_msecs(timeout); __set_current_state(TASK_INTERRUPTIBLE); returnschedule_timeout(timeout); __set_current_state(TASK_UNINTERRUPTIBLE); returnschedule_timeout(timeout);
... int int_src =read_int_status(); //读硬件的中断相关寄存器 switch(int_src) //判断中断源 { case DEV_A: dev_a_handler(); break; caseDEV_B: dev_b_handler(); break; .... default: break; } ..... ... tasklet_schedule(&xxx_tasklet); .. result= request_irq(xxx_irq, xxx_interrupt,SA_INTERRUPT, "XXX",NULL); //申请中断 ... .. free_irq(xxx_irq, xxx_interrupt); //释放中断 .. struct work_struct xxx_wq; ..... ... schedule_work(&xxx_wq); .. result= request_irq(xxx_irq, xxx_interrupt,SA_INTERRUPT, "XXX",NULL); //申请中断 ... INIT_WORK(&xxx_wq, (void(*)(void *))xxx_do_work, NULL); ... .. free_irq(xxx_irq, xxx_interrupt); //释放中断 .. ... int status = read_int_status(); //获取终端源 if(!is_myint(dev_id, status)) //判断是否是本设备的中断 { return IRQ_NONE://立即返回 } .. return IRQ_HANDLED; .. result= request_irq(xxx_irq, xxx_interrupt,SA_SHIRQ, "XXX",xxx_dev); //申请共享中断 ... .. free_irq(xxx_irq, xxx_interrupt); //释放中断 ..
Linux内核中定义提供了一些用于操作定时器的数据结构和函数如下:
1)timer_list:说定时器,当然要来个定时器的结构体
2)初始化定时器:
void init_timer(struct timer_list *timer);
经过这个初始化后,entry的next为NULL,并给base赋值
3)增加定时器:
void add_timer(struct timer_list*timer);
该函数用于注册内核定时器,并将定时器加入到内核动态定时器链表中。
4)删除定时器:int del_timer(struct timer_list *timer);
说明:del_timer_sync是del_timer的同步版,主要在多处理器系统中使用,如果编译内核时不支持SMP,del_timer_sync和del_timer等价.
5)修改定时器:
int mod_timer(struct timer_list *timer, unsigned longexpires);
下边是一个使用定时器的模版:
struct xxx_dev
{
};
int xxx_func1(...) //xxx驱动中某函数
{
}
int xxx_func2(...) //驱动中某函数
{
}
static void xxx_do_timer(unsigned long arg) //定时器处理函数
{
}
在定时器函数中往往会在做完具体工作后,延迟expires并将定时器再次添加到内核定时器链表中,以便定时器能被再次触发。
在内核定时器中,常常少不了要说下内核延迟的事,请接着往下看:
1)短延迟:在linux内核中提供了三个函数来分别实现纳秒,微秒,毫秒延迟,原理上是忙等待,它根据CPU频率进行一定次数的循环
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
毫秒延迟已经相当大了,当然更秒延迟当然要小一些,在内核中,为了性能,最好不要用mdelay,这会耗费大量cpu资源.
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
这三个是内核专门提供该我们用来处理毫秒以上的延迟。上述函数将使得调用它的进程睡眠参数指定的秒数,其中第二个是可以被打断的,其余的两个是不可以的。
2)长延迟:内核中进行延迟最常用的方法就是比较当前的jiffies和目标jiffies(当前的加上时间间隔的jiffies),直到未来的jiffies达到目标jiffies。比如:
unsigned long delay = jiffies + 100; //延迟100个jiffies
while(time_before(jiffies, delay));
与time_before对应的还有一个time_after().其实就是#define time_before(a,b) time_after(b,a);
另外两个是time_after_eq(a,b)和time_before_eq(a,b)
3)睡着延迟:这显然是比忙等待好的方法,因为在未到来之前,进程会处于睡眠状态,把CPU空出来,让CPU可以做别的事情,等时间到了,调用schedule_timeout()就可以唤醒它并重新调度执行。msleep和msleep_interruptible本质上都是依靠包含了schedule_timeout的schedule_timeout_uninterruptible()和schedule_
timeout_interruptible()实现。就像下边这样:
void msleep(unsigned int msecs)
{
}
unsigned long msleep_interruptible(unsigned int msecs)
{
}
signed long __sched schedule_timeout_interruptible()signedlong timeout)
{
}
signed long __sched schedule_timeout_uninterruptible()signedlong timeout)
{
}
另外还有如下:
time_on_timeout(wait_queue_head_t *q, unsigned long timeout);
interruptible_sleep_on_timeout(wait_queue_head_t *q, unsignedlong timeout);
这两个将当前进程添加到等待队列,从而在等待队列上睡眠,当超时发生时,进程将被唤醒。
1)在中断分类中,我们说到了有关向量中断和非向量中断,向量中断就是入口地址不同,进不同的地址做不同的事。那非向量中断则是进同一地址,至于区分就放在了进去后用条件判断,请看下边的模板:
irq_handler()
{
}
2)在底半部机制中,我们讲了tasklet,工作队列和软中断先来看tasklet
tasklet使用模版:
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(XXX_tasklet, xxx_do_tasklet, 0);
void xxx_do_tasklet(unsigned long) //中断处理底半部
{
}
irqreturn_t xxx_interrupt(int irq, void *dev_id, structpt_regs *regs) //中断处理顶半部
{
}
int __init xxx_init(void) //设备驱动模块加载函数
{
}
void __exit xxx_exit(void) //设备驱动卸载模块
{
}
工作队列模版:
void xxx_do_work(unsigned long);
void xxx_do_work(unsigned long) //中断处理底半部
{
}
irqreturn_t xxx_interrupt(int irq, void *dev_id, structpt_regs *regs) //中断处理顶半部
{
}
int xxx_init(void) //设备驱动模块加载函数
{
}
void __exit xxx_exit(void) //设备驱动卸载模块
{
}
3)在上节最后我还给你讲了有关中断共享的东西吧,
irqreturn_t xxx_interrupt(int irq, void *dev_id, structpt_regs *regs) //中断处理顶半部
{
}
int __init xxx_init(void) //设备驱动模块加载函数
{
}
void __exit xxx_exit(void) //设备驱动卸载模块
{
}
共享中断中,我们仔细看一下其实也没什么,不是。就是在和前边中断中要修改一下中断标志,在中断处理中判断一下是否是自己本地的中断,这个我都用红色的标识出来了。
0 0
- Linux内核开发之中断与时钟[转]
- Linux内核开发之中断与时钟(一)
- Linux内核开发之中断与时钟(二)
- Linux内核开发之中断与时钟(三)
- Linux内核开发之中断与时钟(四)
- Linux内核开发之中断与时钟(一)
- Linux内核开发之中断与时钟(二)
- Linux内核开发之中断与时钟(三)
- Linux内核开发之中断与时钟(四)
- Linux内核开发之中断与时钟(三)
- Linux内核开发之中断与时钟(二)
- Linux内核开发之中断与时钟(一)
- Linux内核开发之中断与时钟(一)
- Linux内核开发之中断与时钟(二)
- Linux内核开发之中断与时钟(三)
- Linux内核开发之中断与时钟(四)
- 中断 与 内核时钟
- Linux 内核开发之中断
- Linux基础: 解密module_init幕后…
- version magic '2.6.30.4 mod_unlo…
- linux2.6关于中断的一些入门介绍
- 原子操作(代码)
- 原子操作,信号量
- Linux内核开发之中断与时钟[转]
- 一个ExtJS+jsp+Servlet与数据库连…
- 安装 Nexus——war版本
- 一个简单的extjs+jsp读取数据库信…
- 一个EXTJS与JSP连接把数据写…
- java访问权限修饰符
- eclipse MyEclipse中安装&nbs…
- Ext中文乱码解决方案
- MySQL-5.5.23 在Windows上的…