“小王,听说过锦上添花吧..”我拍拍下王的头说。

“还锦上添花你,为你上次提的几个东东,我是头上长包..”小王气愤地瞪着我。

“啊,为啥这样呢,本来还特意拒绝了MM的月份,抽出时间打算给你说点高级的东东,看来现在是不行了”我吃惊道,“这样吧,这次就给你讲些和前边有关的东西,也不失为锦上添花不是?”。

“好,我也是这么打算的,就是没好意思说,今天讲些啥呢?”小王暗淡的眼光总算闪了闪。(为啥这么难受呢,好像跟什么会嚎叫的特像,哈哈)

那就言归正传,今天我们讲---Linux设备驱动程序之异步通知与异步I/O.”

小王,前边不是讲了阻塞与非阻塞访问,poll()函数等提供的较好的解决设备访问的机制,那么通过这次有关异步通知整套机制的配合,就更相得益彰,锦上添花了。

啥叫异步通知:很简单,一旦设备准备好,就主动通知应用程序,这种情况下应用程序就不需要查询设备状态,这是不是特像硬件上常提的“中断的概念”。上边比较

准确的说法其实应该叫做“信号驱动的异步I/O”,信号是在软件层次上对中断机制的一种模拟。

“小王,给你一个表现的机会,说说这个和前边的几点不同和差异。。”

“嗯,我的理解是这样的哈,阻塞I/O意味着一直等待设备可访问再访问,非阻塞I/O意味着使用poll()来查询是否可访问,而异步通知则意味着设备通知应用程序自身

可访问。”看着小王聪明的眼睛和清晰的思路,我也忍不住给予一个鼓励的微笑啊。

说的好,我只是想强调一点:上面三种方式,其实本身是没有优劣的,应该根据不同的应用场景合理选择罢了。

说到信号,在应用程序中,为了捕获信号(还捕获呢, 不就是一个处理吗)可以使用signal()函数来设置对应的信号的处理函数。函数原型是

void (*signal(int signo,void (*func)(int))) (int)   这个看起来费劲吧,不光你,我看着也费劲,没关系,给你来个例子:

 void sigterm_handler(int signo)
{
char data[MAX_LEN];
int len;
len=read(STDIN_FILENO, &data,MAX_LEN);
data[len]=0;
printf("Input available:%s\n",data);
exit(0);
}
int main(void)
{
int oflags;
//启动信号驱动机制
signal(SIGIO, sigterm_handler);
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
//建立一个死循环,防止程序结束
while(1);
return 0;
}
 看了这段代码明白啥意思了吧,我也不多少了,咱们继续往下走..为了一个用户在用户空间中能处理一个设备释放的信号,它必须完成一下3份工作:
 1)通过F_SETOWN控制指令设置设备文件的拥有者为本进程,这样从设备驱动中发出的信号才能被本进程收到。
 2)通过F_SETFLIO控制命令设置设备文件支持FASYNC,即异步通知模式。
 3)通过signal()链接信号和信号处理函数。
 当然,如果你了解linux/Unix信号机制的话,你可能会问为啥没说sigaction函数,其实没关系,作用差不多,想知道的话,自己看书Apue的P261.
 有了信号的发送,那么就一定得有信号的释放了:
 在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号是不够的,因为信号没有的源头是在驱动端,因此要在适当的时机让设备驱动释放信号。
 为了使设备支持异步通知机制,驱动程序中涉及三个操作:
 1)支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应的进程ID。不过此项工作已由内核完成,设备驱动无须处理。
 2)支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中fasync()函数将得以进行。因此,驱动程序必须实现fasync()函数。
 3)在设备资源可获得时,调用kill_fasync()函数激发相应的信号。
 驱动程序中上面的三步是和应用程序是一一对应的。如下图:
 

设备驱动中异步通知编程还是比较简单的,主要就是一些数据结构,和两个函数:

数据结构:fasync_struct结构体

函数:1)处理FASYNC标志变更的函数int fasync_helper(int fd, struct file *filp, int mode ,struct fasync_struct **fa);

2) 释放信号用的函数void kill_fasync(struct fasync_struct **fa, int sig, int band);

和其他设备驱动一样,一般将fasync_struct放到设备结构体中。下边是典型模版:

struct xxx_dev
{
struct cdev cdev;
...
struct fasync_struct *async_queue; //异步结构体
}

而在驱动的fasync()函数中,只需要简单的将该参数的3个参数以及fasync_struct结构体指针的指针作为第四个参数传给fasync_helper函数即可.下边是典型模版:

