LDD3 SCULL字符设备驱动搭建流程

SCULL字符设备驱动搭建流程

本节对实现LDD3书中前六章的scull字符设备驱动的步骤进行了梳理。搭建步骤依据书中所讲内容,逐步完善。

  • hello模块实现,设备驱动的基础入门

  • scull裸机模块的实现,字符设备驱动开发方式

  • pipe实现

  • access.c实现

我的实现在https://gitee.com/yuan-jing-hahahaha/linux_device_drivers_lab.git中,其中examples是官方例程,scull是我基于linux6.2的实现,scull/test是一些测试程序(文件忘记clean,如果文件太乱可以先make clean一下,使用时再make)。

1. hello

hello模块编写是为了让我们熟悉内核驱动编写的步骤。

  • 内核模块需要编写init和exit函数,用于内核模块在插入和删除的时候调用。

  • 内核模块传参用法

  • 内核模块编译Makefile编写

2. scull字符设备驱动

scull裸机模块涉及字符设备驱动的方方面面,可以让我们熟悉字符设备驱动的开发方法。

字符设备能够实现的前提

  • 申请设备号

  • 初始化cdev结构,其中包括operations操作的定义,cdev_add将设备号和某个cdev结构关联,使得通过设备号我们就可以找到对应的cdev结构

  • 创建设备节点,也就是用设备号创建字符设备文件,即将设备号cdev结构和文件绑定在一起。该文件是用户操作设备的入口,对文件进行open等操作时,它就可以通过cdev结构中的operation找到实际执行的函数,执行。

scull驱动编写步骤:

  • 编写init和exit,在init中完成设备号申请以及cdev结构初始化等,在scull裸机程序中还完成了对scull_device的初始化;exit做一些善后工作,注销掉之前有影响的操作

  • 编写open和close,这里这两个函数比较简单,没做什么重要工作

  • 编写read和write,这里是整个设备实现的核心逻辑,和驱动本身没什么关系,主要是逻辑设计,在和硬件有关的驱动中还会和硬件特性相关。

  • 编写ioctl,这里的ioctl实现了对quantum等参数的修改,实现功能很简单,主要为了使熟悉ioctl方法的使用。(现在用.unlocked_ioctl)

  • 编写llseek,这里可以实现文件指针的移动,可以实现追加写入。(在第六章的后面讲到,可以先看)

  • 编写Makefile

  • 编写scull_load和scull_unload,这两个文件主要为了插入删除模块和创建删除设备节点(用户能够操作设备的入口!

3. pipe实现

pipe是在阻塞IO教程中引入的,其实是另一种内存分配的实现,类似于管道,是一个循环队列,一端写,另一端读完就不保存了。该驱动有以下几个意义:

  • 管道的逻辑实现

  • 阻塞IO编写示例,没数据时读阻塞,数据满时写阻塞

  • 多个驱动模块的组织方式,pipe和scull是两个驱动,但是是通过一个驱动模块入口组织在一起的

编写步骤和scull类似,以下是几点注意和特点:

  • 设备号的设计:pipe并没有重新分配主设备号,而是使用scull的主设备号。因为他们都是属于scull设备,所以共用一个更成一个系统,更合理。

  • init中只做了设备号和cdev的相关处理,pipe设备初始化放在open中做。因为pipe设备关闭后就没了,其生命周期在于它打开的时候。scull因为生命周期在于模块装载在系统中的时候,只要模块没卸载,那个模块就还在,所以将scull设备初始化放在了init中。

  • ioctl的实现放在scull_unlocked_ioctl中一起实现。

  • pipe并没有导出自己的init和exit,而是放到scull的init中调用,这样的话,只需要insmod scull就可以达到启用pipe的作用。

4. access实现

access实现的是4种访问限制,各具特色,且其代码组织方式也很有趣。因为只需要在原scull的open和release上做修改,所以其他部分还是用的scull的函数实现。

  • 独享设备:每个进程互斥访问,用锁就可以实现。

  • 单用户访问:这里使用了内核提供的方法,保存并对比用户uid来实现限制,逻辑也比较简单。当然此教程适用于所有的单XX访问,只要有一个可以标识对比的变量。

  • 阻塞型单用户访问:加了等待队列,即在无法打开时并不是直接退出,而是会根据NONBLOCK来判断是否实现阻塞。

  • 打开时复制设备:这里我觉得是最有意思的,就是一种虚拟设备的实现技巧。什么意思呢,如果只用终端来作为区分,那么就是对于每一个终端来说,他们是自己独立的设备上进行操作,终端A读写的是一个设备,终端B读写的是一个设备,两个设备并不干扰,但是实际上只有一个设备,这种独立性是软件维护的。

    • 它有一个全局设备scull_c_device,其中的cdev部分很重要,这是实体的一个设备,用于通过设备节点能够找到内核函数入口。

    • 设备链表,链表每一项都是为每一个终端分配的一个设备。在open中会从链表中找到或者新分配一个设备对应于当前设备,赋值给filp->privation_data,在read等操作中会修改这个里的数据结构。可以看到每个终端有自己的数据结构,因而相互独立,这就是虚拟的多个设备

  • 代码组织:因为access中有四个驱动部分,对他们四个驱动写了一个init和exit,供scull_init调用。

5. 调试与竟态

这两部分分别是第四章和第五章的内容,我在初次看的时候感觉很懵懂,只是在记忆理解一些概念,很困难。后来发现这两部分是程序在设计中可以贯穿整个程序设计。因此,不必再初次阅读时即要求自己明了,但是也要看一遍对操作有个认识,然后在实际的使用中,你会不停的回顾一些小点,去理解它,即需要时你将理解它。在scull的调式中,有一部分和proc有关,这里我们也可以找到第四章的相关内容,只是我还没有实现,可以在想要实现的时候实现它。

6. 其他

一些方面的理解:

终于理解,其实内核模块插不插入这个事情并不重要,重要的是我们要用这个驱动,必须要执行一段内核代码,只不过插入这个动作可以触发这段代码的执行。还有,字符设备文件即文件只是一个节点,入口!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值