linux环形缓冲区kfifo实践4:异步通知fasync

基础知识

异步通知在内核中使用struct fasync_struct数据结构来描述。

cpp 复制代码
<include/linux/fs.h>
struct fasync_struct {
	spinlock_t		fa_lock;
	int			magic;
	int			fa_fd;
	struct fasync_struct	*fa_next; /* singly linked list */
	struct file		*fa_file;
	struct rcu_head		fa_rcu;
};

设备驱动的file_operations的操作方法集中有一个fasync的方法,我们需要实现它。

cpp 复制代码
static const struct file_operations demodrv_fops = {
    .owner = THIS_MODULE,
    ....
    fasync = my_fasync,
};

static int my_fasync(int fd, struct file *file, int on){
    struct mydemo_private_data *data = file->private_data;
    struct mydemo_device *device = data->device;
    return fasync_helper(fd, file, on, &device->fasync);
}

这里直接使用fasync_helper()函数来构造struct fasync_struct类型的节点,并添加到系统的链表中。

发送信号 kill_fasync

cpp 复制代码
/* can be called from interrupts */
extern void kill_fasync(struct fasync_struct **, int, int);

sigaction

cpp 复制代码
        struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

struct sigaction act,oldact;

sigemptyset(&act.sa_mask);

sigaddset(&act.sa_mask,SIGIO);//设置SIGIO信号

act.sa_flags = SA_SIGINFO;//该选项可以使内核通过siginfo->si_band将POLL_IN和POLL_OUT上传到用户空间。便于在信号处理函数中区分读写信号。

驱动代码:

cpp 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/mutex.h>

#define DEBUG_INFO(format, ...) printk("%s:%d -- "format"\n",\
__func__,__LINE__,##__VA_ARGS__)

struct ch5_kfifo_struct{
    struct miscdevice misc;
    struct file_operations fops;
    struct kfifo fifo;
    char buf[64];
    char name[64];
    wait_queue_head_t read_queue;
    wait_queue_head_t write_queue;
    struct fasync_struct *fasync;
};

static int ch5_open (struct inode *inode, struct file *file){
    struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)container_of(file->f_op,struct ch5_kfifo_struct,fops);
    file->private_data = p;
    DEBUG_INFO("major = %d, minor = %d\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));
    DEBUG_INFO("name = %s",p->misc.name);
    return 0;
}



static unsigned int ch5_poll(struct file *file, poll_table *wait){
    int mask = 0;
    struct ch5_kfifo_struct *p __attribute__((unused)) = (struct ch5_kfifo_struct *)file->private_data;
    DEBUG_INFO("begin wait:%s",p->name);
    poll_wait(file, &p->read_queue, wait);
    poll_wait(file, &p->write_queue, wait);
    DEBUG_INFO("poll:%s",p->name);
    if (!kfifo_is_empty(&p->fifo)){
        mask |= POLLIN | POLLRDNORM;
        DEBUG_INFO("POLLIN:%s",p->name);
    }
        
    if (!kfifo_is_full(&p->fifo)){
        mask |= POLLOUT | POLLWRNORM;
        DEBUG_INFO("POLLOUT:%s",p->name);
    }
        
    return mask;
}

static int ch5_fasync(int fd, struct file *file, int on){
    struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)file->private_data;
    return fasync_helper(fd, file, on, &p->fasync);
}

