本文将主要就中断的上下部机制,阻塞IO和非阻塞IO,异步通知这几个点列出一些实验代码,对照代码的注释,可以帮助我们更好的理解这些概念和知识。本文所贴的代码全部经过上板子编译运行,各位看官可放心食用(>.<)。
中断上下部机制:一般中断上半部只是对中断进行登记,将工作推迟到下半部中去执行,中断上半部要求时间尽量快,不可引起阻塞和休眠,也不可以嵌套中断。在中断上半部发起对下半部的调度,下半部会在合适的时机执行(与内核的调度有关),下半部机制有软中断、tasklet和工作队列。
阻塞IO和非阻塞IO、异步通知:主要是为了解决一个问题,应用层如何知道我们的按键已经按下?如果应用层采用轮询的方式去读取按键值,那么可能会造成该进程的CPU使用率特别高,为了解决这个问题,需要在按键没有按下时,将进程休眠,按键按下时,再将进程唤醒去读取按键值,这就会运用到阻塞IO和非阻塞IO的相关知识;或者采用异步通知的方式,当驱动检测到按键按下时,主动发送信号给应用进程,应用在信号处理函数中读取按键值。
这个实验我们通过按键中断进入中断上半部,在上半部里调度tasklet,下半部tasklet的任务是打印一行printk(“my_tasklet_func,data is %ldn”,data),将初始化时传递的参数data打印出来,注意下半部tasklet运行在中断上下文中,不能休眠和阻塞。
#include二、按键中断和工作队列#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IRQNAME "key_irq" #define GPIONUM 66 //按键的GPIO号,需要根据实际情况修改 struct module_dev{ struct tasklet_struct my_tasklet; //定义一个tasklet }; struct module_dev mydevice; //下半部tasklet执行的函数,打印初始化时传递的参数data void my_tasklet_func(unsigned long data) { printk("my_tasklet_func,data is %ldn",data); } //上半部服务函数,发起tasklet的调度。 static irqreturn_t irqhanler(int irq, void *dev_id) { printk("key irq catched !!n"); tasklet_schedule(&mydevice.my_tasklet); return IRQ_RETVAL(IRQ_HANDLED); } static int __init moudule_test_init(void) { int ret; int irqnum; unsigned long data=1; printk("moudule init!n"); //tasklet初始化,指定tasklet执行的函数,以及传递给函数的参数为data tasklet_init(&mydevice.my_tasklet, my_tasklet_func, data); //按键的gpio资源申请,设置为输入 gpio_request(GPIONUM,"key"); gpio_direction_input(GPIONUM); //按键的gpio转换为中断号 irqnum=gpio_to_irq(GPIONUM); //中断申请,与中断服务函数关联,设置触发模式上升沿触发,传递设备结构体参数mydevice ret = request_irq(irqnum, irqhanler, IRQF_TRIGGER_RISING, IRQNAME, &mydevice); if(ret<0) { printk("irq request failed!n"); } return 0; } static void __exit moudule_test_exit(void) { printk("moudule exit!n"); gpio_free(GPIONUM); free_irq(gpio_to_irq(GPIONUM), &mydevice); } module_init(moudule_test_init); module_exit(moudule_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Q");
本实验通过按键中断进入中断上半部,在中断上半部里调度延时工作队列,添加到工作队列的工作只是简单的打印printk(“my delay work func here!n”)而已,由于工作队列运行休眠和阻塞,我们的延时工作队列在上半部调度queue_delayed_work的时候,指定了一个延时时间,所以可以观察到按键按下1s后才打印printk(“my delay work func here!n”)。
#include三、阻塞IO和等待队列#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IRQNAME "key_irq" #define GPIONUM 66 struct module_dev{ struct workqueue_struct *task; //定义一个工作队列 struct delayed_work my_work; //定义一个延时工作 }; struct module_dev mydevice; //下半部delay_work执行的函数 void my_work_func_t(struct work_struct *work) { printk("my delay work func here!n"); } //上半部服务函数,发起delay_work的调度,delay_work延时一秒执行 static irqreturn_t irqhanler(int irq, void *dev_id) { printk("key irq catched !!n"); queue_delayed_work(mydevice.task, &mydevice.my_work, msecs_to_jiffies(1000)); return IRQ_RETVAL(IRQ_HANDLED); } static int __init moudule_test_init(void) { int ret; int irqnum; printk("moudule initn"); //创建工作队列task,该函数会为cpu创建内核线程 mydevice.task = create_singlethread_workqueue("test"); if(IS_ERR(mydevice.task)) { printk("Failed to create workqueuen"); mydevice.task = NULL; } //delay_work初始化,指定delay_work要执行的函数 INIT_DELAYED_WORK(&mydevice.my_work, my_work_func_t); gpio_request(GPIONUM,"key"); gpio_direction_input(GPIONUM); irqnum=gpio_to_irq(GPIONUM); //中断申请,与中断服务函数关联,设置触发模式上升沿触发 ret = request_irq(irqnum, irqhanler, IRQF_TRIGGER_RISING, IRQNAME, &mydevice); if(ret<0) { printk("irq request failed!n"); } return 0; } static void __exit moudule_test_exit(void) { printk("moudule exitn"); gpio_free(GPIONUM); free_irq(gpio_to_irq(GPIONUM), &mydevice); } module_init(moudule_test_init); module_exit(moudule_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Q");
本实验应用层通过while循环读取按键值,在按键没有按下的时候,驱动层在file_operations 里面的read函数里将进程添加到等待队列头进行休眠,如果按键按下,在按键中断里使用wake_up()将进程唤醒,唤醒后再去读取并返回按键值给应用层。(CPU占用率低)。
应用层代码:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
int main(int argc, char *argv[])
{
int fd;
int ret = 0;
int data = 0;
char *filename="/dev/TEST_MODULE";
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %srn", filename);
return -1;
}
while (1) {
ret = read(fd, &data, sizeof(data));
if (ret < 0) {
} else {
if (data)
printf("key value = %#Xrn", data);
}
mdelay(100);
}
close(fd);
return ret;
}
驱动层代码:
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_CNT 1 #define DEVICE_NAME "TEST_MODULE" #define IRQNAME "key_irq" #define GPIONUM 66 struct module_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; atomic_t key_flag; wait_queue_head_t r_wait; //定义一个用于读数据的等待队列头 struct device_node *nd; }; struct module_dev mydevice; //上半部中断服务函数,按键按下时,设置key_flag,并wake_up()唤醒进程 static irqreturn_t irqhanler(int irq, void *dev_id) { struct module_dev *dev =&mydevice; printk("key irq catched !!n"); atomic_set(&dev->key_flag,1); wake_up_interruptible(&dev->r_wait); return IRQ_RETVAL(IRQ_HANDLED); } static int module_dev_open(struct inode *inode, struct file *filp) { filp->private_data = &mydevice; return 0; } static ssize_t module_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int ret; struct module_dev *dev = (struct module_dev *)filp->private_data; DECLARE_WAITQUEUE(wait, current); //将当前进程定义为一个等待队列 if(atomic_read(&dev->key_flag) == 0) //读flag,flag==0表示按键未按下,不可读数据,需要将进程阻塞休眠。 { add_wait_queue(&dev->r_wait, &wait); //将等待队列添加到等待队列头 __set_current_state(TASK_INTERRUPTIBLE); //设置任务状态为TASK_INTERRUPTIBLE,可由wake_up()和信号唤醒,TASK_UNINTERRUPTIBLE只能由wake_up()唤醒 schedule(); //通知内核重新调度,进程将休眠;直到调用wake_up()或进程接受到信号,唤醒后将从这里开始执行 if(signal_pending(current)) { //唤醒后首先检查一下是不是信号唤醒的 printk("signal wake upn"); ret = -ERESTARTSYS; goto wait_error; } __set_current_state(TASK_RUNNING); //唤醒后设置当前进程为运行状态 remove_wait_queue(&dev->r_wait, &wait); //唤醒后从等待队列头中移除等待队列,解除阻塞 } //唤醒后读取按键值并返回 if(atomic_read(&dev->key_flag) == 1) { ret = copy_to_user(buf, &dev->key_flag, sizeof(&dev->key_flag)); atomic_set(&dev->key_flag,0); } return 0; wait_error: set_current_state(TASK_RUNNING); remove_wait_queue(&dev->r_wait, &wait); return ret; } static struct file_operations mydevice_fops = { .owner = THIS_MODULE, .open = module_dev_open, .read = module_dev_read, }; static int __init moudule_test_init(void) { int ret; int irqnum; printk("moudule initn"); if (mydevice.major) { mydevice.devid = MKDEV(mydevice.major, 0); register_chrdev_region(mydevice.devid, DEVICE_CNT, DEVICE_NAME); } else { alloc_chrdev_region(&mydevice.devid, 0, DEVICE_CNT, DEVICE_NAME); mydevice.major = MAJOR(mydevice.devid); mydevice.minor = MINOR(mydevice.devid); } cdev_init(&mydevice.cdev, &mydevice_fops); cdev_add(&mydevice.cdev, mydevice.devid, DEVICE_CNT); mydevice.class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(mydevice.class)) { return PTR_ERR(mydevice.class); } mydevice.device = device_create(mydevice.class, NULL, mydevice.devid, NULL, DEVICE_NAME); if (IS_ERR(mydevice.device)) { return PTR_ERR(mydevice.device); } //初始化等待队列头 init_waitqueue_head(&mydevice.r_wait); gpio_request(GPIONUM,"key"); gpio_direction_input(GPIONUM); irqnum=gpio_to_irq(GPIONUM); //中断申请,与中断服务函数关联,设置触发模式上升沿触发 ret = request_irq(irqnum, irqhanler, IRQF_TRIGGER_RISING, IRQNAME, &mydevice); if(ret<0) { printk("irq request failed!n"); } return 0; } static void __exit moudule_test_exit(void) { printk("moudule exitn"); gpio_free(GPIONUM); free_irq(gpio_to_irq(GPIONUM), &mydevice); cdev_del(&mydevice.cdev); unregister_chrdev_region(mydevice.devid, DEVICE_CNT); device_destroy(mydevice.class, mydevice.devid); class_destroy(mydevice.class); } module_init(moudule_test_init); module_exit(moudule_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Q");
驱动层也可以使用wait_event_interruptible
驱动层也可以使用wait_event_interruptible()将进程添加到等待队列头进行休眠,但是唤醒进程的条件除了调用wake_up(),还要满足condition的条件为真。
#include四、非阻塞IO和poll机制(select机制)#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_CNT 1 #define DEVICE_NAME "TEST_MODULE" #define IRQNAME "key_irq" #define GPIONUM 66 struct module_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; atomic_t key_flag; wait_queue_head_t r_wait; //定义一个用于读数据的等待队列头 struct device_node *nd; }; struct module_dev mydevice; //上半部中断服务函数,按键按下时,设置key_flag,并wake_up()唤醒进程 static irqreturn_t irqhanler(int irq, void *dev_id) { struct module_dev *dev =&mydevice; printk("key irq catched !!n"); //这里如果不设置key_flag,光是wake_up()是唤醒不了进程的。 atomic_set(&dev->key_flag,1); wake_up_interruptible(&dev->r_wait); return IRQ_RETVAL(IRQ_HANDLED); } static int module_dev_open(struct inode *inode, struct file *filp) { filp->private_data = &mydevice; return 0; } static ssize_t module_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int ret; struct module_dev *dev = (struct module_dev *)filp->private_data; //加入等待队列,等待被唤醒,唤醒的条件是按键中断里调用了wake_up()并且key_flag==1 ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->key_flag)); if(signal_pending(current)) { //唤醒后首先检查一下是不是信号唤醒的 printk("signal wake upn"); ret = -ERESTARTSYS; return ret; } //唤醒后读取按键值并返回用户层 if(atomic_read(&dev->key_flag) == 1) { ret = copy_to_user(buf, &dev->key_flag, sizeof(&dev->key_flag)); atomic_set(&dev->key_flag,0); } return 0; } static struct file_operations mydevice_fops = { .owner = THIS_MODULE, .open = module_dev_open, .read = module_dev_read, }; static int __init moudule_test_init(void) { int ret; int irqnum; printk("moudule initn"); if (mydevice.major) { mydevice.devid = MKDEV(mydevice.major, 0); register_chrdev_region(mydevice.devid, DEVICE_CNT, DEVICE_NAME); } else { alloc_chrdev_region(&mydevice.devid, 0, DEVICE_CNT, DEVICE_NAME); mydevice.major = MAJOR(mydevice.devid); mydevice.minor = MINOR(mydevice.devid); } cdev_init(&mydevice.cdev, &mydevice_fops); cdev_add(&mydevice.cdev, mydevice.devid, DEVICE_CNT); mydevice.class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(mydevice.class)) { return PTR_ERR(mydevice.class); } mydevice.device = device_create(mydevice.class, NULL, mydevice.devid, NULL, DEVICE_NAME); if (IS_ERR(mydevice.device)) { return PTR_ERR(mydevice.device); } //初始化等待队列头 init_waitqueue_head(&mydevice.r_wait); gpio_request(GPIONUM,"key"); gpio_direction_input(GPIONUM); irqnum=gpio_to_irq(GPIONUM); //中断申请,与中断服务函数关联,设置触发模式上升沿触发 ret = request_irq(irqnum, irqhanler, IRQF_TRIGGER_RISING, IRQNAME, &mydevice); if(ret<0) { printk("irq request failed!n"); } return 0; } static void __exit moudule_test_exit(void) { printk("moudule exitn"); gpio_free(GPIONUM); free_irq(gpio_to_irq(GPIONUM), &mydevice); cdev_del(&mydevice.cdev); unregister_chrdev_region(mydevice.devid, DEVICE_CNT); device_destroy(mydevice.class, mydevice.devid); class_destroy(mydevice.class); } module_init(moudule_test_init); module_exit(moudule_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Q");
select机制和poll机制类似,只不过poll机制能够监测的文件描述符数量更多,这里只介绍poll机制。应用通过非阻塞的方式打开驱动文件fd,并通过poll函数的方式监听文件描述符fd,当监听的文件描述符已就绪,就开始read读取。应用的poll函数会对应执行驱动层的poll函数,驱动中的poll函数会将进程添加到等待队列头进行休眠,一旦满足某个条件conditon(这个条件通过按键中断触发),就返回一个mask通知应用层可以读取了,这个时候应用层就监听到了文件描述符的变化,就可以读了(CPU占用率低)。
应用层代码:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
int main(int argc, char *argv[])
{
int fd;
int ret = 0;
struct pollfd fds;
unsigned char data;
fd = open("/dev/TEST_MODULE", O_RDWR | O_NONBLOCK); //以非阻塞的方式打开文件
if (fd < 0) {
printf("Can't open file %srn", "/dev/TEST_MODULE");
return -1;
}
#if 1
fds.fd = fd;
fds.events = POLLIN;
while (1) {
ret = poll(&fds, 1, 500);
if (ret) { //监听的文件描述符已就绪,这时可读
ret = read(fd, &data, sizeof(data));
if(ret < 0) {
printf("read errn");
} else {
printf("key value = %d rn", data);
}
} else if (ret == 0) {
//自定义超时处理
} else if (ret < 0) {
//自定义错误处理
}
}
close(fd);
return ret;
}
驱动层代码:
#include五、异步通知与信号处理#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_CNT 1 #define DEVICE_NAME "TEST_MODULE" #define IRQNAME "key_irq" #define GPIONUM 66 struct module_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; atomic_t key_flag; wait_queue_head_t r_wait; struct device_node *nd; }; struct module_dev mydevice; //上半部中断服务函数,按键按下时,设置key_flag==1,这个时候poll函数将进程唤醒,并返回文件描述符准备就绪 static irqreturn_t irqhanler(int irq, void *dev_id) { struct module_dev *dev =&mydevice; printk("key irq catched !!n"); //wake_up()注释掉,只需要key_flag==1唤醒 atomic_set(&dev->key_flag,1); //wake_up_interruptible(&dev->r_wait); return IRQ_RETVAL(IRQ_HANDLED); } static int module_dev_open(struct inode *inode, struct file *filp) { filp->private_data = &mydevice; return 0; } static ssize_t module_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int ret; struct module_dev *dev = (struct module_dev *)filp->private_data; if (filp->f_flags & O_NONBLOCK) { //非阻塞访问 if(atomic_read(&dev->key_flag) == 0) //没有按键按下,不可读取,返回-EAGAIN return -EAGAIN; } else { //这条分支是将进程阻塞加入等待队列头的方式,与前面一致 ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->key_flag)); if(signal_pending(current)) { printk("signal wake upn"); ret = -ERESTARTSYS; return ret; } } if(atomic_read(&dev->key_flag) == 1) { ret = copy_to_user(buf, &dev->key_flag, sizeof(&dev->key_flag)); atomic_set(&dev->key_flag,0); } return 0; } unsigned int module_dev_poll(struct file *filp, struct poll_table_struct *wait) { unsigned int mask = 0; struct module_dev *dev = (struct module_dev *)filp->private_data; //添加进程到等待队列头进行休眠 poll_wait(filp, &dev->r_wait, wait); //按键按下时,key_flag==1,满足该条件后return mask,告知应用层准备就绪了,可以读了 if(atomic_read(&dev->key_flag)) { mask = POLLIN | POLLRDNORM; } return mask; } static struct file_operations mydevice_fops = { .owner = THIS_MODULE, .open = module_dev_open, .read = module_dev_read, .poll = module_dev_poll, }; static int __init moudule_test_init(void) { int ret; int irqnum; printk("moudule initn"); if (mydevice.major) { mydevice.devid = MKDEV(mydevice.major, 0); register_chrdev_region(mydevice.devid, DEVICE_CNT, DEVICE_NAME); } else { alloc_chrdev_region(&mydevice.devid, 0, DEVICE_CNT, DEVICE_NAME); mydevice.major = MAJOR(mydevice.devid); mydevice.minor = MINOR(mydevice.devid); } cdev_init(&mydevice.cdev, &mydevice_fops); cdev_add(&mydevice.cdev, mydevice.devid, DEVICE_CNT); mydevice.class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(mydevice.class)) { return PTR_ERR(mydevice.class); } mydevice.device = device_create(mydevice.class, NULL, mydevice.devid, NULL, DEVICE_NAME); if (IS_ERR(mydevice.device)) { return PTR_ERR(mydevice.device); } //初始化等待队列头 init_waitqueue_head(&mydevice.r_wait); gpio_request(GPIONUM,"key"); gpio_direction_input(GPIONUM); irqnum=gpio_to_irq(GPIONUM); //中断申请,与中断服务函数关联,设置触发模式上升沿触发 ret = request_irq(irqnum, irqhanler, IRQF_TRIGGER_RISING, IRQNAME, &mydevice); if(ret<0) { printk("irq request failed!n"); } return 0; } static void __exit moudule_test_exit(void) { printk("moudule exitn"); gpio_free(GPIONUM); free_irq(gpio_to_irq(GPIONUM), &mydevice); cdev_del(&mydevice.cdev); unregister_chrdev_region(mydevice.devid, DEVICE_CNT); device_destroy(mydevice.class, mydevice.devid); class_destroy(mydevice.class); } module_init(moudule_test_init); module_exit(moudule_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Q");
应用层通过signal()去捕捉内核发送的SIGIO信号,并在信号处理函数里对数据进行读取。应用层需要做几件事:1、signal捕捉内核发送的SIGIO信号。2、fcntl(fd, F_SETOWN, getpid())告诉内核哪个进程的pid去接受内核的信号。3、追加驱动设备文件状态标志FASYNC,表示启用异步通知功能,设置此项,驱动程序 file_operations 操作集中的 fasync 函数就会执行。4、创建信号处理函数,在处理函数里读取数据。
驱动层里面fasync函数对应执行,fasync_helper会初始化异步结构体fasync_struct,在按键中断的服务程序里,kill_fasync会发送信号,应用层捕捉到信号后,就进入信号处理函数读取数据。
应用层代码:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"
static int fd = 0;
//信号处理函数里面读取按键值并打印
static void sigio_signal_func(int signum)
{
int err = 0;
unsigned int keyvalue = 0;
err = read(fd, &keyvalue, sizeof(keyvalue));
if(err < 0) {
} else {
printf("sigio signal! key value=%drn", keyvalue);
}
}
int main(int argc, char *argv[])
{
int flags = 0;
fd = open("/dev/TEST_MODULE", O_RDWR);
if (fd < 0) {
printf("Can't open file %srn", "/dev/TEST_MODULE");
return -1;
}
signal(SIGIO, sigio_signal_func); //进程捕捉内核发送的信号为SIGIO,信号处理函数为sigio_signal_func
fcntl(fd, F_SETOWN, getpid()); //getpid获取当前进程的pid号,F_SETOWN表示设置接受SIGIO和SIGURG的pid,发送方为fd。也就是说告诉内核哪个进程接受信号。
flags = fcntl(fd, F_GETFL); //获取打开设备文件的文件状态标志
fcntl(fd, F_SETFL, flags | FASYNC); //添加 FASYNC 状态标志,启用异步通知功能,设置此项,驱动程序 file_operations 操作集中的 fasync 函数就会执行 。
while(1) {
sleep(2); //进程睡眠等待内核信号
}
close(fd);
return 0;
}
驱动层代码:
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_CNT 1 #define DEVICE_NAME "TEST_MODULE" #define IRQNAME "key_irq" #define GPIONUM 66 struct module_dev{ dev_t devid; struct cdev cdev; struct class *class; struct device *device; int major; int minor; atomic_t key_flag; wait_queue_head_t r_wait; struct fasync_struct *async_queue; //异步相关结构体fasync_struct struct device_node *nd; }; struct module_dev mydevice; //上半部中断服务函数,按键按下时,kill_fasync发送信号SIGIO static irqreturn_t irqhanler(int irq, void *dev_id) { struct module_dev *dev =&mydevice; printk("key irq catched !!n"); atomic_set(&dev->key_flag,1); //kill_fasync用于发送指定信号,第三个参数可读时设置为POLL_IN,可写时设置为POLL_OUT。 kill_fasync(&dev->async_queue, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED); } static int module_dev_open(struct inode *inode, struct file *filp) { filp->private_data = &mydevice; return 0; } static ssize_t module_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int ret; struct module_dev *dev = (struct module_dev *)filp->private_data; if (filp->f_flags & O_NONBLOCK) { //非阻塞访问 if(atomic_read(&dev->key_flag) == 0) //没有按键按下,不可读取,返回-EAGAIN return -EAGAIN; } else { //这条分支是将进程阻塞加入等待队列头的方式,与前面一致 ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->key_flag)); if(signal_pending(current)) { printk("signal wake upn"); ret = -ERESTARTSYS; return ret; } } if(atomic_read(&dev->key_flag) == 1) { ret = copy_to_user(buf, &dev->key_flag, sizeof(&dev->key_flag)); atomic_set(&dev->key_flag,0); } return 0; } unsigned int module_dev_poll(struct file *filp, struct poll_table_struct *wait) { unsigned int mask = 0; struct module_dev *dev = (struct module_dev *)filp->private_data; //添加进程到等待队列头进行休眠 poll_wait(filp, &dev->r_wait, wait); //按键按下时,key_flag==1,满足该条件后return mask,告知应用层准备就绪了,可以读了 if(atomic_read(&dev->key_flag)) { mask = POLLIN | POLLRDNORM; } return mask; } static int module_dev_fasync(int fd, struct file *filp, int on) { struct module_dev *dev = (struct module_dev *)filp->private_data; //fasync_helper 函数来初始化定义的 fasync_struct 结构体指针 return fasync_helper(fd, filp, on, &dev->async_queue); } static int module_dev_release(struct inode *inode, struct file *filp) { return module_dev_fasync(-1, filp, 0); } static struct file_operations mydevice_fops = { .owner = THIS_MODULE, .open = module_dev_open, .read = module_dev_read, .poll = module_dev_poll, .fasync = module_dev_fasync, .release = module_dev_release, }; static int __init moudule_test_init(void) { int ret; int irqnum; printk("moudule initn"); if (mydevice.major) { mydevice.devid = MKDEV(mydevice.major, 0); register_chrdev_region(mydevice.devid, DEVICE_CNT, DEVICE_NAME); } else { alloc_chrdev_region(&mydevice.devid, 0, DEVICE_CNT, DEVICE_NAME); mydevice.major = MAJOR(mydevice.devid); mydevice.minor = MINOR(mydevice.devid); } cdev_init(&mydevice.cdev, &mydevice_fops); cdev_add(&mydevice.cdev, mydevice.devid, DEVICE_CNT); mydevice.class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(mydevice.class)) { return PTR_ERR(mydevice.class); } mydevice.device = device_create(mydevice.class, NULL, mydevice.devid, NULL, DEVICE_NAME); if (IS_ERR(mydevice.device)) { return PTR_ERR(mydevice.device); } init_waitqueue_head(&mydevice.r_wait); gpio_request(GPIONUM,"key"); gpio_direction_input(GPIONUM); irqnum=gpio_to_irq(GPIONUM); //中断申请,与中断服务函数关联,设置触发模式上升沿触发 ret = request_irq(irqnum, irqhanler, IRQF_TRIGGER_RISING, IRQNAME, &mydevice); if(ret<0) { printk("irq request failed!n"); } return 0; } static void __exit moudule_test_exit(void) { printk("moudule exitn"); gpio_free(GPIONUM); free_irq(gpio_to_irq(GPIONUM), &mydevice); cdev_del(&mydevice.cdev); unregister_chrdev_region(mydevice.devid, DEVICE_CNT); device_destroy(mydevice.class, mydevice.devid); class_destroy(mydevice.class); } module_init(moudule_test_init); module_exit(moudule_test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Q");



