Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1975475
  • 博文数量: 610
  • 博客积分: 11499
  • 博客等级: 上将
  • 技术积分: 5511
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 19:27
文章分类

全部博文(610)

文章存档

2016年(5)

2015年(18)

2014年(12)

2013年(16)

2012年(297)

2011年(45)

2010年(37)

2009年(79)

2008年(101)

分类: LINUX

2008-04-10 23:44:56




设备驱动的异步通知实现

主要实现思路是:设备I/O驱动如果准备好数据(用户可读或者可写),向用户进程发送信号,用户进程收到信号后调用相关的信号处理函数对设备的数据进行访问。

实现上述的思路需要两方面的实现:用户程序的实现、设备驱动程序的实现。

 

用户程序的实现需要:

(1) 设置执行用户程序的PID作为设备文件的所有者。可通过下面语句实现:

fcntl(STDIN_FILENO, F_SETOWN, getpid(  ));

STDIN_FILENO这里是标准输入的文件描述符,我们可以重定向为设备的文件描述符。

F_SETOWN命令表示设置接收SIGIOSIGURG信号的进程ID或者进程组ID

(2) 用户程序必须对访问的设备文件设置FASYNC标志。可通过下面语句实现:

fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);

F_SETFL命令表示设置文件状态标志位(取第3个参数的值)

 

下面的例子来自于LDD3上的asynctest.c

/*

 * asynctest.c: use async notification to read stdin

 *

 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet

 * Copyright (C) 2001 O'Reilly & Associates

 *

 * The source code in this file can be freely used, adapted,

 * and redistributed in source or binary form, so long as an

 * acknowledgment appears in derived source files.  The citation

 * should list that the code comes from the book "Linux Device

 * Drivers" by Alessandro Rubini and Jonathan Corbet, published

 * by O'Reilly & Associates.   No warranty is attached;

 * we cannot take responsibility for errors or fitness for use.

 */

 

#include

#include

#include

#include

#include

#include

 

int gotdata=0; //全局flag,判断signal处理函数有没有执行;

void sighandler(int signo) //signal处理函数

{

    if (signo==SIGIO)

        gotdata++;

    return;

}

 

char buffer[4096]; //全局buffer

 

int main(int argc, char **argv)

{

    int count;

    struct sigaction action;

 

//初始化 action

    memset(&action, 0, sizeof(action));

    action.sa_handler = sighandler;

    action.sa_flags = 0;

 

    sigaction(SIGIO, &action, NULL);//绑定actionSIGIO signal

 

    fcntl(STDIN_FILENO, F_SETOWN, getpid());//设置STDIN_FILENODowner为当前进程

    fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC);//设置STDIN_FILENO的文件状态标志(增加了FASYNC标志)。

    while(1) {

        /* this only returns if a signal arrives */

        sleep(86400); /* one day */

              //SIGIO signal来了,会将当前进程从sleep唤醒

        if (!gotdata)

            continue;

        count=read(0, buffer, 4096);//从标准输入读数据

        /* buggy: if avail data is more than 4kbytes... */

        write(1,buffer,count);//向标准输出写数据

        gotdata=0;

    }

}

 

驱动程序的实现需要:

当用户程序操作时,从内核驱动的角度来看:

(1)    当用户程序调用F_SETOWN命令时(通过fnctl系统调用),所设置的值保存在了驱动程序中的filp->f_owner结构体。

(2)    当用户程序调用F_SETFL命令设置FASYNC标志时,驱动中的fasync方法相应的被调用。fasync方法的实现样例如下:

static int scull_p_fasync(int fd, struct file *filp, int mode)

{

        struct scull_pipe *dev = filp->private_data;

 

        return fasync_helper(fd, filp, mode, &dev->async_queue);//

}

(3)    当设备驱动准备好访问数据后,向所有注册异步通知的进程发送SIGIO信号。它的实现样例如下:

        if (dev->async_queue)

                kill_fasync(&dev->async_queue, SIGIO, POLL_IN);//POLL_IN指设备此时准备好供用户可读的数据;如果要对设备可写,这里应该用POLL_OUT

(4)    当设备文件被关闭时,应当将设备文件从内核维护的活动异步读列表中删掉。它的实现样例如下:

/* remove this filp from the asynchronously notified filp's */

scull_p_fasync(-1, filp, 0);

 

 

 

 

 

参考资料:

1.       LDD3------6.4 section: “asynchronous notification”

2.       Linux设备驱动开发详解“设备驱动的异步通知“一章

3.       APU(2rd)---3.14 section: “fcntl Function”

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