驱动开发之select与中断下半部分:
中断上半部、下半部:
1、已知中断应该尽快完成,但是很多时候不能保证中断代码一定会及时完成。
2、当不能保证中断尽快完成时,内核将曾经的一个中断处理函数分成了两个中断处理函数
其中中断上半部用来处理紧急事件
中断下半部用来处理非紧急事件
3、什么是紧急事件:
直接操作硬件时
对时间要求非常敏感
不能被其他中断打断
4,中断下半部分类:
软中断(需要使用软中断号,但是所有的软中断号都被占用了)
小任务
工作队列
小任务:对软中断的二次封装:
初始化小任务:
1 void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
3 参数1:
4 参数2:中断下半部处理函数
5 参数3:给中断下半部处理函数传参的
调度小任务:
1 tasklet_schedule(struct tasklet_struct *t);
什么时候调用下半部?
在中断上半部处理函数返回前,或者返回后调用。
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/platform_device.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <linux/irqreturn.h> 7 #include <linux/interrupt.h> 8 #include <linux/sched.h> 9 10 int major; 11 12 struct class *cls; 13 struct device *devs; 14 struct resource *res_key2; 15 struct resource *res_key3; 16 17 int key; 18 wait_queue_head_t keyq; 19 int flag = 0; 20 struct timer_list t; 21 int glo_irqno; 22 23 struct tasklet_struct ts; 24 25 irqreturn_t fs4412_key_handler(int irqno,void *id) 26 { 27 glo_irqno = irqno; 28 mod_timer(&t,jiffies + 30); 29 return IRQ_HANDLED; 30 } 31 32 void fs4412_key_timer_handler(unsigned long data) 33 { 34 if(glo_irqno == res_key2->start) 35 key = 2; 36 if(glo_irqno == res_key3->start) 37 key = 3; 38 // wake_up(&keyq); 39 wake_up_interruptible(&keyq); 40 flag = 1; 41 tasklet_schedule(&ts); 42 } 43 44 void fs4412_key_task(unsigned long data) 45 { 46 printk("task schedule "); 47 } 48 49 int fs4412_key_open(struct inode *inode ,struct file *filp) 50 { 51 return 0; 52 } 53 54 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) 55 { 56 int ret; 57 58 // wait_event(keyq,flag != 0); 59 wait_event_interruptible(keyq,flag != 0); 60 ret = copy_to_user(ubuf,&key,sizeof(key)); 61 62 flag = 0; 63 return sizeof(key); 64 } 65 66 struct file_operations fops = { 67 .owner = THIS_MODULE, 68 .open = fs4412_key_open, 69 .read = fs4412_key_read, 70 }; 71 72 int fs4412_key_probe(struct platform_device *pdev) 73 { 74 int ret; 75 printk("match ok "); 76 77 major = register_chrdev(0,"key",&fops); 78 cls = class_create(THIS_MODULE,"key"); 79 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key"); 80 81 res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<> 82 res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1); 83 ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL); 84 ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL); 85 86 init_waitqueue_head(&keyq); 87 88 init_timer(&t); 89 t.function = fs4412_key_timer_handler;//定时器中断处理函数 90 add_timer(&t);//让内核认识定时器中断处理函数 91 92 tasklet_init(&ts,fs4412_key_task,0); 93 return 0; 94 } 95 96 int fs4412_key_remove(struct platform_device *pdev) 97 { 98 del_timer(&t); 99 free_irq(res_key3->start,NULL); 100 free_irq(res_key2->start,NULL); 101 device_destroy(cls,MKDEV(major,0)); 102 class_destroy(cls); 103 unregister_chrdev(major,"key"); 104 return 0; 105 } 106 107 struct of_device_id fs4412_dt_tbl[] = { 108 { 109 .compatible = "fs4412,key", 110 }, 111 {}, 112 }; 113 114 115 struct platform_driver pdrv = { 116 .driver = { 117 .name = "fs4412-key", 118 .of_match_table = fs4412_dt_tbl, 119 }, 120 .probe = fs4412_key_probe, 121 .remove = fs4412_key_remove, 122 }; 123 124 module_platform_driver(pdrv); 125 MODULE_LICENSE("GPL");
工作队列:
1 INIT_WORK(struct work_struct *,void (*)(struct work_struct *work))
2
3 static inline bool schedule_work(struct work_struct *work)
如何选择下半部的使用方法?
小任务运行在中断上下文中(内核空间),对时间要求敏感
工作队列运行在进程上下文中(用户空间和内核空间),对时间不敏感
在工作队列中可以使用等待队列,互斥锁,信号量。小任务不可以。
1 应用层:
2 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set*exceptfds, struct timeval *timeout);
3 参数1:文件描述符最大值+1
4 参数2:读表
5 参数3:写表
6 参数4:异常表
7 参数5:超时检测
8 返回值:出错返回-1,超时返回0,成功返回>0
9 功能:如果没有就绪的文件描述符则阻塞,如果有就绪的文件描述符则唤醒,唤醒的同时会将未就绪的文件描述符清空。
1 FD_SET();将指定文件描述符加入表中
2 FD_ZERO();清空文件描述符表
3 FD_CLR();删除指定的文件描述符
4 FD_ISSET();判断fd是否在表中
1 内核源码分析:
2 struct file_operations
3 {
4 unsigned int (*poll) (struct file *, struct poll_table_struct *);//被select调用
5 }
6
7 vi fs/select.c
8 找到do_select函数
9 mask = (*f_op->poll)(f.file, wait);
10
11 poll_initwait(&table);
12
13 --->void poll_initwait(struct poll_wqueues *pwq)
14 {
15 init_poll_funcptr(&pwq->pt, __pollwait);
16 pwq->polling_task = current;//当前进程
17 }
18
19 --->static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
20 {
21 pt->_qproc = qproc;//将__pollwait赋值给了_qproc
22 }
23
24 如果想要调用__pollwait,需要使用
25 static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
26 {
27 if (p && p->_qproc && wait_address)
28 p->_qproc(filp, wait_address, p);//调用__pollwait
29 }
30
31 --->static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
32 {
33 struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
34 struct poll_table_entry *entry = poll_get_entry(pwq);
35 if (!entry)
36 return;
37 entry->filp = get_file(filp);
38 entry->wait_address = wait_address;
39 entry->key = p->_key;
40 init_waitqueue_func_entry(&entry->wait, pollwake);//唤醒接口
41 entry->wait.private = pwq;
42 add_wait_queue(wait_address, &entry->wait);//将等待队列项添加到等待队列中
43 }
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/platform_device.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <linux/irqreturn.h> 7 #include <linux/interrupt.h> 8 #include <linux/sched.h> 9 10 int major; 11 12 struct class *cls; 13 struct device *devs; 14 struct resource *res_key2; 15 struct resource *res_key3; 16 17 int key; 18 wait_queue_head_t keyq; 19 int flag = 0; 20 struct timer_list t; 21 int glo_irqno; 22 23 struct work_struct ws; 24 25 irqreturn_t fs4412_key_handler(int irqno,void *id) 26 { 27 glo_irqno = irqno; 28 mod_timer(&t,jiffies + 30); 29 return IRQ_HANDLED; 30 } 31 32 void fs4412_key_timer_handler(unsigned long data) 33 { 34 if(glo_irqno == res_key2->start) 35 key = 2; 36 if(glo_irqno == res_key3->start) 37 key = 3; 38 // wake_up(&keyq); 39 wake_up_interruptible(&keyq); 40 flag = 1; 41 schedule_work(&ws); 42 } 43 44 void fs4412_key_work(struct work_struct *work) 45 { 46 printk("work schedule "); 47 } 48 49 int fs4412_key_open(struct inode *inode ,struct file *filp) 50 { 51 return 0; 52 } 53 54 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) 55 { 56 int ret; 57 58 // wait_event(keyq,flag != 0); 59 wait_event_interruptible(keyq,flag != 0); 60 ret = copy_to_user(ubuf,&key,sizeof(key)); 61 62 flag = 0; 63 return sizeof(key); 64 } 65 66 struct file_operations fops = { 67 .owner = THIS_MODULE, 68 .open = fs4412_key_open, 69 .read = fs4412_key_read, 70 }; 71 72 int fs4412_key_probe(struct platform_device *pdev) 73 { 74 int ret; 75 printk("match ok "); 76 77 major = register_chrdev(0,"key",&fops); 78 cls = class_create(THIS_MODULE,"key"); 79 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key"); 80 81 res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<> 82 res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1); 83 ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL); 84 ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL); 85 86 init_waitqueue_head(&keyq); 87 88 init_timer(&t); 89 t.function = fs4412_key_timer_handler;//定时器中断处理函数 90 add_timer(&t);//让内核认识定时器中断处理函数 91 92 INIT_WORK(&ws,fs4412_key_work); 93 return 0; 94 } 95 96 int fs4412_key_remove(struct platform_device *pdev) 97 { 98 del_timer(&t); 99 free_irq(res_key3->start,NULL); 100 free_irq(res_key2->start,NULL); 101 device_destroy(cls,MKDEV(major,0)); 102 class_destroy(cls); 103 unregister_chrdev(major,"key"); 104 return 0; 105 } 106 107 struct of_device_id fs4412_dt_tbl[] = { 108 { 109 .compatible = "fs4412,key", 110 }, 111 {}, 112 }; 113 114 115 struct platform_driver pdrv = { 116 .driver = { 117 .name = "fs4412-key", 118 .of_match_table = fs4412_dt_tbl, 119 }, 120 .probe = fs4412_key_probe, 121 .remove = fs4412_key_remove, 122 }; 123 124 module_platform_driver(pdrv); 125 MODULE_LICENSE("GPL");
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd; 9 10 fd = open("/dev/key",O_RDWR); 11 if(fd == -1) 12 { 13 perror("open"); 14 return -1; 15 } 16 17 int key; 18 while(1) 19 { 20 read(fd,&key,sizeof(key)); 21 printf("key = %d ",key); 22 } 23 return 0; 24 }
异步通知(信号操作):
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<errno.h> 7 #include<stdlib.h> 8 #include<signal.h> 9 int fd; 10 char buf[64] = {0}; 11 12 void func(int sig) 13 { 14 read(fd,buf,sizeof(buf)); 15 printf("%s ",buf); 16 } 17 18 int main(int argc, const char *argv[]) 19 { 20 21 char buf1[64] = {0}; 22 if(mkfifo("./fifo",0664) == -1) 23 { 24 if(errno == EEXIST) 25 { 26 fd = open("./fifo",O_RDONLY); 27 } 28 else 29 { 30 perror("mkfifo"); 31 exit(1); 32 } 33 } 34 else 35 { 36 fd = open("./fifo",O_RDONLY); 37 } 38 39 signal(SIGIO,func); 40 41 int flag; 42 flag = fcntl(fd,F_GETFL); 43 flag = flag | O_ASYNC; 44 fcntl(fd,F_SETFL,flag); 45 fcntl(fd,F_SETOWN,getpid()); 46 while(1) 47 { 48 printf("hello "); 49 sleep(1); 50 } 51 return 0; 52 }
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<errno.h> 7 #include<stdlib.h> 8 #include<string.h> 9 10 int main(int argc, const char *argv[]) 11 { 12 int fd; 13 14 char buf[64] = {0}; 15 if(mkfifo("./fifo",0664) == -1) 16 { 17 if(errno == EEXIST) 18 { 19 fd = open("./fifo",O_WRONLY); 20 } 21 else 22 { 23 perror("mkfifo"); 24 exit(1); 25 } 26 } 27 else 28 { 29 fd = open("./fifo",O_WRONLY); 30 } 31 32 while(1) 33 { 34 fgets(buf,sizeof(buf),stdin); 35 write(fd,buf,strlen(buf) + 1); 36 } 37 return 0; 38 }
1 应用程序:
2 int flag;
3 fd = open("1.txt",O_RDONLY);//一旦打开文件,内核会创建file结构体,其中有一个f_flags成员,此时f_flags = O_RDONLY
4 flag = fcntl(fd,F_GETFL);//从file结构体中获取O_RDONLY保存到flag变量中
5 flag |= O_ASYNC;//将O_RDONLY | O_ASYNC保存到flag变量中(flag是自定义的变量不是内核的)
6 fctnl(fd,F_SETFL,flag);//将O_RDONLY | O_ASYNC保存到file结构体的f_flags成员中,内核会指定如果使用了O_ASYNC属性则默认会使用SIGIO信号
7
8 fcntl(fd,F_SETOWN,getpid());//需要注册信号,但是这个函数只指定了给哪个进程注册,谁在给进程注册信号?内核实现注册功能
1 内核源码分析: 2 vi arch/arm/include/uapi/asm/unistd.h 3 4 #define __NR_fcntl (__NR_SYSCALL_BASE+ 55) 5 6 --->__SYSCALL(__NR_fcntl, sys_fcntl) 7 8 --->err = do_fcntl(fd, cmd, arg, f.file); 9 10 --->case F_GETFL: 11 err = filp->f_flags;对应了flag = fcntl(fd,F_GETFL); 12 break; 13 case F_SETFL: 14 err = setfl(fd, filp, arg);fctnl(fd,F_SETFL,flag) 15 16 ---> if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) { 17 error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); 18 } 19 上层如果执行fctnl(fd,F_SETFL,flag),驱动层可能执行fasync接口,前提是flag |= O_ASYNC;改成flag |= FASYNC;
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/platform_device.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <linux/irqreturn.h> 7 #include <linux/interrupt.h> 8 #include <linux/sched.h> 9 #include <linux/poll.h> 10 11 int major; 12 13 struct class *cls; 14 struct device *devs; 15 struct resource *res_key2; 16 struct resource *res_key3; 17 18 int key; 19 wait_queue_head_t keyq; 20 int flag = 0; 21 struct timer_list t; 22 int glo_irqno; 23 24 struct fasync_struct *fa; 25 26 irqreturn_t fs4412_key_handler(int irqno,void *id) 27 { 28 glo_irqno = irqno; 29 mod_timer(&t,jiffies + 30); 30 return IRQ_HANDLED; 31 } 32 33 void fs4412_key_timer_handler(unsigned long data) 34 { 35 if(glo_irqno == res_key2->start) 36 key = 2; 37 if(glo_irqno == res_key3->start) 38 key = 3; 39 // wake_up(&keyq); 40 wake_up_interruptible(&keyq); 41 flag = 1; 42 43 //注册SIGIO信号 44 kill_fasync(&fa,SIGIO,POLLIN); 45 } 46 47 int fs4412_key_open(struct inode *inode ,struct file *filp) 48 { 49 return 0; 50 } 51 52 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) 53 { 54 int ret; 55 56 // wait_event(keyq,flag != 0); 57 wait_event_interruptible(keyq,flag != 0); 58 ret = copy_to_user(ubuf,&key,sizeof(key)); 59 60 flag = 0; 61 return sizeof(key); 62 } 63 64 int fs4412_key_fasync(int fd,struct file *filp,int on) 65 { 66 fasync_helper(fd,filp,on,&fa);//为了kill_fasync函数做一些成员赋值 67 return 0; 68 } 69 70 struct file_operations fops = { 71 .owner = THIS_MODULE, 72 .open = fs4412_key_open, 73 .read = fs4412_key_read, 74 .fasync = fs4412_key_fasync, 75 }; 76 77 int fs4412_key_probe(struct platform_device *pdev) 78 { 79 int ret; 80 printk("match ok "); 81 82 major = register_chrdev(0,"key",&fops); 83 cls = class_create(THIS_MODULE,"key"); 84 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key"); 85 86 res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<> 87 res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1); 88 ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL); 89 ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL); 90 91 init_waitqueue_head(&keyq); 92 93 init_timer(&t); 94 t.function = fs4412_key_timer_handler;//定时器中断处理函数 95 add_timer(&t);//让内核认识定时器中断处理函数 96 return 0; 97 } 98 99 int fs4412_key_remove(struct platform_device *pdev) 100 { 101 del_timer(&t); 102 free_irq(res_key3->start,NULL); 103 free_irq(res_key2->start,NULL); 104 device_destroy(cls,MKDEV(major,0)); 105 class_destroy(cls); 106 unregister_chrdev(major,"key"); 107 return 0; 108 } 109 110 struct of_device_id fs4412_dt_tbl[] = { 111 { 112 .compatible = "fs4412,key", 113 }, 114 { 115 116 }, 117 }; 118 119 120 struct platform_driver pdrv = { 121 .driver = { 122 .name = "fs4412-key", 123 .of_match_table = fs4412_dt_tbl, 124 }, 125 .probe = fs4412_key_probe, 126 .remove = fs4412_key_remove, 127 }; 128 129 module_platform_driver(pdrv); 130 MODULE_LICENSE("GPL"); 131
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <signal.h> 6 7 int fd; 8 int key; 9 void func(int signo) 10 { 11 if(signo == SIGIO) 12 { 13 read(fd,&key,sizeof(key)); 14 printf("key = %d ",key); 15 } 16 } 17 18 int main(int argc, const char *argv[]) 19 { 20 signal(SIGIO,func); 21 22 fd = open("/dev/key",O_RDWR); 23 if(fd == -1) 24 { 25 perror("open"); 26 return -1; 27 } 28 29 int flag; 30 flag = fcntl(fd,F_GETFL); 31 flag |= FASYNC; 32 fcntl(fd,F_SETFL,flag);//主要功能为了调用驱动中的fasync接口 33 fcntl(fd,F_SETOWN,getpid()); 34 35 while(1) 36 { 37 printf("hello "); 38 sleep(1); 39 } 40 return 0; 41 }
总结:
1 中断上半部和下半部
2 将一个中断处理函数分成了2个函数。
3
4 上半部用来处理紧急事件
5
6 紧急事件:
7 1、直接操作硬件
8 2、对时间敏感
9 3、不能被打断的
10
11 下半部:
12 软中断:
13 open_softirq();
14 raise_softirq();
15 几乎不会使用软中断,内核提供的软中断号都已经被占用了,对应的接口也没有导出符号表。
16
17 小任务:属于一种特殊的软中断,运行在中断上下文,中断处理函数中不能有延时,只能自旋锁。
18 tasklet_init();//初始化小任务
19 tasklet_schedule();//调度小任务
20
21 工作队列:运行在进程上下文,可以有延时,可以使用互斥锁,信号量,等待队列。
22 INIT_WORK();
23 schedule_work();
24
25 什么时候调度中断下半部?
26 一般都是在上半部中断处理函数返回前或者返回后。
27
28 多路复用:
29 int select(文件描述符最大值+1,读表,写表,异常表,超时);
30 功能:如果文件描述符没有就绪则阻塞。
31 如果文件描述符就绪则唤醒,唤醒后会清除未就绪的文件描述符。
32
33 vi arch/arm/include/uapi/asm/unistd.h
34
35
36 异步通知:当一种事件(按键按下)发生时会在应用层产生一个SIGIO信号,进而捕捉SIGIO信号
37
38 应用层:
39
40 signal(SIGIO,func);
41 fd = open();
42
43 flag = fcntl(fd,F_GETFL);//从file结构体的f_flags成员中获取属性存放给flag
44 flag |= FASYNC;
45 fcntl(fd,F_SETFL,flag);//会和FASYNC进行&,如果成功则会执行驱动中的fasync接口。
46 fcntl(fd,F_SETOWN,getpid());//明确给哪个进程注册SIGIO信号(还没有真正注册)。
47
48 while(1)
49 {
50 功能;
51 }
52
53 驱动层:
54 中断处理函数
55 {
56 kill_fasync();
57 //给进程注册信号,但是注册信号时需要使用到一下变量内容,需要准备这些内容
58 }
59
60 fasync()
61 {
62 fasync_helper();//为kill_fasync的功能实现做准备。
63 }