字符设备驱动基础—sys文件系统,udev介绍,驱动模块在内核空间注册设备

news2024/12/29 10:48:57

文章目录

    • sys文件系统介绍
      • 设计思想
      • 应用和功能
    • udev介绍
      • 主要功能
      • 工作原理
      • 使用 `udevadm` 工具
    • 设备文件创建流程
      • 驱动程序的注册
      • device_create函数详解
      • 示例代码
      • 效果图

sys文件系统介绍

sysfs 是 Linux 内核中的一种虚拟文件系统,它为用户空间和内核之间提供了一种统一的接口。通过 sysfs,用户可以查看和修改内核对象的属性,比如设备、驱动程序和内核子系统的配置和状态。sysfs 通常挂载在 /sys 目录下。

设计思想

sysfs 的设计思想主要包括以下几个方面:

  1. 内核对象的可视化和组织

    • sysfs 将内核中的对象(如设备、驱动、类等)以文件和目录的形式展现给用户。这些对象按照层次结构组织,类似于文件系统中的目录结构。这使得内核对象的关系和层次变得清晰易懂。
  2. 统一的接口和操作方式

    • sysfs 提供了一种统一的方式来访问内核对象的属性。用户可以通过标准的文件操作(如 read, write 等)来查看和修改这些属性。每个属性通常对应一个文件,文件的内容即是该属性的值。
  3. 动态可配置性和可扩展性

    • sysfs 支持动态添加和删除节点。这意味着内核模块可以在运行时通过 sysfs 创建和删除文件或目录,以反映系统状态的变化。这种动态特性使 sysfs 适用于热插拔设备等需要实时更新的场景。
  4. 轻量级和高效

    • sysfs 是一个轻量级的文件系统,它没有存储数据的持久化特性,所有数据都驻留在内存中。这使得 sysfs 操作非常高效,适合频繁访问和快速响应的需求。
  5. 安全性和访问控制

    • sysfs 文件系统中的节点可以设置权限,控制不同用户和进程的访问。这有助于保护系统的关键数据和配置,防止未经授权的访问或修改。

应用和功能

sysfs 主要用于以下几个方面:

  1. 设备管理

    • sysfs 通过 /sys/class, /sys/bus, /sys/devices 等目录组织系统中的所有设备和设备驱动,用户可以查看设备的状态、属性,并可以通过写入相应的文件来控制设备。
  2. 驱动管理

    • 驱动程序可以通过 sysfs 暴露其支持的设备类型和属性,这样用户和其他系统组件可以通过读取 sysfs 节点来获取驱动程序的信息。
  3. 内核子系统配置

    • 一些内核子系统(如电源管理、内存管理等)提供了 sysfs 接口,允许用户调整相关配置或获取状态信息。例如,通过 /sys/power 目录,可以管理系统的电源状态。

sysfs 的设计和实现大大增强了内核与用户空间的交互能力,使得系统管理和设备控制变得更加直观和灵活。

udev介绍

udev 是 Linux 系统中的一个设备管理工具和守护进程,负责在用户空间管理设备节点。它是设备管理框架的一部分,用于响应系统中的设备事件,并在 /dev 目录中创建和删除设备节点。udev 是 Linux 系统中处理设备管理的重要组件。它提供了一种灵活而强大的方式来响应和管理设备事件,确保系统中的设备节点和权限设置是动态更新和适当配置的。对于系统管理员和开发者来说,理解和利用 udev 可以大大简化设备管理的工作。