static int xxx_fasync(int fd, struct file *filp, int mode)
{
struct xxx_dev *dev = filp->private_data;
return fasync_helper(fd,filp,mode,&dev->async_queue);
}
一旦设备资源可以获得时,应该调用kill_fasync()释放SIGIO信号,可读时第三个参数设置为POLL_IN,可写时第三个参数设置为POLL_OUT,下边是释放信号的典型模版:
static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_ops)
{
struct xxx_dev *dev = filp->private_data;
....
//产生异步信号
if(dev->async_queue)
{
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
..
}
最后,在文件关闭时,即在设备驱动的release函数中,应调用设备驱动的fasync()函数将文件从异步通知的列表中删除,下边是设备驱动的释放函数的典型模版:
static int xxx_release(struct inode *inode, struct file *filp)
{
struct xxx_dev *dev = filp->private_data;
//将文件从异步通知列表中删除
xxx_fasync(-1,filp,0);
...
return 0;
}

“等等,我知道你明白,你是想向我个例子,我知道你啥意思,没关系,我下次补上 。”没等小王,我立刻给她堵上“你啊,心里几根小九九,我还不知道啊..”

Linux内核开发之异步通知与异步I/O(一)的更多相关文章

  1. arm驱动linux异步通知与异步IO【转】

    转自:http://blog.csdn.net/chinazhangzhong123/article/details/51638793 <[ arm驱动] linux异步通知与 异步IO> ...

  2. Linux设备驱动中的异步通知与异步I/O

    异步通知概念: 异步通知的意识是,一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步IO”,信号是在软件层次 ...

  3. 如何参与Linux内核开发(转)

    本文来源于linux内核代码的Document文件夹下的Hoto文件.Chinese translated version of Documentation/HOWTO If you have any ...

  4. 嵌入式系统Linux内核开发工程师必须掌握的三十道题(转)

    嵌入式系统Linux内核开发工程师必须掌握的三十道题 如果你能正确回答以下问题并理解相关知识点原理,那么你就可以算得上是基本合格的Linux内核开发工程师,试试看! 1) Linux中主要有哪几种内核 ...

  5. 如何参与linux 内核开发

    如果想评论或更新本文的内容,请直接联系原文档的维护者.如果你使用英文 交流有困难的话,也可以向中文版维护者求助.如果本翻译更新不及时或者翻 译存在问题,请联系中文版维护者.   英文版维护者: Gre ...

  6. 如何参与linux内核开发

    如何参与linux 内核开发   如果想评论或更新本文的内容,请直接联系原文档的维护者.如果你使用英文 交流有困难的话,也可以向中文版维护者求助.如果本翻译更新不及时或者翻 译存在问题,请联系中文版维 ...

  7. 嵌入式Linux内核开发工程师必须掌握的三十道题

    如果你能正确回答以下问题并理解相关知识点原理,那么你就可以算得上是基本合格的Linux内核开发工程师. 1. Linux中主要有哪几种内核锁?(进程同步与互斥) (1)自旋锁:非睡眠锁 (2)信号量: ...

  8. Linux内核开发进阶书籍推荐(不适合初学者)

    Linux内核开发进阶书籍推荐(不适合初学者) 很早之前就想写一篇文章总结一下Linux Kernel开发的相关资料,项目的原因,再加上家里的一些事情,一直没能找到闲暇,今天终于有些时间,希望可以完成 ...

  9. Linux内核开发

    Linux内核开发 https://www.cnblogs.com/pugang/p/9728983.html 很早之前就想写一篇文章总结一下Linux Kernel开发的相关资料,项目的原因,再加上 ...

随机推荐

  1. c# -- Form1_Load()不被执行的三个解决方法

    我的第一个c#练习程序,果然又出现问题了...在Form1_Load() not work.估计我的人品又出现问题了. 下面实现的功能很简单,就是声明一个label1然后,把它初始化赋值为hello, ...

  2. Octopress + GitHub Page 搭建个人博客

    Tips:博客已搬家,新地址:http://wanxudong.top 首先说明两个关键术语: Octopress Octopress是基于 Jekyll 的博客框架.他们的关系就像 jQuery 与 ...

  3. gridview 空数据绑定

    private void getDataBind() { DataTable dt = new DataTable(); dt.Columns.Add("id"); dt.Colu ...

  4. Delphi DLL制作和加载 Static, Dynamic, Delayed 以及 Shared-Memory Manager

    一 Dll的制作一般分为以下几步:1 在一个DLL工程里写一个过程或函数2 写一个Exports关键字,在其下写过程的名称.不用写参数和调用后缀.二 参数传递1 参数类型最好与window C++的参 ...

  5. Using an open debug interconnect model to simplify embedded systems design

    Using an open debug interconnect model to simplify embedded systems design Tom Cunningham, Freescale ...

  6. 管理站点IP策略

    修改站点IP策略的代码 using System; using System.Text; using Microsoft.Web.Administration; internal static cla ...

  7. 数据库连接池中是将connection放进threadlocal里的

    我有几点不太明白的,望各位大侠指教下.1.j2ee的应用中,有一个用户请求就会启动一个线程.而如果我们把connection放在Threadlocal里的话,那么我们的程序只需要一个connectio ...

  8. redis cluster 设置密码做集群时gem下client.rb文件修改

    redis节点有设置密码,然后在创建集群的时候没有设置密码的命令 ./redis-trib.rb create --replicas 1 127.0.0.1:6381 127.0.0.1:6382 1 ...

  9. Android 4.4 KitKat升级率已经接近18%(2014-07-09 07:29)

    腾讯数码讯(编 译:张秀梅)按照惯例, 每个月的第一个星期的星期一谷歌都会发布最新一期Android版本分布图.从去年十月末谷歌发布Android 4.4 KitKat以来,截止到目前为止Androi ...

  10. docker和虚拟化技术的区别

    1.docker和虚拟化技术的区别 Docker 扩展了 Linux 容器(Linux Containers),或着说 LXC,通过一个高层次的 API 为进程单独提供了一个轻量级的虚拟环境.Dock ...