在此之前的代码,我们都是应用程序主动去read()按键值

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

这章所要涉及的异步通知是一旦设备就绪,则驱动主动通知应用程序,这样应用程序不需要查询设备状态。这一点类似于“中断”

 

 

一、应用程序异步通知signal()

这章的异步通知使用的是UNIX环境高级编程第十章信号。由于是异步通知,因此我们还需要使用fcntl(fd, F_SETFL, oflag | FASYNC)将应用程序的文件描述符设置为异步模式

 

fcntl()函数最终会调用驱动程序中的fasync() -> fasync_helper(),因此在驱动程序中我们只需要调用fasync_helper()函数即可

fasync_helper()函数的作用就是初始化fasync_struct,包括分配内存和设置属性,最后需要在驱动的release()函数里把fasync_helper()初始化的东西free掉

 

应用程序的示例代码如下:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 #include <string.h>
 7 #include <signal.h>
 8 
 9 int fd;
10 
11 static void sig_handler(int signo)
12 {
13     unsigned char key_val;
14     read(fd, &key_val, 1);
15     printf("key_val = 0x%x\n", key_val);
16 }
17 
18 int main(int argc, char** argv)
19 {
20     int oflags = 0;
21     
22     signal(SIGIO, sig_handler);
23     
24     fd = open("/dev/key", O_RDWR);
25     if (fd < 0) {
26         printf("can't open /dev/key\n");
27         return -1;
28     }
29     
30     fcntl(fd, F_SETOWN, getpid());
31     oflags = fcntl(fd, F_GETFL);
32     fcntl(fd, F_SETFL, oflags | FASYNC);
33 
34     while (1) {
35         sleep(1000);
36     }
37     
38     close(fd);
39 
40     return 0;
41 }

 

应用程序完成的有以下一个任务:

1. fcntl(fd, F_SETOWN, getpid())设置文件拥有者为本进程,这样驱动程序发出的信号才能被本进程接到

2. fcntl(fd, F_SETFL, oflags | FASYNC)设置应用程序支持异步通知

3. 通过signal()连接信号和信号处理函数

 

 

二、驱动程序异步通知fasync()

在分析完应用程序之后,我们需要确定驱动程序需要做什么,如下图:

 六、异步通知 随笔

由图,可得出驱动需要做的有以下几点:

1. 设置file->f_owner,这个已经由内核帮我们完成了

2. 编写fasync()函数,里面调用fasync_helper()函数

3. 在资源可获得时,调用kill_fasync()函数释放信号

 

我们现在来看一下fasync()系统调用过程:

SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
  -> do_fcntl(fd, cmd, arg, f.file);
    -> case F_SETFL:
      -> setfl(fd, filp, arg);
        -> filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);    /* 驱动程序的fasync() */
    -> case F_SETOWN:
      -> f_setown(filp, arg, 1);

 

异步通知所使用保存属性的结构体是fasync_struct

fasync()、fasync_helper()和kill_fasync()函数声明如下:

int (*fasync) (int fd, struct file *filp, int mode);

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

void kill_fasync(struct fasync_struct **fp, int sig, int band)

 

驱动程序模版如下:

struct key_device {
    ...
    struct fasync_struct *fasync;
};

static int key_fasync(int fd, struct file *filp, int mode)
{
    struct key_device *dev = filp->private_data;

    // 初始化fasync
    return fasync_helper(fd, filp, mode, &dev->fasync);
}

static struct file_operations key_fops = {
    ...
    .fasync        = key_fasync,
};

static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff)
{
    ...
    schedule();        // 休眠

    // 有数据,释放信号
    kill_fasync(&dev->fasync, SIGIO, POLL_IN);

    mutex_lock(&dev->lock);
    copy_to_user(buf, &key_val, 1);
    mutex_unlock(&dev->lock);
    ...
}

static int key_release(struct inode *nodep, struct file *filp)
{
    key_fasync(-1, filp, 0);    // 将文件从异步通知列表中删除
    ...
}

 

若需要完整驱动程序代码,点击查看:源代码

 

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