Heng30的博客
搜索 分类 关于 订阅

Linux字符设备驱动如何使用私有数据?

2025-03-18

每次打开字符设备驱动文件都会重新创建一个struct file结构体。如果想为每个打开的文件保留私有的数据,可以使用file->private_data成员。在驱动文件被关闭之前都可以访问该成员,当然也要在关闭文件之前释放分配的资源。

驱动代码

// simple.c

#include <linux/init.h>
#include <linux/io.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/slab.h>

#define MAJOR_NUM 54
#define DEV_NAME "simpledev"

static int _open(struct inode *inode, struct file *file) {
    pr_info("simple_open\n");

    char *ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
    if (!ptr)
        return -ENOMEM;

    memcpy(ptr, "1234567890\n", 11);

    file->private_data = ptr;
    pr_info("kzalloc 4096 bytes of memory\n");

    return 0;
}

static ssize_t _read(struct file *file, char __user *user_buffer, size_t size,
                     loff_t *offs) {
    pr_info("simple_read\n");

    char *my_data = (char *)file->private_data;
    if (!my_data)
        return -1;

    size = min(PAGE_SIZE, size);
    int remain_len = copy_to_user(user_buffer, my_data, size);
    return size - remain_len;
}

static ssize_t _write(struct file *file, const char __user *user_buffer,
                      size_t size, loff_t *offs) {
    pr_info("simple_write\n");

    char *my_data = (char *)file->private_data;
    if (!my_data)
        return -1;

    size = min(PAGE_SIZE, size);
    int remain_len = copy_from_user(my_data, user_buffer, size);
    return size - remain_len;
}

static int _close(struct inode *inode, struct file *file) {
    pr_info("simple_close\n");

    if (file->private_data) {
        kfree(file->private_data);
        pr_info("kfree 4096 bytes of memory\n");
    }

    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = _open,
    .read = _read,
    .write = _write,
    .release = _close,
};

static int __init simple_init(void) {
    pr_info("simple_init\n");

    int status = register_chrdev(MAJOR_NUM, DEV_NAME, &fops);
    if (status < 0) {
        pr_err("register_chrdev error\n");
        return status;
    }

    return 0;
}

static void __exit simple_exit(void) {
    pr_info("simple_exit\n");

    unregister_chrdev(MAJOR_NUM, DEV_NAME);
}

module_init(simple_init);
module_exit(simple_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("heng30");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple kernel module");

编译脚本

#!/bin/sh

top-dir = $(shell pwd)
kernel-version = $(shell uname -r)
kernel-dir ?= /lib/modules/$(kernel-version)/build

obj-m += simple.o

all:
        make -C $(kernel-dir) modules M=$(top-dir)

clean:
        rm -f *.o *.ko *.mod *.mod.c *.order *.symvers *.dtbo
        make -C $(kernel-dir) clean m=$(top-dir)

测试

  • 编译程序:make

  • 安装驱动:insmod simple.ko

  • 创建驱动文件:mknod /dev/simpledev c 54 0

  • 读取数据:head -n 1 /dev/simpledev

  • 写入数据:echo "hello" > /dev/simpledev

  • 移除驱动:rmmod simple.ko

  • 查看输出:dmesg

[24718.066010] simple_init
[24723.819010] simple_open
[24723.819016] kzalloc 4096 bytes of memory
[24723.819020] simple_read
[24723.819034] simple_close
[24723.819034] kfree 4096 bytes of memory
[24748.298867] simple_open
[24748.298875] kzalloc 4096 bytes of memory
[24748.298900] simple_write
[24748.298906] simple_close
[24748.298907] kfree 4096 bytes of memory
[24770.466970] simple_exit