异步通知:
阻塞与非阻塞访问、poll()函数提供了较好地解决设备访问的机制(应用程序主动访问)
异步通知:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步 I/O”
阻塞 I/O 意味着一直等待设备可访问后再访问,非阻塞 I/O 中使用 poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步 I/O。由此可见,这几种方式 I/O可以互为补充。
阻塞、非阻塞 I/O、异步通知本身没有优劣,应该根据不同的应用场景合理选择。
linux异步通知编程:
1.信号的接受:
在用户程序中,为了捕获信号,可以使用 signal()函数来设置对应信号的处理函数:
void (*signal(int signum, void (*handler))(int)))(int);
该函数原型较难理解,它可以分解为:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理函数,若为SIG_IGN,表示 忽略该信号;若为 SIG_DFL,表示采用系统默认方式处理信号;若为用户自定义的函数,则信号 被捕获到后,该函数将被执行。 如果 signal()调用成功,它返回最后一次为信号 signum 绑定的处理函数 handler 值,失败则返 回 SIG_ERR。
使用信号实现异步通知的应用程序实例:
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #define MAX_LEN 100
8 void input_handler(int num)
9 {
10 char data[MAX_LEN];
11 int len;
12
13 /* 读取并输出 STDIN_FILENO 上的输入 */
14 len = read(STDIN_FILENO, &data, MAX_LEN);
15 data[len] = 0;
16 printf("input available:%s\n", data);
17 }
18
19 main()
20 {
21 int oflags;
22
23 /* 启动信号驱动机制 */
24 signal(SIGIO, input_handler);
25 fcntl(STDIN_FILENO, F_SETOWN, getpid());
26 oflags = fcntl(STDIN_FILENO, F_GETFL);
27 fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
28
29 /* 最后进入一个死循环,仅为保持进程不终止,如果程序中
30 没有这个死循会立即执行完毕 */
31 while (1);
32 }
上述代码 24 行为 SIGIO 信号安装 input_handler()作为处理函数,第 25 行设置本进程为 STDIN_FILENO 文件的拥有者(owner),没有这一步内核不会知道应该将信号发给哪个进程。而 为了启用异步通知机制,还需对设备设置 FASYNC 标志,26~27 行代码实现此目的。
由此可见,为了在用户空间中能处理一个设备释放的信号,它必须完成 3 项工作。 (1)通过 F_SETOWN IO 控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的 信号才能被本进程接收到。 (2)通过 F_SETFL IO 控制命令设置设备文件支持 FASYNC,即异步通知模式。 (3)通过 signal()函数连接信号和信号处理函数。
相关链接:http://blog.csdn.net/bailyzheng/article/details/7463775
信号的释放:
在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号 没有的源头在设备驱动端。因此,应该在合适的时机让设备驱动释放信号,在设备驱动程序中增 加信号释放的相关代码。 为了使设备支持异步通知机制,驱动程序中涉及 3 项工作。
(1)支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID。不 过此项工作已由内核完成,设备驱动无需处理。
(2)支持 F_SETFL 命令的处理,每当FASYNC 标志改变时,驱动程序中的fasync()函数将得 以执行。因此,驱动中应该实现 fasync()函数。 (
3)在设备资源可获得时,调用 kill_fasync()函数激发相应的信号
设备驱动中异步通知编程比较简单,主要用到一项数据结构和两个函数。数据结构是
fasync_struct 结构体,两个函数分别是:
处理 FASYNC 标志变更的。
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
释放信号用的函数。
void kill_fasync(struct fasync_struct **fa, int sig, int band);
相关驱动:
#include <linux/module.h>//support module load and unload
#include <linux/types.h>//special type definition,like dev_t off_t defined by typedef
#include <linux/fs.h>//struct file_operations
#include <linux/errno.h>//return value
#include <linux/mm.h>//memory mannage ,include kmalloc.kfree and so on
#include <linux/sched.h>//process schedule
#include <linux/init.h>//
#include <linux/cdev.h>//char device structure definition
#include <linux/io.h>//io operation function ,like ioremap,iowrite
#include <linux/poll.h> MODULE_AUTHOR("qigaohua") ;
MODULE_LICENSE("Dual BSD/GPL") ; #define GLOBALFIFO_SIZE 0x1000 //1M
#define CLEAR_ALL 0x01 //clear fifo
#define GLOBALFIFO_MAJOR 0//major device No static int globalfifo_major = GLOBALFIFO_MAJOR ;
/*define globalfifo structure*/
struct globalfifo_dev {
struct cdev dev ;
unsigned int current_len ;
unsigned char mem[GLOBALFIFO_SIZE] ;
struct semaphore sem ;
wait_queue_head_t r_wait ;
wait_queue_head_t w_wait ;
struct fasync_struct fasync_queue;
} ;
struct globalfifo_dev *globalfifo_devp = NULL; static int globalfifo_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalfifo_devp ;
return ;
}
static int globalfifo_release(struct inode *inode ,struct file *filp)
{
globalfifo_fasync(-, filp, );
return ;
} static int globalfifo_ioctl(struct inode *inode, struct file *filp,
int cmd, unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data ;
switch (cmd)
{
case CLEAR_ALL:
/*note:down_intterruptible is more suitable,why?*/
down_interruptible(&dev->sem) ;
dev->current_len = ;
memset(dev->mem,,GLOBALFIFO_SIZE) ;
up(&dev->sem) ;
printk(KERN_INFO "clear globalfifo successfully!\n") ;
break ;
default:
printk(KERN_INFO "invalid command!\n") ;
return -EINVAL ;
}
} static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = ;
struct globalfifo_dev *dev = filp->private_data ; down_interruptible(&dev->sem) ; poll_wait(filp,&dev->r_wait,wait) ;
poll_wait(filp,&dev->w_wait,wait) ; if(dev->current_len != )
{
mask |= POLLIN | POLLRDNORM ;
}
if(dev->current_len< GLOBALFIFO_SIZE )
{
mask |= POLLOUT | POLLWRNORM ;
}
up(&dev->sem) ;
return mask ;
}
static ssize_t globalfifo_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int ret ;
struct globalfifo_dev *dev = filp->private_data ;
DECLARE_WAITQUEUE(wait,current) ;
down_interruptible(&dev->sem) ;
add_wait_queue(&dev->r_wait,&wait) ;
if(dev->current_len == )
{
if( filp->f_flags & O_NONBLOCK)
{
ret = -EAGAIN ;
goto out ;
}
__set_current_state(TASK_INTERRUPTIBLE) ;
up(&dev->sem) ;
schedule() ;
if(signal_pending(current))
{
ret = - ERESTARTSYS ;
goto out2 ;
}
down_interruptible(&dev->sem) ;
}
if(count>dev->current_len)
{
count = dev->current_len ;
}
if(copy_to_user(buf,dev->mem,count))
{
ret = -EFAULT ;
goto out ;
}else
{
memcpy(dev->mem,dev->mem+count,dev->current_len-count) ;
dev->current_len -= count ;
printk(KERN_INFO "read %d bytes(s),current_len:%d\n",
count,dev->current_len) ;
wake_up_interruptible(&dev->w_wait) ;
ret = count ;
}
out:
up(&dev->sem) ;
out2:
remove_wait_queue(&dev->w_wait,&wait) ;
set_current_state(TASK_RUNNING) ;
return ret ;
} static int globalfifo_fasync(int fd, struct file *filp, int mode)
{
struct globalfifo_dev *dev = file->private_data;
return fasync_helper(fd, filp, mode, &dev->fasync_queue);
} static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct globalfifo_dev *dev = filp->private_data ;
int ret = - ;
DECLARE_WAITQUEUE(wait,current) ; down_interruptible(&dev->sem) ;
add_wait_queue(&dev->w_wait,&wait) ; if(dev->current_len == GLOBALFIFO_SIZE)
{
if(filp->f_flags & O_NONBLOCK)
{
ret = -EAGAIN ;
goto out ;
}
__set_current_state(TASK_INTERRUPTIBLE) ;
up(&dev->sem) ; schedule() ;
if(signal_pending(current))
{
ret = -ERESTARTSYS ;
goto out2 ;
}
down_interruptible(&dev->sem) ;
} if(count>GLOBALFIFO_SIZE-dev->current_len)
{
count = GLOBALFIFO_SIZE - dev->current_len ;
}
if(copy_from_user(dev->mem+dev->current_len,buf,count))
{
ret = - EFAULT ;
goto out ;
}else
{
dev->current_len += count ;
printk(KERN_INFO "written %d bytes(s),cuurent_len:%d",
count , dev->current_len ) ;
wake_up_interruptible(&dev->r_wait) ;
if(dev->fasync_queue)
kill_fasync(&dev->fasync_queue, SIGIO, POLLIN);
ret = count ;
}
out:
up(&dev->sem) ;
out2:
remove_wait_queue(&dev->w_wait,&wait) ;
set_current_state(TASK_RUNNING) ;
return ret ;
}
static const struct file_operations globalfifo_fops = {
.owner = THIS_MODULE ,
.open = globalfifo_open ,
.read = globalfifo_read ,
.write = globalfifo_write ,
.ioctl = globalfifo_ioctl ,
.poll = globalfifo_poll ,
.fasync = globalfifo_fasync,
.release = globalfifo_release ,
} ;
int __init globalfifo_init(void)
{
int ret ;
dev_t devno = MKDEV(globalfifo_major, ) ;
if(globalfifo_major)
{
ret = register_chrdev_region(devno,,"globalfifo") ;
}else
{
ret = alloc_chrdev_region(&devno,,,"globalfifo") ;
globalfifo_major = MAJOR(devno) ;
}
if(ret<){
printk(KERN_INFO "apply for device No failed! major:%d,minor:%d\n",MAJOR(devno),MINOR(devno)) ;
return ret ;
}
globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL) ;
if(!globalfifo_devp){
ret = - ENOMEM ;
goto fail_malloc ;
} memset(globalfifo_devp, , sizeof(struct globalfifo_dev)) ; cdev_init(&globalfifo_devp->dev, &globalfifo_fops) ; globalfifo_devp->dev.owner = THIS_MODULE ;
globalfifo_devp->dev.ops = &globalfifo_fops ;
if(cdev_add(&globalfifo_devp->dev, devno,)) {
printk(KERN_NOTICE "add cdev failed!\n") ;
} INIT_MUTEX(&globalfifo_devp->sem) ;
init_waitqueue_head(&globalfifo_devp->r_wait) ;
init_waitqueue_head(&globalfifo_devp->w_wait) ;
printk(KERN_INFO "init globalfifo device successfully!\n" ) ;
return ;
fail_malloc:
unregister_chrdev_region(devno,) ;
return ret ;
}
void __exit globalfifo_exit(void)
{
if(globalfifo_devp != NULL ){
cdev_del(&globalfifo_devp->dev) ;
kfree(globalfifo_devp) ;
unregister_chrdev_region(MKDEV(globalfifo_major,),) ;
}
printk(KERN_INFO "unload globalfifo device successfully!\n") ;
}
//module_param(globalfifo_major, int , S_IRUGO) ; module_init(globalfifo_init) ;
module_exit(globalfifo_exit) ;
来自《Linux设备驱动开发详解》