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

转自:http://blog.csdn.net/chinazhangzhong123/article/details/51638793

 《[ arm驱动linux异步通知与 异步IO》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动三个

  描述:设备文件IO访问:阻塞与非阻塞io访问,poll函数提供较好的解决设备访问的机制,但是如果有了异步通知整套机制就更加完整了

  一、阻塞 I/O,非阻塞IO,异步I/O

  1、阻塞 I/O :挂起进程一直等待设备可访问后再访问

  2、非阻塞IO:进程进行对设备访问一次,不可访问时,继续执行下一条指令

  3、异步I/O:非常类似于硬件上“中断”的概念(硬件去call软件,内核去call应用程序);信号是在软件层次上对中断机制的一种模拟;

  a)原理:信号是异步的,一个进程不必通过任何操作来等待信号的到达;事实上:进程也不知道信号到底什么时候到达;“一个进程收到一个异步通知信号"与"处理器收到一个中断请求"原理是一样的;

  4、异步I/O通知队列(async_queue):内核通过“内核异步通知的程序 fasync()函数”将设备文件fd描述符加入异步通知队列(内核异步通知的链表)。当fd有I/O操作发生时内核通过kill_fasync()释放(产生) SIGIO 信号,从而达到主动通知注册过SIG_IO信号的应用程序。

  5、异步通知对象:首先它是设备文件,其次要注册过fasync()函的文件;异步通知对象不是不是普通文件(不是随便的/tmp/text.txt),因为普通文件没有在内核中实现fasync()函数和kill_fasync()

  二、异步通讯应用程序部分

  模板一)设备文件的异步通知应用程序

  voidinput_handler(intnum){//信号处理函数

  }

  //打开目标设备

  fd = open("设备文件路径如/dev/xxx", O_RDWR);

  //设置好目标设备的SIGIO信号处理程序;等待内核kill_fasync()释放 SIGIO 信号

  signal(SIGIO,input_handler);

  //使当前进程变成文件的主人,这样才能使文件中的信号发到当前进程

  fcntl(fd, F_SETOWN, getpid());

  //获得当前fd的flag值

  oflags = fcntl(fd, F_GETFL);

  /*设置设备文件描述符号fd的FASYNC异步通知标志,

  即给fd添加异步通知模式,fasync()函数将fd加入异步IO通知队列*/

  fcntl(fd, F_SETFL, oflags | FASYNC);

  图示一、异步通知工作过程图

  实例一)以标准输入输出设备异步通知

  #include <signal.h>

  #include <unistd.h>

  #include <stdio.h>

  #include <fcntl.h>

  #include <signal.h>

  #define MAX_LEN 100

  voidinput_handler(intnum)

  {

  chardata[MAX_LEN];

  intlen;

  len = read(STDIN_FILENO, &data, MAX_LEN);

  data[len] = 0;

  printf("input available :%s\n", data);

  }

  voidsetFdAsync(intfd){

  intoflags;

  //当前进程变成文件的主人

  fcntl(fd, F_SETOWN, getpid());

  //本程序中fd = STDIN_FILENO标准输入设备设备文件描述符号;普通文件内核中没有实现FASYNC,不能使用异步通知

  oflags = fcntl(fd, F_GETFL);//

  //FASYNC在glibc 的fcntl.h文件中可以看到这样的定义 #define FASYNC O_ASYNC

  fcntl(fd, F_SETFL, oflags | FASYNC);

  }

  voidmain(){

  intfd = STDIN_FILENO;//STDIN_FILENO输入输出设备描述符号,一般是键盘

  signal(SIGIO,input_handler);//设置好目标设备的SIGIO信号处理程序;等待内核kill_fasync()释放 SIGIO 信号

  setFdAsync(fd);

  while(1);

  }

  运行结果:

  efgwrfgregr

  input available :efgwrfgregr

  sfsdf

  input available :sfsdf

  //本程序电脑上运行时,由于系统对STDIN_FILENO有特殊保护,while里面的程序运行了两次,进程就被系统挂机休眠,此时cpu消耗为0;

  //但我在arm开发板上的linux2.6内核运行时,while正常,进程不被挂起,估计是没键盘的原因...,也待解

  三、驱动程序部分

  驱动程序:一项数据结构和两个函数

  结构体一)一项数据结构----- fasync_struct结构体

  内核源码一)fasync_struct结构体内核源码

  struct fasync_struct {

  int magic;//启用设备文件镜像,监听文件是否变化(这个说法我猜的)

  int fa_fd;//文件描述符

  struct fasync_struct *fa_next; /* 异步通知单链表 */

  //filp是进程通过PCB中的文件描述符表找到该fd所指向的文件指针;在fopen流操作中使用file结构体指针它的优点是带有I/O缓存

  struct file *fa_file;

  //struct file表示该进程打开的文件,其中有一个owner属性,用来表示打开设备文件的进程

  };

  两个函数

  内核部分函数一)fasync_helper处理设备文件异步通知的标志(O_ASYNC或FASYNC),将fd加入异步通知队列函数

  fasync_helper(int fd, struct file * filp, int on, struct fasync_struct * * fapp);

  内核源码二)fasync_helper内核源码分析

  //第一次因为on = MODE = oflag | FASYNC,on!=0所以执行if (on)对struct fasync_struct **fapp进行初始化,

  //当程序释放设备使用myfasync_drv_fasync(-1, file, 0),就执行goto out释放中断

  int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

  {

  struct fasync_struct *fa, **fp;

  struct fasync_struct *new = NULL;

  int result = 0;

  if (on) {//第一次分配fapp空间

  new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);

  if (!new)

  return -ENOMEM;

  }

  write_lock_irq(&fasync_lock);

  for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {//第一次初始化fapp

  if (fa->fa_file == filp) {

  if(on) {

  fa->fa_fd = fd;

  kmem_cache_free(fasync_cache, new);

  } else {

  *fp = fa->fa_next;

  kmem_cache_free(fasync_cache, fa);

  result = 1;

  }

  goto out;

  }

  }

  if (on) {

  new->magic = FASYNC_MAGIC;

  new->fa_file = filp;

  new->fa_fd = fd;

  new->fa_next = *fapp;

  *fapp = new;

  result = 1;

  }

  out:

  write_unlock_irq(&fasync_lock);

  return result;

  }

  EXPORT_SYMBOL(fasync_helper);

  释放信号函数

  内核部分函数二)kill_fasync(struct fasync_struct * * fp, int sig, int band)

  参数:sig就是我们要发送的信号;band(带宽),一般都是使用POLL_IN,表示设备可读,如果设备可写,使用POLL_OUT

  内核源码三)释放(产生)异步读信号函数

  void __kill_fasync(struct fasync_struct *fa, int sig, int band)

  {

  while (fa) {

  struct fown_struct * fown;

  //如果设备文件镜像不存在如设备文件不存在(被删除或改名)或取消了注册FASYNC;镜像映射失败跳出kill_fasync,不产生信号

  if (fa->magic != FASYNC_MAGIC) {

  printk(KERN_ERR "kill_fasync: bad magic number in "

  "fasync_struct!\n");

  return;

  }

  fown = &fa->fa_file->f_owner;

  /* Don‘t send SIGURG to processes which have not set a

  queued signum: SIGURG has its own default signalling

  mechanism. */

  if (!(sig == SIGURG && fown->signum == 0))

  send_sigio(fown, fa->fa_fd, band);

  fa = fa->fa_next;

  }

  }

  EXPORT_SYMBOL(__kill_fasync);

  模板二)信号的异步通知机制模板

  struct VirtualDisk{

  struct cdev cdev;

  //...其他全局变量....

  struct fasync_struct *async_queue;//异步结构体指针

  };

  /*异步读信号*/

  static int myfasync_drv_fasync(int fd, struct file *file, int mode){

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  //....................

  return fasync_helper(fd, file, mode, &devp->async_queue);

  }

  static ssize_t myfasync_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  //...............

  //产生异步读信号SIGIO

  if(devp->async_queue)kill_fasync(&devp->async_queue, SIGIO, POLL_IN);

  return 0;

  }

  static int myfasync_drv_release(struct inode *inode, struct file *file)

  {

  /*当设备关闭时,需要将fasync_struct从异步队列中删除/*

  myfasync_drv_fasync(-1, file, 0);

  return 0;

  }

  实例二)驱动程序完整实例:

  //“myfasync_drv”,"myfasync_","myfasync_drv"

  #include <linux/module.h>//模块所需的大量符号和函数定义

  #include <linux/kernel.h>

  #include <linux/fs.h>//文件系统相关的函数和头文件

  #include <linux/init.h> //指定初始化和清除函数

  #include <linux/delay.h>

  #include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>

  #include <linux/device.h>

  #include <linux/mm.h>

  //#include <linux/sched.h>//包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明

  #include <asm/uaccess.h>//在内核和用户空间中移动数据的函数

  #include <asm/irq.h>

  #include <asm/io.h>

  #include <asm/arch/regs-gpio.h>

  #include <asm/hardware.h>

  #define VIRTUALDISK_SIZE 0x1000//4k

  #define MEM_CLEAR 0x1

  #define VIRTUALDISK_MAJOR 250

  int VirtualDisk_major = VIRTUALDISK_MAJOR;

  struct fasync_struct *async_queue;//异步结构体指针

  struct VirtualDisk{

  struct cdev cdev;//详细看cdev机制

  unsigned char mem[VIRTUALDISK_SIZE ];

  long count; /*记录设备目前被多少设备打开*/

  };

  static struct class *myfasync_class;

  static struct class_device *myfasync_class_dev;

  struct VirtualDisk *VirtualDiskp;

  static int myfasync_drv_fasync(int fd, struct file *file, int mode){

  printk("myfasync_drv_fasync %d\n", fd);

  return fasync_helper(fd, file, mode, &async_queue);

  }

  static int myfasync_drv_open(struct inode *inode, struct file *file)

  {

  printk("myfasync_drv open\n");

  file->private_data = VirtualDiskp;

  VirtualDiskp->count++; /*增加设备打开次数*/

  return 0;

  }

  static int myfasync_drv_release(struct inode *inode, struct file *file)

  {

  printk("myfasync_drv release\n");

  VirtualDiskp->count--; /*减少设备打开次数*/

  myfasync_drv_fasync(-1, file, 0);//当设备关闭时,需要将fasync_struct从异步队列中删除

  return 0;

  }

  /*seek文件定位函数:seek()函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/

  static loff_t myfasync_drv_llseek(struct file *file, loff_t offset, int origin){

  loff_t ret = 0;/*返回的位置偏移*/

  switch (origin)

  {

  case SEEK_SET: /*相对文件开始位置偏移*/

  if (offset < 0)/*offset不合法*/

  {

  ret = - EINVAL; /*无效的指针*/

  break;

  }

  if ((unsigned int)offset > VIRTUALDISK_SIZE)/*偏移大于设备内存*/

  {

  ret = - EINVAL; /*无效的指针*/

  break;

  }

  file->f_pos = (unsigned int)offset; /*更新文件指针位置*/

  ret = file->f_pos;/*返回的位置偏移*/

  break;

  case SEEK_CUR: /*相对文件当前位置偏移*/

  if ((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于设备内存*/

  {

  ret = - EINVAL;/*无效的指针*/

  break;

  }

  if ((file->f_pos + offset) < 0)/*指针不合法*/

  {

  ret = - EINVAL;/*无效的指针*/

  break;

  }

  file->f_pos += offset;/*更新文件指针位置*/

  ret = file->f_pos;/*返回的位置偏移*/

  break;

  default:

  ret = - EINVAL;/*无效的指针*/

  break;

  }

  return ret;

  }

  /*设备控制函数:ioctl()函数接受的MEM_CLEAR命令,这个命令将全局内存的有效数据长度清零,对于设备不支持的命令,ioctl()函数应该返回-EINVAL*/

  static int myfasync_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){

  struct VirtualDisk *devp = file->private_data;/*获得设备结构体指针*/

  switch (cmd)

  {

  case MEM_CLEAR:/*设备内存清零*/

  memset(devp->mem, 0, VIRTUALDISK_SIZE);

  printk(KERN_INFO "VirtualDisk is set to zero\n");

  break;

  default:

  return - EINVAL;

  }

  return 0;

  }

  /*读函数:读写函数主要是让设备结构体的mem[]数组与用户空间交互数据,并随着访问字节数变更返回用户的文件读写偏移位置*/

  static ssize_t myfasync_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)

  {

  unsigned long p = *ppos; /*记录文件指针偏移位置*/

  unsigned int countt = count;/*记录需要读取的字节数*/

  int ret = 0; /*返回值*/

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  printk("myfasync_drv read\n");

  /*分析和获取有效的读长度*/

  if (p >= VIRTUALDISK_SIZE ) /*要读取的偏移大于设备的内存空间*/

  return 0;/*读取地址错误*/

  if (countt > VIRTUALDISK_SIZE - p)/*要读取的字节大于设备的内存空间*/

  countt = VIRTUALDISK_SIZE - p;/*将要读取的字节数设为剩余的字节数*/

  /*内核空间->用户空间交换数据*/

  if (copy_to_user(buf, (void*)(devp->mem + p), countt))

  {

  ret = - EFAULT;

  }

  else

  {

  *ppos += countt;

  ret = countt;

  printk("read %d bytes(s) is %ld\n", countt, p);

  }

  printk("bytes(s) is %s\n", devp->mem);

  return ret;

  }

  /*

  file 是文件指针,count 是请求的传输数据长度,buff 参数是指向用户空间的缓冲区,这个缓冲区或者保存要写入的数据,或者是一个存放新读入数据的空缓冲区,该地址在内核空间不能直接读写,ppos 是一个指针指向一个"long offset type"对象, 它指出用户正在存取的文件位置. 返回值是一个"signed size type。写的位置相对于文件开头的偏移。

  */

  static ssize_t myfasync_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

  {

  unsigned long p = *ppos; /*记录文件指针偏移位置*/

  int ret = 0; /*返回值*/

  unsigned int countt = count;/*记录需要写入的字节数*/

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  printk("myfasync_drv write\n");

  /*分析和获取有效的写长度*/

  if (p >= VIRTUALDISK_SIZE )/*要写入的偏移大于设备的内存空间*/

  return 0;/*写入地址错误*/

  if (countt > VIRTUALDISK_SIZE - p)/*要写入的字节大于设备的内存空间*/

  countt = VIRTUALDISK_SIZE - p;/*将要写入的字节数设为剩余的字节数*/

  /*用户空间->内核空间*/

  if (copy_from_user(devp->mem + p, buf, countt))

  ret = - EFAULT;

  else

  {

  *ppos += countt;/*增加偏移位置*/

  ret = countt;/*返回实际的写入字节数*/

  printk("written %u bytes(s) from%lu, buffer is %s\n", countt, p, devp->mem);

  }

  if(async_queue){

  kill_fasync(&async_queue, SIGIO, POLL_IN);

  printk("write kill_fasync\n");

  }

  return ret;

  }

  static struct file_operations myfasync_drv_fops = {

  .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

  .open = myfasync_drv_open,

  .read = myfasync_drv_read,

  .write = myfasync_drv_write,

  .release = myfasync_drv_release,

  .llseek = myfasync_drv_llseek,

  .ioctl = myfasync_drv_ioctl,

  .fasync = myfasync_drv_fasync,

  };

  /*将 cdev 结构嵌入一个你自己的设备特定的结构,你应当初始化你已经分配的结构使用以上函数,有一个其他的 struct cdev 成员你需要初始化. 象 file_operations 结构,struct cdev 有一个拥有者成员,应当设置为 THIS_MODULE,一旦 cdev 结构建立, 最后的步骤是把它告诉内核, 调用:

  cdev_add(&dev->cdev, devno, 1);*/

  static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minorIndex){

  int err;

  int devno = MKDEV(VirtualDisk_major, minorIndex);

  cdev_init(&dev->cdev, &myfasync_drv_fops);

  dev->cdev.owner = THIS_MODULE;

  err = cdev_add(&dev->cdev, devno, 1);

  if(err){

  printk("error %d cdev file added\n", err);

  }

  }

  static int myfasync_drv_init(void)

  {

  int result;

  dev_t devno = MKDEV(VirtualDisk_major, 0);

  if(VirtualDisk_major){

  result = register_chrdev_region(devno, 1, "myfasync_drv");

  }else{

  result = alloc_chrdev_region(&devno, 0, 1, "myfasync_drv");

  VirtualDisk_major = MAJOR(devno);

  }

  if(result < 0 ){

  return result;

  }

  VirtualDiskp = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);

  if(!VirtualDiskp){

  result = -ENOMEM;

  goto fail_malloc;

  }

  memset(VirtualDiskp, 0, sizeof(struct VirtualDisk));

  VirtualDisk_setup_cdev(VirtualDiskp, 0);

  myfasync_class = class_create(THIS_MODULE, "myfasync_drv");

  if (IS_ERR(myfasync_class))

  return PTR_ERR(myfasync_class);

  myfasync_class_dev = class_device_create(myfasync_class, NULL, MKDEV(VirtualDisk_major, 0), NULL, "myfasync_drv"); /* /dev/xyz */

  if (IS_ERR(myfasync_class_dev))

  return PTR_ERR(myfasync_class_dev);

  return 0;

  fail_malloc:

  unregister_chrdev_region(devno, 1);

  return result;

  }

  static void myfasync_drv_exit(void)

  {

  cdev_del(&VirtualDiskp->cdev);

  kfree(VirtualDiskp);

  unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);

  class_device_unregister(myfasync_class_dev);

  class_destroy(myfasync_class);

  }

  module_init(myfasync_drv_init);

  module_exit(myfasync_drv_exit);

  MODULE_LICENSE("GPL");

  Makefile

  #myfasync_drv.c

  KERN_DIR = /workspacearm/linux-2.6.2.6

  all:

  make -C $(KERN_DIR) M=`pwd` modules

  cp myfasync_drv.ko /opt/fsmini/

  clean:

  make -C $(KERN_DIR) M=`pwd` modules clean

  rm -rf timerlists.order

  obj-m += myfasync_drv.o

  实例三)驱动程序对应的测试的应用程序部分

  #include <signal.h>

  #include <unistd.h>

  #include <stdio.h>

  #include <fcntl.h>

  #include <signal.h>

  int myfd;

  int lenthe;

  void input_handler(int num)

  {

  char data[80];

  int len;

  lseek(myfd, -lenthe, SEEK_CUR);//移动偏移量到写之前位置

  len = read(myfd, data, lenthe);

  //data[len] = ‘‘;

  printf("myfd = %d, len = %d buffuer input available :%s\n",myfd, len, data);

  }

  void setFdAsync(int fd){

  int oflags;

  //当前进程变成文件的主人

  fcntl(fd, F_SETOWN, getpid());

  //本程序中fd = STDIN_FILENO标准输入设备设备文件描述符号;普通文件内核中没有实现FASYNC,不能使用异步通信

  oflags = fcntl(fd, F_GETFL);//

  //FASYNC在glibc 的fcntl.h文件中可以看到这样的定义 #define FASYNC O_ASYNC

  fcntl(fd, F_SETFL, oflags | FASYNC);

  }

  int main(){

  myfd = open("/dev/myfasync_drv", O_RDWR);//STDIN_FILENO输入输出设备描述符号,一般是键盘

  printf("fd = %d,pid = %d", myfd, getpid());

  signal(SIGIO,input_handler);//设置好目标设备的SIGIO信号处理程序;等待内核kill_fasync()释放 SIGIO 信号

  setFdAsync(myfd);

  printf("before while\n");

  while(1){

  char buffer[80];

  lenthe = read(STDIN_FILENO, buffer, 80);

  write(myfd, buffer, lenthe);

  }

  return 0;

  }

  我的Makefile

  objs := $(patsubst %c, %o, $(shell ls *.c))

  myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc

  mybutton.bin:$(objs)

  $(myarmgcc) -o [email protected] $^

  cp *.bin /opt/fsmini/

  %.o:%.c

  $(myarmgcc) -c -o [email protected] $<

  clean:

  rm -f *.bin *.o

  实验结果

  # insmod myfasync_drv.ko

  # ./mybutton.bin

  myfasync_drv open//对应应用程序myfd = open("/dev/myfasync_drv",调用了内核驱动open函数

  myfasync_drv_fasync 3//对应应用程序fcntl(fd, F_SETFL, oflags | FASYNC);调用了内核驱动的myfasync_drv_fasync()函数

  //

  fd = 3,pid = 793before while//while前的进程信息输出

  hello//键盘输入hello

  myfasync_drv write//调用驱动程序write函数

  written 6 bytes(s) from0, buffer is hello//驱动程序write函数内部输出

  write kill_fasync//内涵write函数中,执行kill_fasync(&async_queue, SIGIO, POLL_IN);释放SIGIO信号

  myfasync_drv read//此时应用程序收到中断,应用程序执行read函数,read对应内核驱动的read

  read 6 bytes(s) is 0//内核驱动read打印输出

  bytes(s) is hello //内核驱动read打印输出

  myfd = 3, len = 6 buffuer input available :hello//应用程序input_handler函数输出驱动的写入值

  //下面是while第二次执行

  it is ok

  myfasync_drv write

  written 9 bytes(s) from6, buffer is hello

  it is ok

  write kill_fasync

  myfasync_drv read

  read 9 bytes(s) is 6

  bytes(s) is hello

  it is ok

  myfd = 3, len = 9 buffuer input available :it is ok

  //按ctrl+c退出程序,会执行myfasync_drv_release中myfasync_drv_fasync(-1, file, 0),释放本进程的异步通知

  myfasync_drv release

  myfasync_drv_fasync -1

  #

  四、异步IO缺陷:当有多个文件发送异步通知信号给一个进程时,进程无法知道是哪个文件发送的信号,这时候“设备文件 ”还是要借助poll机制完成IO;(应用程序中使用select)

 《[ arm驱动linux异步通知与 异步IO》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动三个

  描述:设备文件IO访问:阻塞与非阻塞io访问,poll函数提供较好的解决设备访问的机制,但是如果有了异步通知整套机制就更加完整了

  一、阻塞 I/O,非阻塞IO,异步I/O

  1、阻塞 I/O :挂起进程一直等待设备可访问后再访问

  2、非阻塞IO:进程进行对设备访问一次,不可访问时,继续执行下一条指令

  3、异步I/O:非常类似于硬件上“中断”的概念(硬件去call软件,内核去call应用程序);信号是在软件层次上对中断机制的一种模拟;

  a)原理:信号是异步的,一个进程不必通过任何操作来等待信号的到达;事实上:进程也不知道信号到底什么时候到达;“一个进程收到一个异步通知信号"与"处理器收到一个中断请求"原理是一样的;

  4、异步I/O通知队列(async_queue):内核通过“内核异步通知的程序 fasync()函数”将设备文件fd描述符加入异步通知队列(内核异步通知的链表)。当fd有I/O操作发生时内核通过kill_fasync()释放(产生) SIGIO 信号,从而达到主动通知注册过SIG_IO信号的应用程序。

  5、异步通知对象:首先它是设备文件,其次要注册过fasync()函的文件;异步通知对象不是不是普通文件(不是随便的/tmp/text.txt),因为普通文件没有在内核中实现fasync()函数和kill_fasync()

  二、异步通讯应用程序部分

  模板一)设备文件的异步通知应用程序

  voidinput_handler(intnum){//信号处理函数

  }

  //打开目标设备

  fd = open("设备文件路径如/dev/xxx", O_RDWR);

  //设置好目标设备的SIGIO信号处理程序;等待内核kill_fasync()释放 SIGIO 信号

  signal(SIGIO,input_handler);

  //使当前进程变成文件的主人,这样才能使文件中的信号发到当前进程

  fcntl(fd, F_SETOWN, getpid());

  //获得当前fd的flag值

  oflags = fcntl(fd, F_GETFL);

  /*设置设备文件描述符号fd的FASYNC异步通知标志,

  即给fd添加异步通知模式,fasync()函数将fd加入异步IO通知队列*/

  fcntl(fd, F_SETFL, oflags | FASYNC);

  图示一、异步通知工作过程图

  实例一)以标准输入输出设备异步通知

  #include <signal.h>

  #include <unistd.h>

  #include <stdio.h>

  #include <fcntl.h>

  #include <signal.h>

  #define MAX_LEN 100

  voidinput_handler(intnum)

  {

  chardata[MAX_LEN];

  intlen;

  len = read(STDIN_FILENO, &data, MAX_LEN);

  data[len] = 0;

  printf("input available :%s\n", data);

  }

  voidsetFdAsync(intfd){

  intoflags;

  //当前进程变成文件的主人

  fcntl(fd, F_SETOWN, getpid());

  //本程序中fd = STDIN_FILENO标准输入设备设备文件描述符号;普通文件内核中没有实现FASYNC,不能使用异步通知

  oflags = fcntl(fd, F_GETFL);//

  //FASYNC在glibc 的fcntl.h文件中可以看到这样的定义 #define FASYNC O_ASYNC

  fcntl(fd, F_SETFL, oflags | FASYNC);

  }

  voidmain(){

  intfd = STDIN_FILENO;//STDIN_FILENO输入输出设备描述符号,一般是键盘

  signal(SIGIO,input_handler);//设置好目标设备的SIGIO信号处理程序;等待内核kill_fasync()释放 SIGIO 信号

  setFdAsync(fd);

  while(1);

  }

  运行结果:

  efgwrfgregr

  input available :efgwrfgregr

  sfsdf

  input available :sfsdf

  //本程序电脑上运行时,由于系统对STDIN_FILENO有特殊保护,while里面的程序运行了两次,进程就被系统挂机休眠,此时cpu消耗为0;

  //但我在arm开发板上的linux2.6内核运行时,while正常,进程不被挂起,估计是没键盘的原因...,也待解

  三、驱动程序部分

  驱动程序:一项数据结构和两个函数

  结构体一)一项数据结构----- fasync_struct结构体

  内核源码一)fasync_struct结构体内核源码

  struct fasync_struct {

  int magic;//启用设备文件镜像,监听文件是否变化(这个说法我猜的)

  int fa_fd;//文件描述符

  struct fasync_struct *fa_next; /* 异步通知单链表 */

  //filp是进程通过PCB中的文件描述符表找到该fd所指向的文件指针;在fopen流操作中使用file结构体指针它的优点是带有I/O缓存

  struct file *fa_file;

  //struct file表示该进程打开的文件,其中有一个owner属性,用来表示打开设备文件的进程

  };

  两个函数

  内核部分函数一)fasync_helper处理设备文件异步通知的标志(O_ASYNC或FASYNC),将fd加入异步通知队列函数

  fasync_helper(int fd, struct file * filp, int on, struct fasync_struct * * fapp);

  内核源码二)fasync_helper内核源码分析

  //第一次因为on = MODE = oflag | FASYNC,on!=0所以执行if (on)对struct fasync_struct **fapp进行初始化,

  //当程序释放设备使用myfasync_drv_fasync(-1, file, 0),就执行goto out释放中断

  int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

  {

  struct fasync_struct *fa, **fp;

  struct fasync_struct *new = NULL;

  int result = 0;

  if (on) {//第一次分配fapp空间

  new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);

  if (!new)

  return -ENOMEM;

  }

  write_lock_irq(&fasync_lock);

  for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {//第一次初始化fapp

  if (fa->fa_file == filp) {

  if(on) {

  fa->fa_fd = fd;

  kmem_cache_free(fasync_cache, new);

  } else {

  *fp = fa->fa_next;

  kmem_cache_free(fasync_cache, fa);

  result = 1;

  }

  goto out;

  }

  }

  if (on) {

  new->magic = FASYNC_MAGIC;

  new->fa_file = filp;

  new->fa_fd = fd;

  new->fa_next = *fapp;

  *fapp = new;

  result = 1;

  }

  out:

  write_unlock_irq(&fasync_lock);

  return result;

  }

  EXPORT_SYMBOL(fasync_helper);

  释放信号函数

  内核部分函数二)kill_fasync(struct fasync_struct * * fp, int sig, int band)

  参数:sig就是我们要发送的信号;band(带宽),一般都是使用POLL_IN,表示设备可读,如果设备可写,使用POLL_OUT

  内核源码三)释放(产生)异步读信号函数

  void __kill_fasync(struct fasync_struct *fa, int sig, int band)

  {

  while (fa) {

  struct fown_struct * fown;

  //如果设备文件镜像不存在如设备文件不存在(被删除或改名)或取消了注册FASYNC;镜像映射失败跳出kill_fasync,不产生信号

  if (fa->magic != FASYNC_MAGIC) {

  printk(KERN_ERR "kill_fasync: bad magic number in "

  "fasync_struct!\n");

  return;

  }

  fown = &fa->fa_file->f_owner;

  /* Don‘t send SIGURG to processes which have not set a

  queued signum: SIGURG has its own default signalling

  mechanism. */

  if (!(sig == SIGURG && fown->signum == 0))

  send_sigio(fown, fa->fa_fd, band);

  fa = fa->fa_next;

  }

  }

  EXPORT_SYMBOL(__kill_fasync);

  模板二)信号的异步通知机制模板

  struct VirtualDisk{

  struct cdev cdev;

  //...其他全局变量....

  struct fasync_struct *async_queue;//异步结构体指针

  };

  /*异步读信号*/

  static int myfasync_drv_fasync(int fd, struct file *file, int mode){

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  //....................

  return fasync_helper(fd, file, mode, &devp->async_queue);

  }

  static ssize_t myfasync_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  //...............

  //产生异步读信号SIGIO

  if(devp->async_queue)kill_fasync(&devp->async_queue, SIGIO, POLL_IN);

  return 0;

  }

  static int myfasync_drv_release(struct inode *inode, struct file *file)

  {

  /*当设备关闭时,需要将fasync_struct从异步队列中删除/*

  myfasync_drv_fasync(-1, file, 0);

  return 0;

  }

  实例二)驱动程序完整实例:

  //“myfasync_drv”,"myfasync_","myfasync_drv"

  #include <linux/module.h>//模块所需的大量符号和函数定义

  #include <linux/kernel.h>

  #include <linux/fs.h>//文件系统相关的函数和头文件

  #include <linux/init.h> //指定初始化和清除函数

  #include <linux/delay.h>

  #include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>

  #include <linux/device.h>

  #include <linux/mm.h>

  //#include <linux/sched.h>//包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明

  #include <asm/uaccess.h>//在内核和用户空间中移动数据的函数

  #include <asm/irq.h>

  #include <asm/io.h>

  #include <asm/arch/regs-gpio.h>

  #include <asm/hardware.h>

  #define VIRTUALDISK_SIZE 0x1000//4k

  #define MEM_CLEAR 0x1

  #define VIRTUALDISK_MAJOR 250

  int VirtualDisk_major = VIRTUALDISK_MAJOR;

  struct fasync_struct *async_queue;//异步结构体指针

  struct VirtualDisk{

  struct cdev cdev;//详细看cdev机制

  unsigned char mem[VIRTUALDISK_SIZE ];

  long count; /*记录设备目前被多少设备打开*/

  };

  static struct class *myfasync_class;

  static struct class_device *myfasync_class_dev;

  struct VirtualDisk *VirtualDiskp;

  static int myfasync_drv_fasync(int fd, struct file *file, int mode){

  printk("myfasync_drv_fasync %d\n", fd);

  return fasync_helper(fd, file, mode, &async_queue);

  }

  static int myfasync_drv_open(struct inode *inode, struct file *file)

  {

  printk("myfasync_drv open\n");

  file->private_data = VirtualDiskp;

  VirtualDiskp->count++; /*增加设备打开次数*/

  return 0;

  }

  static int myfasync_drv_release(struct inode *inode, struct file *file)

  {

  printk("myfasync_drv release\n");

  VirtualDiskp->count--; /*减少设备打开次数*/

  myfasync_drv_fasync(-1, file, 0);//当设备关闭时,需要将fasync_struct从异步队列中删除

  return 0;

  }

  /*seek文件定位函数:seek()函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/

  static loff_t myfasync_drv_llseek(struct file *file, loff_t offset, int origin){

  loff_t ret = 0;/*返回的位置偏移*/

  switch (origin)

  {

  case SEEK_SET: /*相对文件开始位置偏移*/

  if (offset < 0)/*offset不合法*/

  {

  ret = - EINVAL; /*无效的指针*/

  break;

  }

  if ((unsigned int)offset > VIRTUALDISK_SIZE)/*偏移大于设备内存*/

  {

  ret = - EINVAL; /*无效的指针*/

  break;

  }

  file->f_pos = (unsigned int)offset; /*更新文件指针位置*/

  ret = file->f_pos;/*返回的位置偏移*/

  break;

  case SEEK_CUR: /*相对文件当前位置偏移*/

  if ((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于设备内存*/

  {

  ret = - EINVAL;/*无效的指针*/

  break;

  }

  if ((file->f_pos + offset) < 0)/*指针不合法*/

  {

  ret = - EINVAL;/*无效的指针*/

  break;

  }

  file->f_pos += offset;/*更新文件指针位置*/

  ret = file->f_pos;/*返回的位置偏移*/

  break;

  default:

  ret = - EINVAL;/*无效的指针*/

  break;

  }

  return ret;

  }

  /*设备控制函数:ioctl()函数接受的MEM_CLEAR命令,这个命令将全局内存的有效数据长度清零,对于设备不支持的命令,ioctl()函数应该返回-EINVAL*/

  static int myfasync_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){

  struct VirtualDisk *devp = file->private_data;/*获得设备结构体指针*/

  switch (cmd)

  {

  case MEM_CLEAR:/*设备内存清零*/

  memset(devp->mem, 0, VIRTUALDISK_SIZE);

  printk(KERN_INFO "VirtualDisk is set to zero\n");

  break;

  default:

  return - EINVAL;

  }

  return 0;

  }

  /*读函数:读写函数主要是让设备结构体的mem[]数组与用户空间交互数据,并随着访问字节数变更返回用户的文件读写偏移位置*/

  static ssize_t myfasync_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)

  {

  unsigned long p = *ppos; /*记录文件指针偏移位置*/

  unsigned int countt = count;/*记录需要读取的字节数*/

  int ret = 0; /*返回值*/

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  printk("myfasync_drv read\n");

  /*分析和获取有效的读长度*/

  if (p >= VIRTUALDISK_SIZE ) /*要读取的偏移大于设备的内存空间*/

  return 0;/*读取地址错误*/

  if (countt > VIRTUALDISK_SIZE - p)/*要读取的字节大于设备的内存空间*/

  countt = VIRTUALDISK_SIZE - p;/*将要读取的字节数设为剩余的字节数*/

  /*内核空间->用户空间交换数据*/

  if (copy_to_user(buf, (void*)(devp->mem + p), countt))

  {

  ret = - EFAULT;

  }

  else

  {

  *ppos += countt;

  ret = countt;

  printk("read %d bytes(s) is %ld\n", countt, p);

  }

  printk("bytes(s) is %s\n", devp->mem);

  return ret;

  }

  /*

  file 是文件指针,count 是请求的传输数据长度,buff 参数是指向用户空间的缓冲区,这个缓冲区或者保存要写入的数据,或者是一个存放新读入数据的空缓冲区,该地址在内核空间不能直接读写,ppos 是一个指针指向一个"long offset type"对象, 它指出用户正在存取的文件位置. 返回值是一个"signed size type。写的位置相对于文件开头的偏移。

  */

  static ssize_t myfasync_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

  {

  unsigned long p = *ppos; /*记录文件指针偏移位置*/

  int ret = 0; /*返回值*/

  unsigned int countt = count;/*记录需要写入的字节数*/

  struct VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/

  printk("myfasync_drv write\n");

  /*分析和获取有效的写长度*/

  if (p >= VIRTUALDISK_SIZE )/*要写入的偏移大于设备的内存空间*/

  return 0;/*写入地址错误*/

  if (countt > VIRTUALDISK_SIZE - p)/*要写入的字节大于设备的内存空间*/

  countt = VIRTUALDISK_SIZE - p;/*将要写入的字节数设为剩余的字节数*/

  /*用户空间->内核空间*/

  if (copy_from_user(devp->mem + p, buf, countt))

  ret = - EFAULT;

  else

  {

  *ppos += countt;/*增加偏移位置*/

  ret = countt;/*返回实际的写入字节数*/

  printk("written %u bytes(s) from%lu, buffer is %s\n", countt, p, devp->mem);

  }

  if(async_queue){

  kill_fasync(&async_queue, SIGIO, POLL_IN);

  printk("write kill_fasync\n");

  }

  return ret;

  }

  static struct file_operations myfasync_drv_fops = {

  .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

  .open = myfasync_drv_open,

  .read = myfasync_drv_read,

  .write = myfasync_drv_write,

  .release = myfasync_drv_release,

  .llseek = myfasync_drv_llseek,

  .ioctl = myfasync_drv_ioctl,

  .fasync = myfasync_drv_fasync,

  };

  /*将 cdev 结构嵌入一个你自己的设备特定的结构,你应当初始化你已经分配的结构使用以上函数,有一个其他的 struct cdev 成员你需要初始化. 象 file_operations 结构,struct cdev 有一个拥有者成员,应当设置为 THIS_MODULE,一旦 cdev 结构建立, 最后的步骤是把它告诉内核, 调用:

  cdev_add(&dev->cdev, devno, 1);*/

  static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minorIndex){

  int err;

  int devno = MKDEV(VirtualDisk_major, minorIndex);

  cdev_init(&dev->cdev, &myfasync_drv_fops);

  dev->cdev.owner = THIS_MODULE;

  err = cdev_add(&dev->cdev, devno, 1);

  if(err){

  printk("error %d cdev file added\n", err);

  }

  }

  static int myfasync_drv_init(void)

  {

  int result;

  dev_t devno = MKDEV(VirtualDisk_major, 0);

  if(VirtualDisk_major){

  result = register_chrdev_region(devno, 1, "myfasync_drv");

  }else{

  result = alloc_chrdev_region(&devno, 0, 1, "myfasync_drv");

  VirtualDisk_major = MAJOR(devno);

  }

  if(result < 0 ){

  return result;

  }

  VirtualDiskp = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);

  if(!VirtualDiskp){

  result = -ENOMEM;

  goto fail_malloc;

  }

  memset(VirtualDiskp, 0, sizeof(struct VirtualDisk));

  VirtualDisk_setup_cdev(VirtualDiskp, 0);

  myfasync_class = class_create(THIS_MODULE, "myfasync_drv");

  if (IS_ERR(myfasync_class))

  return PTR_ERR(myfasync_class);

  myfasync_class_dev = class_device_create(myfasync_class, NULL, MKDEV(VirtualDisk_major, 0), NULL, "myfasync_drv"); /* /dev/xyz */

  if (IS_ERR(myfasync_class_dev))

  return PTR_ERR(myfasync_class_dev);

  return 0;

  fail_malloc:

  unregister_chrdev_region(devno, 1);

  return result;

  }

  static void myfasync_drv_exit(void)

  {

  cdev_del(&VirtualDiskp->cdev);

  kfree(VirtualDiskp);

  unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);

  class_device_unregister(myfasync_class_dev);

  class_destroy(myfasync_class);

  }

  module_init(myfasync_drv_init);

  module_exit(myfasync_drv_exit);

  MODULE_LICENSE("GPL");

  Makefile

  #myfasync_drv.c

  KERN_DIR = /workspacearm/linux-2.6.2.6

  all:

  make -C $(KERN_DIR) M=`pwd` modules

  cp myfasync_drv.ko /opt/fsmini/

  clean:

  make -C $(KERN_DIR) M=`pwd` modules clean

  rm -rf timerlists.order

  obj-m += myfasync_drv.o

  实例三)驱动程序对应的测试的应用程序部分

  #include <signal.h>

  #include <unistd.h>

  #include <stdio.h>

  #include <fcntl.h>

  #include <signal.h>

  int myfd;

  int lenthe;

  void input_handler(int num)

  {

  char data[80];

  int len;

  lseek(myfd, -lenthe, SEEK_CUR);//移动偏移量到写之前位置

  len = read(myfd, data, lenthe);

  //data[len] = ‘‘;

  printf("myfd = %d, len = %d buffuer input available :%s\n",myfd, len, data);

  }

  void setFdAsync(int fd){

  int oflags;

  //当前进程变成文件的主人

  fcntl(fd, F_SETOWN, getpid());

  //本程序中fd = STDIN_FILENO标准输入设备设备文件描述符号;普通文件内核中没有实现FASYNC,不能使用异步通信

  oflags = fcntl(fd, F_GETFL);//

  //FASYNC在glibc 的fcntl.h文件中可以看到这样的定义 #define FASYNC O_ASYNC

  fcntl(fd, F_SETFL, oflags | FASYNC);

  }

  int main(){

  myfd = open("/dev/myfasync_drv", O_RDWR);//STDIN_FILENO输入输出设备描述符号,一般是键盘

  printf("fd = %d,pid = %d", myfd, getpid());

  signal(SIGIO,input_handler);//设置好目标设备的SIGIO信号处理程序;等待内核kill_fasync()释放 SIGIO 信号

  setFdAsync(myfd);

  printf("before while\n");

  while(1){

  char buffer[80];

  lenthe = read(STDIN_FILENO, buffer, 80);

  write(myfd, buffer, lenthe);

  }

  return 0;

  }

  我的Makefile

  objs := $(patsubst %c, %o, $(shell ls *.c))

  myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc

  mybutton.bin:$(objs)

  $(myarmgcc) -o [email protected] $^

  cp *.bin /opt/fsmini/

  %.o:%.c

  $(myarmgcc) -c -o [email protected] $<

  clean:

  rm -f *.bin *.o

  实验结果

  # insmod myfasync_drv.ko

  # ./mybutton.bin

  myfasync_drv open//对应应用程序myfd = open("/dev/myfasync_drv",调用了内核驱动open函数

  myfasync_drv_fasync 3//对应应用程序fcntl(fd, F_SETFL, oflags | FASYNC);调用了内核驱动的myfasync_drv_fasync()函数

  //

  fd = 3,pid = 793before while//while前的进程信息输出

  hello//键盘输入hello

  myfasync_drv write//调用驱动程序write函数

  written 6 bytes(s) from0, buffer is hello//驱动程序write函数内部输出

  write kill_fasync//内涵write函数中,执行kill_fasync(&async_queue, SIGIO, POLL_IN);释放SIGIO信号

  myfasync_drv read//此时应用程序收到中断,应用程序执行read函数,read对应内核驱动的read

  read 6 bytes(s) is 0//内核驱动read打印输出

  bytes(s) is hello //内核驱动read打印输出

  myfd = 3, len = 6 buffuer input available :hello//应用程序input_handler函数输出驱动的写入值

  //下面是while第二次执行

  it is ok

  myfasync_drv write

  written 9 bytes(s) from6, buffer is hello

  it is ok

  write kill_fasync

  myfasync_drv read

  read 9 bytes(s) is 6

  bytes(s) is hello

  it is ok

  myfd = 3, len = 9 buffuer input available :it is ok

  //按ctrl+c退出程序,会执行myfasync_drv_release中myfasync_drv_fasync(-1, file, 0),释放本进程的异步通知

  myfasync_drv release

  myfasync_drv_fasync -1

  #

  四、异步IO缺陷:当有多个文件发送异步通知信号给一个进程时,进程无法知道是哪个文件发送的信号,这时候“设备文件 ”还是要借助poll机制完成IO;(应用程序中使用select)