主要功能

  1. 设备节点管理

    • udev 动态地在 /dev 目录下创建和删除设备节点,这些节点表示系统中的硬件设备。它根据系统中的设备出现或消失的情况更新设备节点。
  2. 设备事件处理

    • 当新的硬件设备插入或移除时,内核会生成相应的事件。udev 监听这些事件,并根据配置文件中的规则执行相应的操作,如创建设备节点、设置设备权限、加载固件等。
  3. 设备命名

    • udev 允许管理员通过规则文件对设备节点进行命名。例如,可以根据设备的类型、属性、序列号等为设备节点分配有意义的名称,这样在管理系统设备时更容易识别和区分设备。
  4. 权限设置

    • udev 规则文件可以指定设备节点的权限和所有者。这对于多用户系统非常重要,可以控制哪些用户或组可以访问特定的设备。
  5. 自动化任务

    • udev 可以在设备事件发生时触发脚本或程序。例如,当插入一个USB设备时,可以自动挂载它,或者当插入一个网络接口时,自动配置网络。

工作原理

  1. 内核事件

    • 当一个设备插入或移除时,内核会通过 netlink 接口通知 udev。这些事件包括设备的添加、移除、变化等。
  2. 规则匹配

    • udev 通过配置的规则文件(通常位于 /etc/udev/rules.d/)对事件进行匹配。规则文件指定了当匹配到特定设备时应该采取的操作。
  3. 执行操作

    • 根据匹配的规则,udev 执行相应的操作,如创建设备节点、设置权限、运行脚本等。

使用 udevadm 工具

udevadmudev 提供的命令行工具,用于管理和调试 udev。它可以用于触发设备事件、监视设备事件、查看设备信息等。例如:

  • 查看当前的 udev 规则:

    udevadm info --query=all --name=/dev/sda
    
  • 监视设备事件:

    udevadm monitor
    

设备文件创建流程

驱动程序的注册

  1. 定义和实现操作函数

    • 驱动程序必须定义一个 file_operations 结构体,包含设备操作函数的指针,如 openreleasereadwrite 等。这些函数定义了如何处理设备的各种操作请求。
    static const struct file_operations my_fops = {
        .open = my_open,
        .release = my_release,
        .read = my_read,
        .write = my_write,
    };
    
  2. 注册字符设备

    • 驱动程序使用 register_chrdev(或类似的函数)注册设备号,并将其与 file_operations 结构体相关联。这样,内核知道如何处理对该设备的操作。
    int major = register_chrdev(0, "my_device", &my_fops);
    

    这里,register_chrdev 返回主设备号,my_device 是设备名称,my_fops 是操作函数集合。

  3. 创建设备节点

    • 驱动程序可以使用 device_create 函数创建设备节点,通常在用户空间使用 udev 来自动创建和管理设备节点。
    struct class *my_class;
    struct device *my_device;
    
    my_class = class_create(THIS_MODULE, "my_class");
    my_device = device_create(my_class, NULL, MKDEV(major, 0), NULL, "my_device");
    

device_create函数详解

当驱动程序调用 device_create 时,它实际上是在内核中注册了一个新的设备对象。这个设备对象包含了设备的相关信息,包括设备名称、设备号、设备类等。

struct device *device_create(struct class *cls, struct device *parent, dev_t devt,
                             void *drvdata, const char *fmt, ...);

这里,cls 是设备的类结构体指针,devt 是设备号,fmt 是格式化字符串,用于生成设备名称。

内核在成功创建设备对象后,会生成一个 uevent 事件。这是一个内核通知事件,用于告知用户空间有新的设备注册或现有设备状态发生变化。这个事件包含了设备的属性和相关信息。

udev 守护进程监听这些 uevent 事件。每当内核发出这样的事件时,udev 会根据事件信息和系统中定义的 udev 规则文件,决定如何处理该事件。

udev 规则文件通常位于 /etc/udev/rules.d//lib/udev/rules.d/ 目录下,这些规则定义了如何为不同类型的设备创建设备节点、设置权限、指定设备文件名称等。

根据 udev 规则和 uevent 中的信息,udev/dev 目录下创建相应的设备文件(设备节点)。这些设备文件允许用户空间的应用程序与该设备进行交互。

