Chinaunix首页 | 论坛 | 博客
  • 博客访问: 344924
  • 博文数量: 92
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 960
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-21 19:38
文章分类

全部博文(92)

文章存档

2010年(71)

2009年(21)

我的朋友

分类: 嵌入式

2010-04-12 21:01:24

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

Fcntl()必须先学习。

int fcntl(int fd, int cmd, ...);

[描述]

            Fcntl()针对(文件)描述符提供控制.参数fd 是被参数cmd操作(如下面的描述)的描述符.

            针对cmd的值,fcntl能够接受第三个参数int arg

 

fcntl函数有5种功能:

1.复制一个现有的描述符(cmd=F_DUPFD.

2.获得/设置文件描述符标记(cmd=F_GETFDF_SETFD).

3.获得/设置文件状态标记(cmd=F_GETFLF_SETFL).

4.获得/设置异步I/O所有权(cmd=F_GETOWNF_SETOWN).

5.获得/设置记录锁(cmd=F_GETLK,F_SETLKF_SETLKW).

cmd值:(我所用到的)

F_GETOWN             取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回成负值(arg被忽略)

    

  

F_SETOWN            设置将接收SIGIOSIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明,否则,arg将被认为是

F_GETFL            取得fd的文件状态标志,如同下面的描述一样(arg被忽略)

    

  

F_SETFL            设置给arg描述符状态标志,可以更改的几个标志是: O_APPEND O_NONBLOCKO_SYNCO_ASYNC

 

 

信号的接收:

为了在用户空间能处理一个设备释放的信号,它必须完成以下3项工作

l         通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。

l         通过F_SETFL IO控制命令设置文件支持FASYNC,即异步通知模式。

l         通过signal()函数连接信号和信号处理函数。

/*======================================================================

    A test program to access /dev/second

    This example is to help understand async IO

   

    The initial developer of the original code is Baohua Song

    . All Rights Reserved.

======================================================================*/

#include

#include

#include

#include

#include

#include

 

/*接收到异步读信号后的动作*/

void input_handler(int signum)

{

  printf("receive a signal from globalfifo,signalnum:%d\n",signum);

}

 

main()

{

  int fd, oflags;

  fd = open("/dev/globalfifo", O_RDWR, S_IRUSR | S_IWUSR);

  if (fd !=  - 1)

  {

    //启动信号驱动机制

    signal(SIGIO, input_handler); //input_handler()处理SIGIO信号

    fcntl(fd, F_SETOWN, getpid());

    oflags = fcntl(fd, F_GETFL);

    fcntl(fd, F_SETFL, oflags | FASYNC);

    while(1)

    {

           sleep(100);

    }

  }

  else

  {

    printf("device open failure\n");

  }

}

 

 

 

信号的释放:

在设备驱动和应用程序的异步通知交互中,仅仅在应用程序端捕获信号时不够的,因为信号没有的源头在设备驱动端。因此,应该在适合的时机让设备驱动释放信号,在设备驱动中增加信号释放的相关代码。

为了使设备支持异步通知机制,驱动程序要做3项工作

l         支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无需处理。

l         支持F_SETEL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该实现fasync()函数。

l         在设备资源可获得时,调用kill_fasync()函数激发响应的信号。

/*======================================================================

    A globalfifo driver as an example of char device drivers 

    This example is to introduce asynchronous notifier

     

    The initial developer of the original code is Baohua Song

    . All Rights Reserved.

======================================================================*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define GLOBALFIFO_SIZE 0x1000    /*全局fifo最大4K字节*/

#define FIFO_CLEAR 0x1  /*0全局内存的长度*/

#define GLOBALFIFO_MAJOR 253    /*预设的globalfifo的主设备号*/

 

static int globalfifo_major = GLOBALFIFO_MAJOR;

/*globalfifo设备结构体*/

struct globalfifo_dev                                    

{                                                        

  struct cdev cdev; /*cdev结构体*/                      

  unsigned int current_len;    /*fifo有效数据长度*/

  unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/       

  struct semaphore sem; /*并发控制用的信号量*/          

  wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/    

  wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/   

  struct fasync_struct *async_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(&dev->sem); //获取信号量

  add_wait_queue(&dev->w_wait, &wait); //进入写等待队列头

 

  /* 等待FIFO非满 */

  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(&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),current_len:%d\n", count, dev

      ->current_len);

 

    wake_up_interruptible(&dev->r_wait); //唤醒读等待队列

    /* 产生异步读信号 */

    if (dev->async_queue)

       kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

   

    ret = count;

  }

 

  out: up(&dev->sem); //释放信号量

  out2:remove_wait_queue(&dev->w_wait, &wait); //从附属的等待队列头移除

  set_current_state(TASK_RUNNING);

  return ret;

}

 

/*文件释放函数*/

int globalfifo_release(struct inode *inode, struct file *filp)

{

       /* 将文件从异步通知列表中删除 */

  globalfifo_fasync( - 1, filp, 0);

 

  return 0;

}

 

 

阅读(1403) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~