【linux驱动】字符驱动

本文详细解析了Linux内核中字符设备的开发过程,包括设备号的分配方式(动态与静态)、哈希表在内核管理中的应用、kgdb调试中查看设备哈希表,以及设备注册、创建节点和注销的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本次主要是理清字符驱动的加载过程, 分析几个重要函数,以及文件系统与之对应关系。

字符设备开发基本步骤:

  • 分配设备号
  • 设备注册
  • 创建设备节点 ( 用于和用户数据传输 )
  • 注销设备相关信息

设备号详解

在内核设备号是32位变量,高12位是主设备号 , 低20位是次设备号。
分配设备号的方式有两种:静态和动态分配, 常用动态分配
函数:

设备ID = 高12位是主设备号 + 低20位是次设备号 ;

动态分配函数:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

dev_t *dev 设备ID
unsigned baseminor 起始次设备号
unsigned count 设备数
const char *name 设备名

静态分配函数:
用于已知设备号的情况

int register_chrdev_region(dev_t from, unsigned count, const char *name)

dev_t from 已知的起始设备ID
unsigned count 设备数
const char *name 设备名

设备ID 和 主次设备号 相互转换宏:

#define MAJOR(dev)	((dev)>>8)   // 从设备ID获取主设备号
#define MINOR(dev)	((dev) & 0xff) // // 从设备ID获取次设备号
#define MKDEV(ma,mi)	((ma)<<8 | (mi)) // // 将主设备和次设备号转换成 设备ID

内核管理的字符设备数据结构

原理

内核管理字符设备是通过哈希表进行的,哈希表其实可以简单理解为 数组串起来的链表:

图片来源: https://www.jianshu.com/p/f8ea1b33ed5b

在这里插入图片描述

内核字符设备的哈希表原型:

名字:chrdevs , 长度是255 个,但是有时会看到257的主设备号,这种内核直接模255得到2 ,会把257加载到chrdevs[2]的位置,然后按次设备号大小排在next里。

//linux-imx-4.1.15-2.1.0-g3dc0a4b-v2.7/fs/char_dev.c

#define CHRDEV_MAJOR_HASH_SIZE	255

static struct char_device_struct {
	struct char_device_struct *next; // 同一个主设备号的链表
	unsigned int major; // 主设备号
	unsigned int baseminor; // 次设备号
	int minorct; // 个数
	char name[64]; 
	struct cdev *cdev;		/* 字符设备在文件系统的目录节点,以及各种操作接口 */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

在文件系统表现

如下图第一列是主设备号, 第二列是设备名,获取注册的所有字符设备cat /proc/devices ,将通过kgdb调试实时查看内核管理的字符设备哈希表。
在这里插入图片描述

kgdb调试打印内核哈希表:

在函数 chrdev_show 处打断点,当函数执行到这儿时开始打印哈希表

  • 主设备号4
    通过文件系统可知有两个设备 /dev/vc/0 和 、tty , 以下是动态打印的内核哈希表
    baseminor = 0 的是第一个设备 ,baseminor = 1 的是第二个设备 tty, tty有设备63个 ,next等于0说明后面没有设备。
    在这里插入图片描述

  • 主设备号5
    通过文件系统可知有3个设备 /dev/tty 、/dev/console 、/dev/ptmx , 以下是动态打印的内核哈希表
    在这里插入图片描述

分配设备号及注册

 /* 1、构建设备号 */
 if (chrdev.major)
 {
     chrdev.devid = MKDEV(chrdev.major, 0);
     register_chrdev_region(chrdev.devid, CHARDEV_CNT, "chrdev_register");
 }
 else
 {
     alloc_chrdev_region(&chrdev.devid, 5, CHARDEV_CNT, "chrdev_register");
     chrdev.major = MAJOR(chrdev.devid);
 }

 /* 2、注册设备 */
 cdev_init(&chrdev.cdev, &chrdev_ops);
 cdev_add(&chrdev.cdev, chrdev.devid, CHARDEV_CNT);

如图分成两步, 第一步获取设备号,第二步正式注册设备。
注册的设备号在文件系统 /proc/devices 下可以查看 , 在内核注册设备实际就是申请 cdev 内存空间,然后将其挂载到字符设备节点下。
在这里插入图片描述

创建设备节点

源码:

       /* 3、创建类 */
        chrdev.class = class_create(THIS_MODULE, "chrdev_class");
        if (IS_ERR(chrdev.class)) {
            return PTR_ERR(chrdev.class);
        }

        /* 4、创建设备节点文件 */
        chrdev.device = device_create(chrdev.class, NULL, chrdev.devid, NULL, "chrdev_device");

        if (IS_ERR(chrdev.device))
        {
            return PTR_ERR(chrdev.device);
        }

所谓的设备节点主要用来传输内核到用户空间的数据 , 节点存在于文件系统 /dev/ 下,创建节点前提还得有对应的类,所以以上先创建类在创建节点。
设备类:
在这里插入图片描述

设备节点:
在这里插入图片描述

注销设备

注销设备需按照以下方式进行, 主要是将分配的设备号从内核删除,将cdev 从文件系统删除, 以及删除设备节点。

    cdev_del(&chrdev.cdev);/*  删除cdev */
    unregister_chrdev_region(chrdev.devid, CHARDEV_CNT); /* 注销设备号 */

    device_destroy(chrdev.class, chrdev.devid);
	class_destroy(chrdev.class);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值