10---中断与时钟

轮询、中断和异常比较

  • 轮询:内核定期对设备进行查询做出相应处理
  • 中断:硬件在需要时向内核CPU发出信号
  • 异常:由于编程失误导致错误,需要内核来处理

中断概念

CPU 在执行程序的过程中,出现了某些突发事件时CPU 暂停执行当前的程序,转去处理突发事件,处理完毕后CPU 又返回原程序被中断的位置并继续执行。

中断机制

顶半部和底半部

划分顶半部和底半部优势

中断会随时发生,产生中断的设备会一直等待中断请求被CPU响应,因此中断处理过程要尽可能快。此外,在中断处理过程会禁止其他中断。采用顶半部和底半部的机制可以解决中断响应既快又多的要求。在顶半部,关闭中断,处理限时的硬件操作后就可以打开中断来服务更多的中断请求。底半部在系统不忙的情况下,处理耗时的中断任务,此时,可以被新的中断打断。

申请和释放,屏蔽和使能中断

//申请IRQ,注意该函数可能会睡眠,不可以在中断上下文和不允许阻塞的程序中调用该函数
//函数返回0 表示成功,
//返回-INVAL 表示中断号无效或处理函数指针为NULL,
//返回-EBUSY 表示中断已经被占用且不能共享。
int request_irq(unsigned int irq,//要申请的硬件中断号
                void (*handler)(int irq, void *dev_id, struct pt_regs*regs),//中断处理函数的入口句柄
                unsigned long irqflags,//中断处理标志
                const char * devname,//产生中断设备的ASIIC文本
                void *dev_id);//共享中断

//释放IRQ
void free_irq(unsigned int irq,//要释放的硬件中断号
              void *dev_id);


//屏蔽中断
void disable_irq(int irq);//等待目前的中断处理完成
void disable_irq_nosync(int irq);//立即返回
//使能中断
void enable_irq(int irq);


//屏蔽本CPU的所有中断
void local_irq_save(unsigned long flags);//将目前的中断状态保留在flags并被直接传递,
void local_irq_disable(void);//直接禁止中断

//恢复本CPU的所有中断
void local_irq_restore(unsigned long flags);
void local_irq_enable(void);

中断处理底半部------tasklet工作机制

1 /*定义关联函数xxx_do_tasklet*/
2 void xxx_do_tasklet(unsigned long);
 /*定义一个tasklet结构体xxx_tasklet,与xxx_do_tasklet(data)函数绑定并传入参数0*/
3 DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);             
4
5 /*中断处理底半部*/
6 void xxx_do_tasklet(unsigned long) {
8 ...
9 }
10
11 /*中断处理顶半部*/
12 irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
14 ...
15 tasklet_schedule(&xxx_tasklet);//使系统调度运行xxx_tasklet
16 ...
17 }
18
19 /*设备驱动模块加载函数*/
20 int __init xxx_init(void) {
22 ...
23 /*申请中断*/
24 result = request_irq(xxx_irq, xxx_interrupt,SA_INTERRUPT, "xxx", NULL);
26 ...
27 }
28
29 /*设备驱动模块卸载函数*/
30 void __exit xxx_exit(void){
32 ...
33 /*释放中断*/
34 free_irq(xxx_irq, xxx_interrupt);
35 ...
36 }

中断处理底半部------工作队列

1 /*定义工作队列和关联函数*/
2 struct work_struct xxx_wq;
3 void xxx_do_work(unsigned long);
4
5 /*中断处理底半部*/
6 void xxx_do_work(unsigned long) {
8 ...
9 }
10
11 /*中断处理顶半部*/
12 irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
14 ...
15 schedule_work(&xxx_wq);//调度工作队列执行
16 ...
17 }
18
19 /*设备驱动模块加载函数*/
20 int xxx_init(void) {
22 ...
23 /*申请中断*/
24 result = request_irq(xxx_irq, xxx_interrupt,
25 SA_INTERRUPT, "xxx", NULL);
26 ...
27 /*初始化工作队列并将其与xxx_do_work处理函数绑定*/
28 INIT_WORK(&xxx_wq, (void (*)(void *)) xxx_do_work, NULL);
29 ...
30 }
31
32 /*设备驱动模块卸载函数*/
33 void xxx_exit(void) {
35 ...
36 /*释放中断*/
37 free_irq(xxx_irq, xxx_interrupt);
38 ...
39 }