除了创建设备文件外,udev 还可以根据规则设置设备文件的权限、所有者和其他属性。这确保了设备文件的安全性和可访问性。

示例代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>

#define DEVICE_NAME "my_char_device"

static int major_number;
static struct class *my_class = NULL;
static struct device *my_device = NULL;
static struct cdev mydev;  // 声明 cdev 结构体

static int my_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "my_char_device: open()\n");
    return 0;
}

static int my_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "my_char_device: release()\n");
    return 0;
}

static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    printk(KERN_INFO "my_char_device: read()\n");
    return 0;
}

static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    printk(KERN_INFO "my_char_device: write()\n");
    return count;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
};

static int __init test_init(void)
{
    int retval;
    dev_t dev;

    printk(KERN_INFO "module init success\n");

    // 1. 动态分配主次设备号
    retval = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
    if (retval < 0)
    {
        printk(KERN_ERR "Failed to allocate major number\n");
        goto fail_alloc_chrdev_region;
    }

    major_number = MAJOR(dev);
    printk(KERN_INFO "major number is: %d, minor number is: %d\n", major_number, MINOR(dev));

    // 2. 初始化 cdev 结构体并添加到内核
    cdev_init(&mydev, &fops);
    retval = cdev_add(&mydev, dev, 1);
    if (retval < 0)
    {
        printk(KERN_ERR "Failed to add cdev\n");
        goto fail_cdev_add;
    }

    // 3. 创建设备类
    my_class = class_create(THIS_MODULE, "my_class");
    if (IS_ERR(my_class))
    {
        printk(KERN_ERR "Failed to create class\n");
        retval = PTR_ERR(my_class);
        goto fail_class_create;
    }

    // 4.  申请设备,内核空间就会通知用户空间的udev 进行创建设备,驱动程序本身自己是创建不了文件的!
    my_device = device_create(my_class, NULL, dev, NULL, DEVICE_NAME);
    if (IS_ERR(my_device))
    {
        printk(KERN_ERR "Failed to create device\n");
        retval = PTR_ERR(my_device);
        goto fail_device_create;
    }

    printk(KERN_INFO "my_char_device: module loaded\n");
    return 0;



fail_device_create:
    class_destroy(my_class);
fail_class_create:
    cdev_del(&mydev);
fail_cdev_add:
    unregister_chrdev_region(dev, 1);
fail_alloc_chrdev_region:
    return retval;
}

