6. linux 内核事件通知驱动编程

1. eventfd 事件通知

eventfd 事件通知不仅可以用于线程间的事件通知,还可以用于内核和用户空间的事件通知。

2. eventfd 用户空间API

#include <sys/eventfd.h>


//创建事件通知句柄,可以被read 和 write 函数使用
int eventfd(unsigned int initval, int flags);

Linux shell下 输入 man eventfd 命令查看,里面有详细的使用说明和例子

3. eventfd 内核空间API

#include <linux/eventfd.h>


//将fd 转换为 struct eventfd_ctx
struct eventfd_ctx *eventfd_ctx_fdget(int fd);


//移除 struct eventfd_ctx
void eventfd_ctx_put(struct eventfd_ctx *ctx);


//相当于用户层的write
__u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n);
//相当于用户层的read
ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, __u64 *cnt);

4. 驱动例程

command.h

#ifndef INCLUDE_COMMAND_H
#define INCLUDE_COMMAND_H


#include <linux/ioctl.h>


//幻数
#define IOCTL_MAGIC 'x'


//定义命令
#define HELLO_RESET _IO(IOCTL_MAGIC, 1)
#define HELLO_READ _IOR(IOCTL_MAGIC, 2, int)
#define HELLO_WRITE _IOW(IOCTL_MAGIC, 3, int)
#define HELLO_NOTIFICATION _IOW(IOCTL_MAGIC, 4, int)


#endif

hello.c

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/delay.h>  
#include <linux/irq.h>  
#include <linux/poll.h>  
#include <linux/cdev.h>  
#include <linux/device.h>
#include <linux/eventfd.h>
#include <linux/timer.h>


#include "command.h"


#define HELLO_CNT   1  


//主设备号为0,表示动态分配设备号 
dev_t dev = 0;
static int major = 0;   
static int minor = 0;

static struct cdev *hello_cdev[HELLO_CNT];
static struct class *hello_class = NULL;
static struct class_device * hello_class_dev[HELLO_CNT];




static int s_val = 0;
static int s_EventFd = -1;
static struct eventfd_ctx * eve_ctx = NULL;
static struct timer_list tm;


int hello_open(struct inode * pnode, struct file * pfile)
{
    printk("open file..\n");
    int num = MINOR(pnode->i_rdev);
    if(num >= HELLO_CNT)
    {
        return -ENODEV;
    }

    pfile->private_data = hello_cdev[num];

    return 0;
}




int hello_release(struct inode *pnode, struct file *pfile)
{
    printk("release file ..\n");


    return 0;
}




long hello_ioctl(struct file *pfile, unsigned int cmd, unsigned long val)
{
    int ret = 0;

    switch(cmd)
    {
        case HELLO_RESET:
        {
            printk("Rev HELLO_RESET cmd\n");
            break;
        }
        case HELLO_READ:
        {
            printk("Rec HELLO_READ cmd\n");

            ret = copy_to_user(val, &s_val, sizeof(int));
            break;
        }
        case HELLO_WRITE:
        {
            printk("Rec HELLO_WRITE cmd\n");

            ret = copy_from_user(&s_val, val, sizeof(int));
            break;
        }
        case HELLO_NOTIFICATION:
        {
            printk("Rec HELLO_NOTIFICATION cmd..\n");
            ret = copy_from_user(&s_EventFd, val, sizeof(int));
            eve_ctx = eventfd_ctx_fdget(s_EventFd);
            break;
        }
        default:
        {
            printk("unkownd cmd...\n");
            return -EINVAL;
        }
    }

    return ret;
}


//文件操作结构体
static const struct file_operations fops = 
{
    .owner = THIS_MODULE,
    .open = hello_open,
    .release = hello_release,
    //.read = hello_read,
    //.write = hello_write,
    .unlocked_ioctl = hello_ioctl,
};


static void setup_cdev(int index)
{
    int err, devno = MKDEV(major, index);

    cdev_init(hello_cdev[index], &fops);
    hello_cdev[index]->owner = THIS_MODULE;
    hello_cdev[index]->ops = &fops;
    err = cdev_add(hello_cdev[index], devno, 1);
    if(err)
    {
        printk(KERN_NOTICE "Error %d adding hello%d", err, index);
    }
}




void call_back(unsigned long val)
{
    printk("timer is out\n");
    //事件通知上层运用
    if(eve_ctx)
    {
       eventfd_signal(eve_ctx, 1);
    }


    tm.expires = jiffies + 2 * HZ;
    add_timer(&tm);
}

static void __init hello_init(void)
{


    //申请设备号,动态or静态
    int ret = 0;
    if(major)
    {
        //为字符设备静态申请第一个设备号
        dev = MKDEV(major, minor);
        ret = register_chrdev_region(dev, HELLO_CNT, "hello");
    }
    else
    {
        //为字符设备动态申请一个设备号
        ret = alloc_chrdev_region(&dev, minor, HELLO_CNT, "hello");
        major = MAJOR(dev);
    }


    //构造cdev设备对象
    int i = 0;
    for(i = 0; i < HELLO_CNT; ++i)
    {
        hello_cdev[i] = cdev_alloc();


    }


    //初始化设备对象    
    for(minor = 0; minor < HELLO_CNT; ++minor)
    {
        setup_cdev(minor);
    }

    hello_class = class_create(THIS_MODULE, "hello");
    for(minor = 0; minor < HELLO_CNT; ++minor)
    {
        hello_class_dev[minor] = device_create(hello_class, NULL, MKDEV(major, minor), NULL, "hello%d",minor);
    }


    //初始化定时器
    init_timer(&tm);
    tm.function = call_back;
    tm.expires = jiffies + 2 * HZ;
    add_timer(&tm);


}




static void __exit hello_exit(void)
{
    for(minor = 0; minor < HELLO_CNT; ++minor)
    {
        device_destroy(hello_class, MKDEV(major, minor));
    }
    class_destroy(hello_class);


    //从内核注销cdev设备对象
    cdev_del(hello_cdev);

    //回收设备号
    unregister_chrdev_region(dev, HELLO_CNT);


    //回收eventfd context
    if(eve_ctx)
    {
        eventfd_ctx_put(eve_ctx);
        eve_ctx = NULL;
    }


    //注销定时器
    del_timer(&tm);
}


module_init(hello_init);  
module_exit(hello_exit);  


MODULE_LICENSE("GPL");  

5. 用户空间

test_ioctl.c

/*
 * =====================================================================================
 *
 *       Filename:  ioctl.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  08/27/17 14:18:42
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  linsheng.pan (), [email protected]
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>


#include "command.h"


int main(int argc, char *argv[])
{
    int fd = open("/dev/hello0", O_RDWR);
    if(fd < 0)
    {
        perror("open error:");
        return -1;
    }


    if(ioctl(fd, HELLO_RESET) < 0)
    {
        perror("error:");
        return -1;
    }


    int val = 1;

    if(ioctl(fd, HELLO_WRITE, &val) < 0)
    {
        perror("write error:");
        return -1;
    }


    val = 2;
    if(ioctl(fd, HELLO_READ, &val) < 0)
    {
        perror("read error");
        return -1;
    }


    printf("val = %d\n", val);


    return 0;
}

猜你喜欢

转载自www.cnblogs.com/standardzero/p/12551024.html