Linux fasync机制

linux异步通知类似于中断,程序开始做的就是将想监测的文件设置为异步通知,然后程序就去做别的事情了,当这些文件上面有数据可读时候,就发送一个信号,程序接受到这个信号就去处理这个文件的数据。这样程序不再是主动去读文件了,而是以类型于中断的方式,这样程序更加灵活。
要弄清linux异步通知必须要弄明白一下几件事:
1.谁发信号
2.发给谁
3.接受到信号怎么办

一、异步通知的使用

注:以下程序可在TQ2440(kernel 2.6.30.4)上运行

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> int fd; /*信号处理函数

*进程受到规定信号后执行该函数

*回答上面的问题3<接受到信号怎么办>

*/ void my_signal_fun(int signum) { unsigned char key_val; read(fd, &key_val, 1); printf("key_val: 0x%x\n", key_val); } int main(int argc, char **argv) { unsigned char key_val; int ret; int Oflags; /*设置信号处理函数

*设置进程接受到该信号的处理函数

*回答上面的问题3<接受到信号怎么办>

*/ signal(SIGIO, my_signal_fun); /*打开设备*/ fd = open("/dev/buttons_fasync", O_RDWR); if (fd < 0) { printf("can't open!\n"); } /*将filp->owner设置为当前的进程

*filp所指向的文件可读或者可写就会给filp->owner发消息

*回答了上面的问题2<发给谁>

*/ fcntl(fd, F_SETOWN, getpid()); /*获得该设备额标志*/ Oflags = fcntl(fd, F_GETFL); /*设置该文件的标志为FASYNC

*设置文件的标志为FASYNC将导致驱动程序的fasync被调用

*这样文件就开始处于异步通知状态了

*/ fcntl(fd, F_SETFL, Oflags | FASYNC); while (1) { sleep(1); printf("no data!\n"); } return 0; }

二、异步通知驱动实现

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #include <linux/fcntl.h> #define DEVICE_NAME "buttons_fasync" //设备名/dev/buttons_fasync /*等待队列 *当进程主动去读按键的值时,如果没有数据,进程就睡眠 *在进程采取异步去读取按键值时,是按键有数据,驱动就发信号通知进程读 *那么就不存在没有数据可以读的情况* *所以呀,这个等待队列在中是不起作用的 */ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*中断事件标志*/ static volatile int ev_press = 0; /*异步通知结构体

*根据里面的值确定kill_fasync发送的对象

*内核会统一管理struct fasync_struct结构体

*/

static struct fasync_struct *button_async; struct pin_desc{ unsigned int pin; unsigned int key_val; }; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 * 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; /* K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0*/ struct pin_desc pins_desc[4] = { {S3C2410_GPF1, 0x01}, {S3C2410_GPF4, 0x02}, {S3C2410_GPF2, 0x03}, {S3C2410_GPF0, 0x04}, }; /*确定按键值*/ static irqreturn_t buttons_irq(int irq, void *dev_id) { /*获取request_irq注册时,中断号关联的参数*/ struct pin_desc * pindesc = (struct pin_desc *)dev_id; unsigned int pinval; /*读取按键按下的引脚的值*/ pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval) { /*松开*/ key_val = 0x80 | pindesc->key_val; } else { /*按下*/ key_val = pindesc->key_val; } /*表示中断发生了*/ ev_press = 1; /*唤醒因为无按键可读取而休眠的进程*/ wake_up_interruptible(&button_waitq);

 

/* 

*struct fasync_struct { * int magic; * int fa_fd; * struct fasync_struct *fa_next; * struct file *fa_file; * };

* 向和这个文件(设备)关联的进程发送信号 * fasync_helper初始化了button_async里面的信息

* button_async->fa_fd->owner即为发送给谁 * 当有按键按下就进入到驱动程序的中断处理函数中就调用kill_fasync

*/ kill_fasync(&button_async, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED); } /*系统调用open的底层实现*/ static int button_open(struct inode *inode, struct file *file) { /*注册GPF1、GPF4、GPF2、GPF0为中断引脚*/ request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0]); request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]); request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]); request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]); return 0; } /*系统调用read的底层实现*/ ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { /*没有按键按下*/ if(!ev_press) { /*如果是无阻塞读取,那么程序就直接返回*/ if (filp->f_flags & O_NONBLOCK) return -EAGAIN; else /*如果是阻塞读取,那么程序就休眠*/ wait_event_interruptible(button_waitq, ev_press); } /*如果有按键动作, 返回键值 */ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; } /*系统调用close的底层实现*/ int button_close(struct inode *inode, struct file *file) { free_irq(IRQ_EINT1, &pins_desc[0]); free_irq(IRQ_EINT4, &pins_desc[1]); free_irq(IRQ_EINT2, &pins_desc[2]); free_irq(IRQ_EINT0, &pins_desc[3]); return 0; } static int fasync (int fd, struct file *filp, int on) { printk("fasync_helper is calling\n");

/*应用程序的fcntl(fd, F_SETOWN, getpid())告诉了文件和哪个进程关联

*fasync_helper用这个fd初始化button_async

*内核以链表的方式统一管理button_async

*/ return fasync_helper (fd, filp, on, &button_async); } static struct file_operations dev_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = button_open, .read = button_read, .release = button_close, .fasync = fasync, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; static int __init dev_init(void) { int ret; /*杂项设备注册,和register_chrdev差不多 *不同点在于它的主设备号固定为10,并且自动在/dev目录下创建好设备 *注册后次设备号、设备名、设备的fops就都融为一体了 */ ret = misc_register(&misc); printk (DEVICE_NAME" initialized\n"); return ret; } static void __exit dev_exit(void)

{ misc_deregister(&misc); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL");

三、开发板测试过程
在PC机上编译好驱动和测试程序,然后拷贝到TQ2440上

#chmod 777 *                                     (改变文件的属性为可执行)
#insmod buttondrv_fasync.ko               (加载模块)
#ls /dev                                              (可以看到buttons)
#./buttondrvtest
四、程序源代码
http://pan.baidu.com/share/link?shareid=372548&uk=101680913

你可能感兴趣的:(fasync机制)