时间: 2024-11-07 04:48:17

arm驱动linux异步通知与异步IO【转】的相关文章

深入浅出~Linux设备驱动之异步通知和异步I/O

在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问.因此,使用无阻塞I/O的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似“中断”的异步通知所取代.异步通知类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步I/O". 1.异步通知的概念和作用 影响:阻塞--应用程序无需轮询设备是否可以访问 非阻塞--中断进行通知 即:由驱动发起,主动通知应用程序 2.linux异步通知编程 2.1 linux信号 作用:linux系统中,异步通知使用信号来实现

QGC 支付宝无法异步通知及异步通知常见问题注意事项

支付宝异步通知参数(notify_url)路径常见问题注意事项: 1.不能是内网或者局域网地址,必须是外网可以访问的.否则无法调用!! 2.地址不能有session拦截,支付宝主动发送,所有session会失效:cookies.session等在此页面会失效!! 3.必须保证服务器异步通知页面(notify_url)上无任何字符,如空格.HTML标签.开发系统自带抛出的异常提示信息等: 4.程序执行完后必须打印输出“success”(不包含引号).如果商户反馈给支付宝的字符不是success这7

linux设备驱动程序中的阻塞、IO多路复用与异步通知机制

一.阻塞与非阻塞 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经常用到等待队列. 阻塞调用是指调用结果返回之前,当前线程会被挂起,函数只有在得到结果之后才会返回. 非阻塞指不能立刻得到结果之前,该函数不会阻塞当前进程,而会立刻返回. 函数是否处于阻塞模式和驱动对应函数中的实现机制是直接相关的,但并不是一一对应的,例如我们在应用层设置为阻塞模式,如果驱动中没有实现阻塞,函数仍然没有阻塞功能. 二.等待队列 在linux设备驱动程序中,阻塞进程可以使用等待队列来实现. 在内核中,