static ssize_t ch5_read (struct file *file, char __user *buf, size_t size, loff_t *pos){
    struct ch5_kfifo_struct *p __attribute__((unused)) = (struct ch5_kfifo_struct *)file->private_data;
    int ret;
    int actual_readed = 0;

    if(kfifo_is_empty(&p->fifo)){
        if(file->f_flags & O_NONBLOCK){
            DEBUG_INFO("kfifo is null");
            return -EAGAIN;
        }
        ret = wait_event_interruptible(p->read_queue,kfifo_is_empty(&p->fifo) == 0);
        if(ret){
            DEBUG_INFO("wait_event_interruptible error");
            return ret;
        }
        DEBUG_INFO("");
    }
    
    ret = kfifo_to_user(&p->fifo, buf, size, &actual_readed);
    if (ret){
        DEBUG_INFO("kfifo_to_user error");
		return -EIO;
    }
    
    DEBUG_INFO("size = %d,actual_readed = %d\n",size,actual_readed);

    if (!kfifo_is_full(&p->fifo)){
        wake_up_interruptible(&p->write_queue);
        if(p->fasync != NULL){
            kill_fasync(&p->fasync, SIGIO, POLL_OUT);
        }
    }

    memset(p->buf,0,sizeof(p->buf));
    ret = copy_from_user(p->buf, buf, actual_readed);
    if(ret != 0){
        DEBUG_INFO("copy_from_user error ret = %d\n",ret);
    }else{
        DEBUG_INFO("read p->buf = %s\n",p->buf);
    }
    *pos = *pos + actual_readed;
    return actual_readed;
}
static ssize_t ch5_write (struct file *file, const char __user *buf, size_t size, loff_t* pos){
    struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)file->private_data;
    int actual_writed = 0;
    int ret;
    if(kfifo_is_full(&p->fifo)){
        if(file->f_flags & O_NONBLOCK){
            DEBUG_INFO("kfifo is full");
            return -EAGAIN;
        }
        ret = wait_event_interruptible(p->write_queue, kfifo_is_full(&p->fifo) == 0);
        if(ret){
            DEBUG_INFO("wait_event_interruptible error");
            return ret;
        }
        DEBUG_INFO("");
    }
    ret = kfifo_from_user(&p->fifo, buf, size, &actual_writed);
    if (ret){
        DEBUG_INFO("kfifo_from_user error");
		return -EIO;
    }
    
    DEBUG_INFO("actual_writed = %d\n",actual_writed);

    if (!kfifo_is_empty(&p->fifo)){
        wake_up_interruptible(&p->read_queue);
        if(p->fasync != NULL){
            kill_fasync(&p->fasync, SIGIO, POLL_IN);
        }
        
    }
    memset(p->buf,0,sizeof(p->buf));
    ret = copy_from_user(p->buf, buf, actual_writed);
    if(ret != 0){
        DEBUG_INFO("copy_from_user error ret = %d\n",ret);
    }else{
        DEBUG_INFO("write:p->buf = %s\n",p->buf);
    }
    *pos = *pos + actual_writed;
    return actual_writed;
}

static int ch5_release (struct inode *inode, struct file *file){
    struct ch5_kfifo_struct *p = (struct ch5_kfifo_struct *)container_of(file->f_op,struct ch5_kfifo_struct,fops);
    ch5_fasync(-1, file, 0);
    p->fasync = NULL;
    DEBUG_INFO("close");
    return 0;
}
struct ch5_kfifo_struct ch5_kfifo[1];
//  = {
//     .misc = { 
//         .name = "ch5-04-block",
//         .minor = MISC_DYNAMIC_MINOR,
//     },
//     .fops = {
//         .owner = THIS_MODULE,
//         .read = ch5_read,
//         .write = ch5_write,
//         .open = ch5_open,
//         .release = ch5_release,
//     },
// };

static int __init ch5_init(void){
    int ret = 0;
    int i = 0;
    struct ch5_kfifo_struct *p;

    DEBUG_INFO("start init\n");
    for(i = 0;i < sizeof(ch5_kfifo)/sizeof(ch5_kfifo[0]);i++){
        p = &ch5_kfifo[i];
        p->fasync = NULL;
        snprintf(p->name,sizeof(p->name),"ch5-06-fasync-%d",i);
        p->misc.name = p->name;
        p->misc.minor = MISC_DYNAMIC_MINOR;

        p->fops.owner = THIS_MODULE;
        p->fops.read = ch5_read;
        p->fops.write = ch5_write;
        p->fops.open = ch5_open;
        p->fops.release = ch5_release;
        p->fops.poll = ch5_poll;
        p->fops.fasync = ch5_fasync;

        p->misc.fops = &p->fops;
        ret = kfifo_alloc(&p->fifo,
                    8,
                    GFP_KERNEL);
        if (ret) {
            DEBUG_INFO("kfifo_alloc error: %d\n", ret);
            ret = -ENOMEM;
            return ret;
        }
        DEBUG_INFO("kfifo_alloc size = %d",kfifo_avail(&p->fifo));

        init_waitqueue_head(&p->read_queue);
        init_waitqueue_head(&p->write_queue);

        ret = misc_register(&p->misc);
        if(ret < 0){
            DEBUG_INFO("misc_register error: %d\n", ret);
            return ret;
        }
    }
    
    DEBUG_INFO("misc_register ok");
    return 0;
}

