字符设备驱动(内核态用户态内存交互)

前言

内核驱动:运行在内核态的动态模块,遵循内核模块框架接口,更倾向于插件。

应用程序:运行在用户态的进程。

应用程序与内核驱动交互通过既定接口,内核态和用户态访问依然遵循内核既定接口。

环境搭建

系统:openEuler-20.03-LTS-SP3

复制代码
yum install gcc kernel-devel

编写源码

  • char_module.c
c 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/device.h> //下面这三个头文件是由于动态创建需要加的
#include <linux/device.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

#define DEVICE_NAME "char_module"
#define BUF_SIZE 32

static struct class *cdev_class;
dev_t dev_num = 0; // 这里是动态分配设备号和动态创建设备结点需要用到的
struct cdev dev_c;

static char context_buf[BUF_SIZE]={"this a test context buffer\0"};

static ssize_t read(struct file *, char *, size_t, loff_t *);
static ssize_t write(struct file *, const char *, size_t, loff_t *);
static int open(struct inode *, struct file *);
static int release(struct inode *, struct file *);

// 初始化字符设备驱动的 file_operations 结构体
struct file_operations fops = {
        .read = read,
        .write = write,
        .open = open,
        .release = release
};

static int __init demo_init(void)
{
        int ret, err;

        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);

        // 注册设备驱动
        ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); // 动态分配设备号
        if (ret)
        {
                printk("demo_init register failure\n");
                unregister_chrdev_region(dev_num, 1);
                return ret;
        }
        printk("demo_init register success\n");

        // 初始化设备操作
        cdev_init(&dev_c, &fops);
        err = cdev_add(&dev_c, dev_num, 1);
        if (err)
        {
                printk(KERN_NOTICE "error %d adding cdev\n", err);
                unregister_chrdev_region(dev_num, 1);
                return err;
        }

        // 动态创建设备结点
        cdev_class = class_create(THIS_MODULE, DEVICE_NAME); 
        if (IS_ERR(cdev_class))
        {
                printk("ERR:cannot create a cdev_class\n");
                unregister_chrdev_region(dev_num, 1);
                return -1;
        }
        device_create(cdev_class, NULL, dev_num, 0, DEVICE_NAME);

        return ret;
}

static void __exit demo_exit(void)
{
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);

        // 注销设备驱动
        device_destroy(cdev_class, dev_num);
        class_destroy(cdev_class);
        unregister_chrdev_region(dev_num, 1);
}

static ssize_t read(struct file *filp, char *buf, size_t len, loff_t *off)
{
        // 内核空间到用户空间copy
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);
        if (raw_copy_to_user(buf, &context_buf, sizeof(context_buf)))
        {
                return -EFAULT;
        }
        printk(KERN_INFO "user space: %pF", buf);
        printk(KERN_INFO "read: %pF; size: %ld; data: %s", &context_buf, sizeof(context_buf), context_buf);
        return BUF_SIZE;
}

static ssize_t write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
        // 用户空间到内核空间copy
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);
        if (raw_copy_from_user(&context_buf, buf, sizeof(context_buf)))
        {
                return -EFAULT;
        }
        printk(KERN_INFO "user space: %pF", buf);
        printk(KERN_INFO "write: %pF; size: %ld; data: %s", &context_buf, sizeof(context_buf), context_buf);
        return BUF_SIZE;
}

static int open(struct inode *inodp, struct file *filp)
{
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);
        return 0;
}

static int release(struct inode *inodp, struct file *filp)
{
        printk(KERN_INFO "%s: %s", DEVICE_NAME, __func__);
        return 0;
}

module_init(demo_init);
module_exit(demo_exit);
  • Makefile
c 复制代码
ifneq ($(KERNELRELEASE),)
obj-m := char_module.o

else
PWD  := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules modules_install
clean:
        rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.*  Module.*
endif

app.c

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
 
#define CHAR_DEV_NAME "/dev/char_module"

int main()
{
        int ret;
        int fd;
        char buf[32];
 
        fd = open(CHAR_DEV_NAME, O_RDWR | O_NDELAY);
        if(fd < 0)
        {
                printf("open failed!\n");
                return -1;
        }

        int size = read(fd, buf, 32);

        printf("read size: %d;\nbuffer:[%s]\n", size, buf);

        char *write_buf = "use a application wirte to driver buffer";

        int w_size = write(fd, write_buf, strlen(write_buf));

        printf("write size: %d;\nbuffer:[%s]\n", w_size, write_buf);

        close(fd);

        return 0;
}

构建并测试

  • 驱动构建

    bash 复制代码
    make && insmod char_module.ko
  • 驱动信息确认

  • 应用程序构建

    bash 复制代码
    gcc app.c -o app
    ./app
  • 应用程序运行结果

  • 查看驱动日志

    bash 复制代码
    dmesg
相关推荐
EQ-雪梨蛋花汤1 小时前
【Part 4 未来趋势与技术展望】第二节|多模态交互体验:手势、语音与眼动控制的集成应用
交互
猫林老师1 天前
HarmonyOS分布式硬件共享:调用手机摄像头的手表应用
华为·交互·harmonyos
Larry_Yanan3 天前
QML学习笔记(四十三)QML与C++交互:上下文属性暴露
c++·笔记·qt·学习·ui·交互
top_designer3 天前
告别“静态”VI手册:InDesign与AE打造可交互的动态品牌规范
设计模式·pdf·交互·vi·工作流·after effects·indesign
嵌入式学习和实践3 天前
C# WinForms 多窗口交互通信的示例-主窗口子窗口交互通信
c#·交互·主窗口-子窗口通信
梵得儿SHI4 天前
Prompt Engineering 核心知识:从基础模式到思维链,掌握大模型高效交互秘籍
大模型·prompt·交互·提示词·对话·大模型提问艺术·极简指令
IT小哥哥呀4 天前
工业互联与设备IOT助力生产制造实践分享
物联网·交互·制造·简单工厂模式
jjjxxxhhh1234 天前
【项目-】Qt + QCustomPlot 实现频谱监测仪:四图联动、高频信号注入、鼠标交互全解析
开发语言·qt·交互
gis分享者5 天前
学习threejs,打造交互式花卉生成器
交互·threejs·生成·shadermaterial·花卉·planegeometry
猫头虎6 天前
OpenAI发布构建AI智能体的实践指南:实用框架、设计模式与最佳实践解析
人工智能·设计模式·开源·aigc·交互·pip·ai-native