创建虚拟字符设备

虚拟字符设备通过文件操作与用户空间应用程序交互。

示例代码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>  // copy_to_user, copy_from_user
#include <linux/cdev.h>

#define DEVICE_NAME "vchardev"
#define BUF_LEN 80

static int major;
static char message[BUF_LEN];
static struct class *vchardev_class = NULL;
static struct device *vchardev_device = NULL;

static int dev_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "vchardev: device opened\n");
    return 0;
}

static int dev_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "vchardev: device closed\n");
    return 0;
}

static ssize_t dev_read(struct file *file, char __user *buffer, size_t len, loff_t *offset)
{
    int bytes_read = 0;
    if (*message == 0)
        return 0;

    while (len && *message)
    {
        put_user(*(message++), buffer++);
        len--;
        bytes_read++;
    }
    printk(KERN_INFO "vchardev: read %d bytes\n", bytes_read);
    return bytes_read;
}

static ssize_t dev_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset)
{
    int i;
    for (i = 0; i < len && i < BUF_LEN - 1; i++)
        get_user(message[i], buffer + i);

    message[i] = '\0';
    printk(KERN_INFO "vchardev: received %zu characters from user\n", len);
    return len;
}

static struct file_operations fops = {
    .open = dev_open,
    .read = dev_read,
    .write = dev_write,
    .release = dev_release,
};

static int __init vchardev_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &fops);
    if (major < 0)
    {
        printk(KERN_ALERT "vchardev failed to register a major number\n");
        return major;
    }
    printk(KERN_INFO "vchardev: registered with major number %d\n", major);

    vchardev_class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(vchardev_class))
    {
        unregister_chrdev(major, DEVICE_NAME);
        printk(KERN_ALERT "Failed to register device class\n");
        return PTR_ERR(vchardev_class);
    }
    printk(KERN_INFO "vchardev: device class registered\n");

    vchardev_device = device_create(vchardev_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
    if (IS_ERR(vchardev_device))
    {
        class_destroy(vchardev_class);
        unregister_chrdev(major, DEVICE_NAME);
        printk(KERN_ALERT "Failed to create the device\n");
        return PTR_ERR(vchardev_device);
    }
    printk(KERN_INFO "vchardev: device created\n");

    return 0;
}

static void __exit vchardev_exit(void)
{
    device_destroy(vchardev_class, MKDEV(major, 0));
    class_unregister(vchardev_class);
    class_destroy(vchardev_class);
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "vchardev: module unloaded\n");
}

module_init(vchardev_init);
module_exit(vchardev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple virtual character device");

编译运行调试

编译代码:

$ make

加载模块:

$ insmod vchardev.ko

查看模块信息:

$ lsmod | grep vchardev
vchardev 16384 0

创建设备文件:

$ mknod /dev/vchardev c 254 0

查看设备文件信息:

$ ls -l /dev/vchardev
crw-rw-rw- 1 root root 254, 0 May 20 10:20 /dev/vchardev

打开设备文件:

$ cat /dev/vchardev

向设备文件写入数据:

$ echo "Hello, world!" > /dev/vchardev

从设备文件读取数据:

$ cat /dev/vchardev
Hello, world!

卸载模块:

$ rmmod vchardev