中断处理底半部------软中断

用软件方式模拟硬件中断的概念,实现宏观上的异步执行效果

在 Linux内核中,用softirq_action结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数。使用open_softirq()函数可以注册软中断对应的处理函数,而raise_softirq()函数可以触发一个软中断。

tasklet,工作队列,软中断比较

  • 软中断和 tasklet仍然运行于中断上下文,软中断和tasklet处理函数中不能睡眠
  • 工作队列则运行于进程上下文,工作队列处理函数中允许睡眠。
  • tasklet也是基于软中断实现的
  • 软中断具有更高的性能

定时器实现方式 

    软件意义上的定时器最终依赖硬件定时器来实现,内核在时钟中断发生后检测各定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行。

timer_list结构体的一个实例

//对应一个定时器
1 struct timer_list {
2 struct list_head entry; //定时器列表
3 unsigned long expires; //定时器到期时间
4 void (*function)(unsigned long); //定时器处理函数
5 unsigned long data; //作为参数被传入定时器处理函数
6 struct timer_base_s *base;
7 };

内核定时器使用模板

1 /*xxx设备结构体*/
2 struct xxx_dev{
4 struct cdev cdev;
5 ...
6 timer_list xxx_timer;/*设备要使用的定时器,定义一个名为XXX_timer 的定时器*/
7 };
8
9 /*xxx驱动中的某函数*/
10 xxx_func1(...) {
12 struct xxx_dev *dev = filp->private_data;
13 ...
14 /*初始化定时器*/
15 init_timer(&dev->xxx_timer);
16 dev->xxx_timer.function = &xxx_do_timer;
17 dev->xxx_timer.data = (unsigned long)dev;
18 /*设备结构体指针作为定时器处理函数参数*/
19 dev->xxx_timer.expires = jiffies + delay;
20 /*添加(注册)定时器到内核动态定时器链表*/
21 add_timer(&dev->xxx_timer);
22 ...
23 }

25 /*xxx驱动中的某函数*/
26 xxx_func2(…) {
28 ...
29 /*删除定时器*/
30 del_timer (&dev->xxx_timer);
31 ...
32 }
33
34 /*定时器处理函数*/
35 static void xxx_do_timer(unsigned long arg) {
37 struct xxx_device *dev = (struct xxx_device *)(arg);
38 ...
39 /*调度定时器再执行*/
40 dev->xxx_timer.expires = jiffies + delay;
41 add_timer(&dev->xxx_timer);
42 ...
43 }

使用内核定时器的second字符设备驱动

1 #include ...
3 #define SECOND_MAJOR 252 /*预设的second的主设备号*/
5 static int second_major = SECOND_MAJOR;
6
7 /*second设备结构体*/
8 struct second_dev{
10 struct cdev cdev; /*cdev结构体*/
11 atomic_t counter;/* 一共经历了多少秒?*/
12 struct timer_list s_timer; /*设备要使用的定时器*/
13 };
14
15 struct second_dev *second_devp; /*设备结构体指针*/
16
17 /*定时器处理函数*/
18 static void second_timer_handle(unsigned long arg) {
20 mod_timer(&second_devp->s_timer,jiffies + HZ);
21 atomic_inc(&second_devp->counter);
23 printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
24 }
25
26 /*文件打开函数*/
27 int second_open(struct inode *inode, struct file *filp) {
29 /*初始化定时器*/
30 init_timer(&second_devp->s_timer);
31 second_devp->s_timer.function = &second_timer_handle;
32 second_devp->s_timer.expires = jiffies + HZ;
34 add_timer(&second_devp->s_timer); /*添加(注册)定时器*/
36 atomic_set(&second_devp->counter,0); //计数清零
38 return 0;
39 }

40 /*文件释放函数*/
41 int second_release(struct inode *inode, struct file *filp){
43 del_timer(&second_devp->s_timer);
45 return 0;
46 }
47
48 /*globalfifo读函数*/
49 static ssize_t second_read(struct file *filp, char _ _user *buf,size_t count, loff_t *ppos){
52 int counter;
54 counter = atomic_read(&second_devp->counter);
55 if(put_user(counter, (int*)buf))
56 return - EFAULT;
57 else
58 return sizeof(unsigned int);
59 }
60
61 /*文件操作结构体*/
62 static const struct file_operations second_fops = {
64                                            .owner = THIS_MODULE,
65                                            .open = second_open,
66                                            .release = second_release,
67                                            .read = second_read,
68                                            };

