蓝桉云顶

Good Luck To You!

如何利用Cdev在Linux系统中实现设备驱动开发?

Linux内核中的cdev结构体用于描述字符设备,包括设备号和操作函数等关键信息。

Linux内核字符设备驱动详解

在Linux操作系统的世界中,一切皆文件,无论是硬件设备还是普通文件,都可以通过统一的接口进行访问,本文将详细探讨Linux内核中的字符设备驱动程序,特别是cdev结构体的使用,通过本文,您将了解字符设备的基础知识、cdev结构体的定义和操作,以及如何在内核中实现一个简单的字符设备驱动。

一、字符设备简介

字符设备是Linux系统中的三大类设备之一(另外两类是块设备和网络设备),它们面向流数据,以字节为单位进行顺序读取或写入操作,常见的字符设备包括鼠标、键盘、串口等,与块设备不同,字符设备通常不能随机访问数据,而是按照顺序读取或写入。

二、cdev结构体解析

在Linux内核中,struct cdev结构体用于描述一个字符设备,该结构体定义在<linux/cdev.h>头文件中,其定义如下:

struct cdev {
    struct kobject kobj;         // 内嵌的内核对象
    struct module *owner;        // 拥有该字符设备的模块指针
    const struct file_operations *ops; // 默认文件操作接口
    struct list_head list;      // 用于链表管理
    dev_t dev;                  // 设备号
    unsigned int count;         // 次设备号的数量
};

1.kobject kobj

kobject是一个内核对象,用于表示驱动程序中的设备,它包含一些基本的成员函数指针和引用计数等。

2.module *owner

指向拥有该字符设备结构体的模块的指针,这是为了确保设备在模块卸载时能够被正确释放。

3.file_operations *ops

这是一个函数指针数组,包含了字符设备驱动提供给虚拟文件系统的接口函数,如openreadwrite等。

4.list_head list

用于将所有注册的字符设备连接成一个链表,方便管理和遍历。

5.dev_t dev

字符设备的设备号,由主设备号和次设备号组成,主设备号标识设备类型,而次设备号区分同一类型的多个设备。

6.unsigned int count

隶属于同一主设备号的次设备号的数量。

三、cdev结构体的操作函数

1.cdev_init()

该函数用于初始化一个cdev结构体,并建立cdev与file_operations之间的连接。

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof(*cdev));
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops;
}

2.cdev_alloc()

动态申请一个cdev结构体内存,并进行初步的初始化。

struct cdev *cdev_alloc(void)
{
    struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    if (p) {
        INIT_LIST_HEAD(&p->list);
        kobject_init(&p->kobj, &ktype_cdev_dynamic);
    }
    return p;
}

3.cdev_add()

向系统添加一个cdev结构体,完成字符设备的注册。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    int ret;
    p->dev = dev;
    p->count = count;
    ret = kobject_add(&p->kobj, &char_ktype, NULL, "%s", devname(dev));
    if (ret < 0)
        return ret;
    return 0;
}

4.cdev_del()

从系统中删除一个cdev结构体,注销字符设备。

void cdev_del(struct cdev *p)
{
    kobject_put(&p->kobj);
}

四、字符设备驱动模型

字符设备驱动模型主要包括以下几个部分:

设备号分配:静态或动态分配设备号。

cdev结构体初始化:使用cdev_init()函数初始化cdev结构体。

file_operations实现:编写具体的设备操作函数,并将函数指针赋值给file_operations结构体。

设备注册与注销:使用cdev_add()cdev_del()函数向系统注册和注销字符设备。

用户空间交互:用户程序通过设备文件访问字符设备,触发内核中的设备操作函数。

五、示例代码