static void __exit ch5_exit(void){
    int i = 0;
    struct ch5_kfifo_struct *p;
    for(i = 0;i < sizeof(ch5_kfifo)/sizeof(ch5_kfifo[0]);i++){
        p = &ch5_kfifo[i];
        misc_deregister(&p->misc);
        kfifo_free(&p->fifo);
    }

    DEBUG_INFO("exit\n");
}

module_init(ch5_init);
module_exit(ch5_exit);

MODULE_LICENSE("GPL");

应用代码:

cpp 复制代码
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <linux/input.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>

#define DEBUG_INFO(format, ...) printf("%s:%d -- "format"\n",\
    __func__,__LINE__,##__VA_ARGS__)

int fd = 0;

void signal_handler(int signum,siginfo_t *siginfo,void *act){
    int ret = 0;
    char buf[1024] = {0};
    if(signum == SIGIO){
        if(siginfo->si_band & POLLIN){
            DEBUG_INFO("kfifo is not empty");
            if((ret = read(fd,buf,sizeof(buf))) == -1){
                buf[ret] = '\0';
            }
            DEBUG_INFO("buf = %s",buf);
        }
        if(siginfo->si_band & POLLOUT){
            DEBUG_INFO("kfifo is not full");
        }
    }
}

int main(int argc, char**argv){
    struct sigaction act,oldact;
    int flag;
    char *filename = "/dev/ch5-06-fasync-0";
    int ret = 0;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask,SIGIO);

    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = signal_handler;

    if(sigaction(SIGIO, &act,&oldact) == -1){
        DEBUG_INFO("sigaction failed");
        return -1;
    }
    fd = open(filename,O_RDWR);
    if(fd < 0){
        perror("open");
        DEBUG_INFO("open %s failed",filename);
        return -1;
    }
    DEBUG_INFO("open %s ok",filename);

    ret = fcntl(fd, F_SETOWN, getpid());
    if(ret < 0){
        DEBUG_INFO("fcntl F_SETOWN  %s failed",filename);
        return -1;
    }
    ret = fcntl(fd, F_SETSIG, SIGIO);
    if(ret < 0){
        DEBUG_INFO("fcntl F_SETSIG  %s failed",filename);
        return -1;
    }
    flag = fcntl(fd, F_GETFL);
    ret = fcntl(fd, F_SETFL, flag | FASYNC);
    if(ret < 0){
        DEBUG_INFO("fcntl F_SETFL  %s failed",filename);
        return -1;
    }
    while(1){
        sleep(1);
    }              
    return 0;
}

测试结果:

小结

相关推荐
Konwledging7 小时前
kernel-devel_kernel-headers_libmodules
linux
Web极客码7 小时前
CentOS 7.x如何快速升级到CentOS 7.9
linux·运维·centos
一位赵8 小时前
小练2 选择题
linux·运维·windows
代码游侠8 小时前
学习笔记——Linux字符设备驱动开发
linux·arm开发·驱动开发·单片机·嵌入式硬件·学习·算法
Lw老王要学习9 小时前
CentOS 7.9达梦数据库安装全流程解析
linux·运维·数据库·centos·达梦
CRUD酱9 小时前
CentOS的yum仓库失效问题解决(换镜像源)
linux·运维·服务器·centos
zly350010 小时前
VMware vCenter Converter Standalone 转换Linux系统,出现两个磁盘的处理
linux·运维·服务器
Albert Edison10 小时前
【Python】函数
java·linux·python·pip
General_G10 小时前
Linux中的信号
linux·运维·服务器
诸神缄默不语10 小时前
当无法直接用apt instll时,Linux如何离线安装软件包(以make为例)
linux·运维·服务器