异步通知

目录 更新记录 1.异步通知概念 2.异步通知机制 2.1 从应用程序的角度考虑 2.2 从驱动程序的角度考虑 2.3 小结 3.案列 参考 更新记录 version status description date author V1.0 C Create Document 2019.1.13 John Wan status: C―― Create, A-- Add, M-- Modify, D-- Delete. 注:内核版本 3.0.15 1.异步通知概念 ??异步通知:一旦设备就绪,则主动

linux设备驱动归纳总结(三):7.异步通知fasync【转】

本文转载自:http://blog.chinaunix.net/uid-25014876-id-62725.html linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 异步通知fasync是应用于系统调用signal和sigaction函数,下面我会使用signal函数.简单的说,signal函数就是让一个信号与与

Linux的fasync驱动异步通知详解

工作项目用有个需求是监测某个GPIO输入方波的频率!通俗的讲就是一个最最简单的测方波频率的示波器!不过只是测方波的频率!频率范围是0~200HZ,而且频率方波不是一直都是200HZ,大多数的时候可能一直是0或者一个更低频率的方波!同时要考虑到方波有可能一直维持在200HZ ,同时保持效率和性能的情况下,fasync驱动异步通知是个不错的选择,当初写demo的时候实测1K的方波完全没有问题!应用到项目中也是完全能满足需求!驱动很简单!业余时间把自己之前学到的知识总结一下!对自己也是个提高! 根据需