static void __exit test_exit(void)
{
    dev_t dev = MKDEV(major_number, 0);
    if (my_device)
        device_destroy(my_class, dev);
    if (my_class)
        class_destroy(my_class);
    cdev_del(&mydev);
    unregister_chrdev_region(dev, 1);
    printk(KERN_INFO "my_char_device: module unloaded\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

效果图

当insmod 模块之后,在/dev目录下就可以查看由udev生成的设备文件了

在这里插入图片描述

如果使用cat 来查看设备文件,此时设备的文件操作结构体file_operations也会被触发,例如:

cat /dev/my_char_device 

在这里插入图片描述

cat 本质上就是读操作,相当于读取驱动程序,因此,Open read 会被触发,当读取完毕之后,就触发release操作

如果调用下方这个命令

echo 1 > /dev/my_char_device 

echo本质上就是写操作,相当于往驱动程序写值, 此时 open write会被触发,当写入完毕之后,就会触发release操作

在这里插入图片描述

当然,本次代码没有实现具体的读写逻辑,只是展示了cat 和echo的作用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1969641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言小练习(贰)

上机 计算n以内所有正奇数的和 ? n值通过键盘输入 #include <stdio.h>int main() {int sum 0;int i 1;int n;printf("请输入一个范围\n");scanf("%d",&n);do{if(i % 2 ! 0)//判断奇数{sum i;}i;}while(i < n);//限定条件printf("范围…

Python连接数据库:JDBC不是唯一选择!

你是否曾困惑于如何在Python中连接数据库?也许你听说过JDBC,但不确定它是否适用于Python?别担心,本文将为你揭开Python数据库连接的神秘面纱! 目录 JDBC vs Python数据库连接Python连接数据库的正确姿势Python的数据库连接方案为什么Python不直接使用ODBC或JDBC&#xff1f;…

正点原子imx6ull-mini-Linux驱动之阻塞IO和非阻塞IO实验(12)

阻塞和非阻塞 IO 是 Linux 驱动开发里面很常见的两种设备访问模式&#xff0c;在编写驱动的时候 一定要考虑到阻塞和非阻塞。本章我们就来学习一下阻塞和非阻塞 IO&#xff0c;以及如何在驱动程序中 处理阻塞与非阻塞&#xff0c;如何在驱动程序使用等待队列和 poll 机制。 1&…

2024-08-01升级问题记录:升级ArcGIS for Android

升级以离线方式引用的arcgis for Android &#xff0c;从10.2.7到 10.2.9 1、下载完整的aar库 地址&#xff1a;JFroghttps://esri.jfrog.io/ui/native/arcgis/com/esri/arcgis/android/arcgis-android/10.2.9/ 2、替换旧的库&#xff0c;由于10.2.7是以jar方式引用&#xf…

Win10系统,使用钉钉会议共享屏幕的时候,别人看到的都是全黑或全白屏幕

环境&#xff1a; Win10系统 钉钉7.6.0 问题描述&#xff1a; Win10系统&#xff0c;使用钉钉会议共享屏幕的时候&#xff0c;别人看到的都是全黑或全白屏幕 解决方案&#xff1a; 在Win10系统上使用钉钉会议共享屏幕出现全黑或全白的问题&#xff0c;这可能与系统升级兼容…

盘点那些实用的开发技术!!

敏感信息加密操作&#xff0c;让开发的系统更加的安全可靠&#xff01;&#xff01;Jasypt&#xff08;Java Simplified Encryption&#xff09;是一个开源的Java库&#xff0c;用于简化加密操作。https://mp.weixin.qq.com/s/sPBV8Ej46YJsElImodRjAQ每个Web开发都应了解的&…

一个前后端分离架构的低代码开发平台,支持微服务架构,支持开发SAAS项目(附源码)

前言 在当前的企业软件开发领域&#xff0c;开发者常常面临着代码重复性高、开发效率低、项目周期长等挑战。现有的软件解-决方案往往难以满足快速变化的市场需求&#xff0c;特别是在SAAS项目、企业信息管理系统&#xff08;MIS&#xff09;、内部办公系统&#xff08;OA&…

淘天笔试0508-选择题

kmp中&#xff0c;匹配失败时&#xff0c;主串不动。模式串回退&#xff1a;最长前后缀的前缀位置。 bcabbccabbacaa abbac 比较3次&#xff0c;a匹配成功 一直到匹配abb&#xff0c;比较了5次. c和a不匹配&#xff0c;比较6次。 abb最长前后缀长度为0&#xff0c;模式串回退到…

昇思25天学习打卡营第18天|MindSporeK近邻算法实现红酒聚类学习- MindSpore进行KNN实验

基于MobileNetv2的垃圾分类 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中。 1、实验目的 了解熟悉垃圾分类应用代码的编写&#xff08;Python语言&#xff09;&a…

[Bugku] web-CTF-alert

1.开启环境 2.根据题目得知flag就在这里&#xff0c;F12查看一下源码 3.发现编码&#xff0c;使用Unicode解码

Java数组反转,添加,排序,查找

目录 1.数组反转 2.数组添加 3.排序的介绍 冒泡排序法 4.查找 1.数组反转 定义一个数组然后将它的第一个元素与最后一个元素调换位置。 i为数组的长度减一&#xff0c;也就是最后一个元素的下标&#xff0c;j为0&#xff0c;也就是数组中第一个元素的下标&#xff0c;然后…

MATLAB(13)蚁狮群优化BP模型数据读取ALO_BP

一、前言 为了使用蚁狮优化算法&#xff08;Ant Lion Optimizer, ALO&#xff09;来优化BP&#xff08;反向传播&#xff09;神经网络模型中的参数&#xff08;如学习率、权重初始化等&#xff09;&#xff0c;我们首先需要定义蚁狮优化算法来搜索最优参数&#xff0c;然后使用…

【全网最全最详细】MYSQL 面试题大全(下)

目录 五十一、MYSQL主从复制的过程? 五十二、介绍一下InnbDB的数据页,和B+树的关系是什么? 五十三、MYSQL的驱动表是什么?如何选驱动表? 五十四、MYSQL的hash join是什么? 五十五、MYSQL执行大事务会存在什么问题? 五十六、什么是buffer pool? 五十七、buffer p…

ChinaJoy BTOB完美收官,Flat Ads高光时刻全回顾

7 月 26 日至 7 月 29 日, 2024 年第二十一届 ChinaJoy 在上海新国际博览中心隆重举行,其中 ChinaJoy BTOB 商务洽谈馆已于 7 月 28 日正式收官。ChinaJoy 作为全球数字娱乐领域兼具知名度与影响力的年度盛会,汇聚了来自世界各地的游戏和科技企业,展示其最新的科技成果和创新产…

AIGC第“五小龙”有苗条了?

纵观人类发展史&#xff0c;每一次世界性变革发生的背后无一不是靠生产力、生产工具支撑、驱动的。并且随着生产工具愈发先进话、科技化&#xff0c;相邻两场革命的时间间隔也在不断缩减&#xff0c;带来的社会、经济、政治等多方面的效应却是以超十倍、百倍、千倍……的增速在…

帮助网站提升用户参与度的5个WordPress插件

仅靠编写精彩的内容、设计精美的图像和创建简化的客户旅程不足以提高网站参与度。您需要让用户在首次访问后继续与您的网站互动并成为回访者&#xff0c;才能真正吸引您所追求的兴趣。 幸运的是&#xff0c;对于 WordPress 用户来说&#xff0c;有数百种工具可用于提高用户参与…

事件循环-宏任务与微任务

事件循环(eventloop) 同步和异步 JS是单线程的&#xff0c;也就是说&#xff0c; 同一时间只能做一件事&#xff0c;所有任务需要排队&#xff0c;前一个任务结束之后才会执行下一个任务。 作为浏览器脚本语言&#xff0c;JavaScript的主要用途是和用户互动以及操作DOM&#…

jQuery入门(一)jQuery基本语法

一、JQuery介绍 - jQuery 是一个 JavaScript 库。 - 所谓的库&#xff0c;就是一个 JS 文件&#xff0c;里面封装了很多预定义的函数&#xff0c;比如获取元素&#xff0c;执行隐藏、移动等&#xff0c;目的就 是在使用时直接调用&#xff0c;不 需要再重复定义&#xff0c;这…

【linux】【操作系统】内核之asm.s源码阅读

asm.s是Linux内核的一部分&#xff0c;主要负责处理各种类型的硬件异常和中断。 _divide_error 处理除法错误中断。当CPU执行除法指令时遇到除数为零的情况&#xff0c;会触发这个中断。此函数首先保存当前的寄存器状态&#xff0c;然后调用_do_divide_error函数来处理具体的错…

JavaEE 从入门到精通(二) ~SpringMVC 接收请求和设置响应

晚上好&#xff0c;愿这深深的夜色给你带来安宁&#xff0c;让温馨的夜晚抚平你一天的疲惫&#xff0c;美好的梦想在这个寂静的夜晚悄悄成长。 目录 前言 一、获取请求数据 1. 简单参数 1.1 请求行获取参数 a. 与查询参数的名称相同&#xff0c;底层会自动映射到形参中。 …