70 /*初始化并注册cdev*/
71 static void second_setup_cdev(struct second_dev *dev, int index){
73 int err, devno = MKDEV(second_major, index);
75 cdev_init(&dev->cdev, &second_fops);
76 dev->cdev.owner = THIS_MODULE;
77 dev->cdev.ops = &second_fops;
78 err = cdev_add(&dev->cdev, devno, 1);
79 if (err)
80 printk(KERN_NOTICE "Error %d adding LED%d", err, index);
81 }
82
83 /*设备驱动模块加载函数*/
84 int second_init(void){
86 int ret;
87 dev_t devno = MKDEV(second_major, 0);
89 /* 申请设备号*/
90 if (second_major)
91 ret = register_chrdev_region(devno, 1, "second");
92 else{ /* 动态申请设备号*/
94 ret = alloc_chrdev_region(&devno, 0, 1, "second");
95 second_major = MAJOR(devno);
96 }
97 if (ret < 0)
98 return ret;
99 /* 动态申请设备结构体的内存*/
100 second_devp = kmalloc(sizeof(struct second_dev), GFP_KERNEL);
101 if (!second_devp) {/*申请失败*/
103 ret = - ENOMEM;
104 goto fail_malloc;
105 }
107 memset(second_devp, 0, sizeof(struct second_dev));
109 second_setup_cdev(second_devp, 0);
111 return 0;
113 fail_malloc: unregister_chrdev_region(devno, 1);
114 }
115
116 /*模块卸载函数*/
117 void second_exit(void) {
119 cdev_del(&second_devp->cdev); /*注销cdev*/
120 kfree(second_devp); /*释放设备结构体内存*/
121 unregister_chrdev_region(MKDEV(second_major, 0), 1); /*释放设备号*/
122 }

124 MODULE_AUTHOR("Song Baohua");
125 MODULE_LICENSE("Dual BSD/GPL");
127 module_param(second_major, int, S_IRUGO);
128
129 module_init(second_init);
130 module_exit(second_exit);

内核延时

忙等待(空转)---适用于等待时间短,不睡眠的上下文

//短延时
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);

//应用
void delay(unsigned int time){
while (time--);
}

//耗费CPU资源,对于毫秒级以上时延,应用以下函数使得调用它的进程睡眠参数指定的时间
void msleep(unsigned int millisecs);//不能被中断打断
void ssleep(unsigned int seconds);//不能被中断打断
unsigned long msleep_interruptible(unsigned int millisecs);//可以被中断打断


//长延迟
//比较当前的jiffies 和目标jiffies(设置为当前jiffies 加上时间间隔的jiffies),
//直到未来的jiffies 达到目标jiffies

1 /*延迟100个jiffies*/
2 unsigned long delay = jiffies + 100;
3 while (time_before(jiffies, delay));
4
5 /*再延迟2s*/
6 unsigned long delay = jiffies + 2*HZ;
7 while (time_before(jiffies, delay));

睡着延时---在等待的时间到来之前进程处于睡眠状态,CPU资源被其他进程使用

1 void msleep(unsigned int msecs){
3 unsigned long timeout = msecs_to_jiffies(msecs) + 1;
5 while (timeout)
6 timeout = schedule_timeout_uninterruptible(timeout);//先置进程状态为TASK_INTERRUPTIBLE
7 }
8
9 unsigned long msleep_interruptible(unsigned int msecs) {
11 unsigned long timeout = msecs_to_jiffies(msecs) + 1;
13 while (timeout && !signal_pending(current))      
14 timeout = schedule_timeout_interruptible(timeout);//先置进程状态为TASK_UNINTERRUPTIBLE
15 return jiffies_to_msecs(timeout);
16 }

1 signed long _ _sched schedule_timeout_interruptible(signed longtimeout) {
3 _ _set_current_state(TASK_INTERRUPTIBLE);
4 return schedule_timeout(timeout);
5 }
6
7 signed long _ _sched schedule_timeout_uninterruptible(signed longtimeout) {
9 _ _set_current_state(TASK_UNINTERRUPTIBLE);
10 return schedule_timeout(timeout);
11 }


//将当前进程添加到等待队列中,从而在等待队列上睡眠。当超时发生时,进程将被唤醒
sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);
interruptible_sleep_on_timeout(wait_queue_head_t*q, unsigned long timeout);//可在超时前被打断

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值