《Linux4.0设备驱动开发详解》笔记--第九章:Linux设备驱动中的异步通知与同步I/O

在设备驱动中使用异步通知可以使得对设备的访问可进行时,由驱动主动通知应用程序进行访问.因此,使用无阻塞I/O的应用程序无需轮询设备是否可访问,而阻塞访问也可以被类似"中断"的异步通知所取代.异步通知类似于硬件上的"中断"概念,比较准确的称谓是"信号驱动的异步I/O". 9.1 异步通知的概念和作用 异步通知:一旦设备就绪,则主动通知应用程序,该应用程序无需查询设备状态 几种通知方式比较: 阻塞I/O :一直等待设备可访问后开始访问 非阻塞I/O:

Hasen的linux设备驱动开发学习之旅--异步通知

/** * Author:hasen * 参考 :<linux设备驱动开发详解> * 简介:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:异步通知 * Date:2014-11-05 */ 一.异步通知的概念和作用 阻塞和非阻塞访问.poll()函数提供了较好地解决设备访问的机制,但是如果有了异步通知整套机制就更 加完整了. 异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这 一点非常类似于硬件上"中断"的概

Linux的fasync驱动异步通知详解【转】

本文转载自:http://blog.csdn.net/coding__madman/article/details/51851338 版权声明:本文为博主原创文章,未经博主允许不得转载. 工作项目用有个需求是监测某个GPIO输入方波的频率!通俗的讲就是一个最最简单的测方波频率的示波器!不过只是测方波的频率!频率范围是0~200HZ,而且频率方波不是一直都是200HZ,大多数的时候可能一直是0或者一个更低频率的方波!同时要考虑到方波有可能一直维持在200HZ ,同时保持效率和性能的情况下,fasy