通常应用程序跟驱动之间交互,主要是通过轮询的方式。这种方式效率非常低下。

下面介绍一种异步通知的方式。在这种模式下,应用程序不需要查询驱动,当驱动需要应用程序读取数据时,发送一个信号给应用程序,应用程序收到信号后,再去从驱动读取数据。

驱动代码:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/signal.h> //for SIGIO,POLL_IN
#include <linux/timer.h>
#include <linux/jiffies.h>

//设备名
#define DEVICE_NAME         "sync"
//主设备号
#define DEVICE_MAJOR        228

//异步通知结构变量
struct fasync_struct *async_queue;
//内核定时器
struct timer_list my_timer;


//定时器超时函数
static void timer_function(unsigned long arg)
{
        //发送信号到用户空间,POLL_IN表示可写
        kill_fasync(&async_queue, SIGIO, POLL_IN);
}

//用户空间调用fcntl函数时会调用这个函数
static int async_fasync(int fd, struct file *filp, int mode)
{
            //根据用户空间的需要,获取或设置相应的属性
        return fasync_helper(fd, filp, mode, &async_queue);
}

static int async_open(struct inode *node,struct file *flip)
{
        //初始化定时器
        init_timer(&my_timer);
        my_timer.data = 0;
        //定时器超时函数
        my_timer.function = &timer_function;
        //定时器超时时间(当前时间的后1000个时钟滴答)
        my_timer.expires = jiffies + 5*200;
        //启动定时器
        add_timer(&my_timer);

        return 0;
}


static struct file_operations async_fops =
{
        .owner    = THIS_MODULE,
        .fasync = async_fasync,
        .open = async_open,
};


static int __init dev_init(void)
{
     int ret;

      //注册字符设备
     ret = register_chrdev(DEVICE_MAJOR , DEVICE_NAME, &async_fops);
     if (ret < 0)
     {
            printk(DEVICE_NAME " can't register\n");
            return ret;
     }
     else
     {
            printk(DEVICE_NAME " register successful\n");
            return 0;
     }

}


static void __exit dev_exit(void)
{
        //撤销字符设备
        unregister_chrdev(DEVICE_MAJOR , DEVICE_NAME);
        printk("unregister success \n");
}


module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LKN@SCUT");

  

 

编译成功后会生成fasync.ko文件,先加载该驱动:

#insmod fasync.ko

然后,在/dev目录下创建设备文件:

#mknod sync c 228 1

接着,编写应用程序:

#include <unistd.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <signal.h>  
#include <string.h> 

//是否已接收到信号标志
int flag = 0;
 
//信号处理函数定义,由内核调用执行
void sig_handler(int signo)   
{    
        if (signo==SIGIO)   
        {   
              printf("receive successful!!!\n");
              flag = 1;
        }   
}   
  
int main()   
{   
        struct sigaction action;  
        int async_fd; 
  
        memset(&action, 0, sizeof(action));   
        //信号处理函数
        action.sa_handler = sig_handler;   
        action.sa_flags = 0;       
        //注册信号类型
        sigaction(SIGIO, &action, NULL);   

        async_fd = open("/dev/sync", O_RDONLY);   
        if(async_fd < 0)
        {
            printf("can not open /dev/sync \n");
            return -1;
        }

        //告诉驱动当前进程的PID
        fcntl(async_fd, F_SETOWN, getpid());   
        //设置驱动的FASYNC属性,支持异步通知
        fcntl(async_fd, F_SETFL, fcntl(async_fd, F_GETFL) | FASYNC);   
  
        printf("waiting for receive...\n");

        while(!flag)
        {
        }   
  
        close(async_fd);  
 
        return 0;   
}