以下是一个简单的字符设备驱动示例,展示了如何使用cdev结构体实现一个基本的字符设备驱动。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#define MYDEVICE_NAME "mychardevice"
#define CLASS_NAME "mycharclass"
static int majorNumber;
static struct class *charClass;
static struct cdev myCdev;
static char msg[80] = "Hello, Linux kernel!";
static int deviceOpened = 0;
static int messageLen = 0;
// 定义file_operations结构体
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = deviceOpen,
    .release = deviceRelease,
    .read = deviceRead,
    .write = deviceWrite,
};
// 打开设备函数
int deviceOpen(struct inode *inode, struct file *file) {
    if (deviceOpened)
        return -EBUSY;
    deviceOpened++;
    try_module_get(THIS_MODULE);
    printk(KERN_INFO "Device opened
");
    return 0;
}
// 释放设备函数
int deviceRelease(struct inode *inode, struct file *file) {
    deviceOpened--;
    module_put(THIS_MODULE);
    printk(KERN_INFO "Device released
");
    return 0;
}
// 读取设备函数
ssize_t deviceRead(struct file *filp, char __user *buffer, size_t len, loff_t *offset) {
    int error_count = 0;
    printk(KERN_INFO "Device read request
");
    if (*offset == 0) {
        error_count = copy_to_user(buffer, msg, strlen(msg));
        *offset = strlen(msg);
    } else {
        error_count = -EFAULT; // Offset not supported yet.
    }
    return error_count ? error_count : strlen(msg);
}
// 写入设备函数
ssize_t deviceWrite(struct file *filp, const char __user *buff, size_t len, loff_t *offset) {
    int error_count = 0;
    printk(KERN_INFO "Device write request
");
    if (len > sizeof(msg))
        len = sizeof(msg);
    if (copy_from_user(msg, buff, len)) {
        printk(KERN_ALERT "Failed to copy from user space
");
        error_count = -EFAULT; // Failed to copy from user space.
    } else {
        msg[len] = '\0'; // Null-terminate the string.
        messageLen = len;
    }
    return error_count ? error_count : len;
}
// 初始化模块和注册设备
static int __init myCharDriver_init(void) {
    int result;
    printk(KERN_INFO "Initializing myCharDriver
");
    majorNumber = register_chrdev(0, "myCharDriver"); // 动态分配设备号
    if (majorNumber < 0) {
        printk(KERN_ALERT "Error registering char device
");
        return majorNumber;
    }
    printk(KERN_INFO "Device registered with major number %d
", majorNumber);
    charClass = class_create(THIS_MODULE, CLASS_NAME); // 创建设备类
    if (IS_ERR(charClass)) {
        unregister_chrdev(majorNumber, "myCharDriver"); // 如果创建失败,则注销设备号
        printk(KERN_ALERT "Failed to create device class
");
        return PTR_ERR(charClass);
    }
    cdev_init(&myCdev, &fops); // 初始化cdev结构体并建立与file_operations的连接
    myCdev.owner = THIS_MODULE; // 设置所有者为当前模块
    result = cdev_add(&myCdev, MKDEV(majorNumber, 0), 1); // 向系统添加cdev结构体,完成字符设备的注册
    if (result) {
        printk(KERN_ALERT "Failed to add character device
");
        class_destroy(charClass); // 如果添加失败,销毁设备类
        unregister_chrdev(majorNumber, "myCharDriver"); // 注销设备号
        return result;
    }
    printk(KERN_INFO "Character device registered successfully
");
    return 0; // 返回0表示成功加载模块
}
// 清理模块和注销设备
static void __exit myCharDriver_exit(void) {
    cdev_del(&myCdev); // 从系统中删除cdev结构体,注销字符设备
    class_unregister(charClass); // 注销设备类
    class_destroy(charClass); // 销毁设备类
    unregister_chrdev(majorNumber, "myCharDriver"); // 注销设备号
    printk(KERN_INFO "Character device unregistered
");
}
module_init(myCharDriver_init); // 指定模块加载函数
module_exit(myCharDriver_exit); // 指定模块卸载函数
MODULE_LICENSE("GPL"); // 指定许可证类型
MODULE_AUTHOR("Your Name"); // 指定作者信息
MODULE_DESCRIPTION("A simple char device driver example"); // 指定模块描述信息

各位小伙伴们,我刚刚为大家分享了有关“cdev linux”的知识,希望对你们有所帮助。如果您还有其他相关问题需要解决,欢迎随时提出哦!

  •  梦幻之城下
     发布于 2024-01-21 05:38:39  回复该评论
  • 自学Python用什么书好「自学Python哪本书好」这本书对于初学者来说非常友好,内容通俗易懂,适合零基础的Python学习者。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«    2024年11月    »
123
45678910
11121314151617
18192021222324
252627282930
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接