开发者学堂课程【物联网开发-Linux 驱动开发实操演练:模块编写1】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/657/detail/10869
模块编写1
内容介绍
一、设备分类
二、字符设备驱动
三、模块编写
一、设备分类
1.引入
在上节课我们学习了驱动的基本概念,清楚了驱动的基本概念之后,进行对驱动设备分类的了解。
驱动就是驱动设备,所以在学习以前首先应该了解设备分为哪几类。以下是 linux将驱动设备分成的三个大类,其中每个设备应该所具有的设备驱动,形成三大类设备驱动对应三大类设备:
1)字符设备 ---->>字符设备驱动
2)网络设备 ----->>网络设备驱动
3)块设备 ----->>块设备驱动
在上节课的分析中,可以知道设备驱动是由内核设备统一管理来控制的,因此在写相应的驱动时,要遵循内核提供的框架来写。
2. linux驱动相关概念
驱动的种类:
(1)字符设备
① I/0传输过程中以字符为单位进行传输。
② 用户对字符设备发出读/写请求时,实际的硬件读/写操作一般紧
接着发生。
(表示字符设备的读写时同步的)
(2)块设备指的是存储设备,如:硬盘,u盘等存储设备,特点如下:
①块设备与字符相反,它的数据传输以块(内存缓冲)为单位传输,块指的是存储单位,一般为4k。
②用户对块设备读/写时,硬件上的读/写操作不会紧接着发生,即用户请求和硬件操作是异步的。
③磁盘类、闪存类等设备都封装成块设备。
(3)网络设备
网络设备时一类特殊的设备,它不像字符设备或块设备那样通过对应的设备文件访问,也不能直接通过 read 或 write 进行数据请求,而是通过 socket 接口函数进行访问。
简言之网络设备就是一个网卡的设备,所以我们需要对应给其写出网卡的设备。
(4)设备文件:
在 linux 中有一句话叫做“一切皆文件”,即我们在对具体的硬件文件进行操作的时候也可以通过文件进行操作,也就是说设备文件对应的就是一个设备,而对设备进行操作其实操作的就是对每个设备文件进行操作。
而设备文件具体怎么创建以及和驱动的关系是什么,以及怎么通过应用层去操作文件而间接的操作到我们设备,放在之后的章节中讲述。而在此传达的意思就是,所有的设备驱动都对应了一个设备驱动文件,如字符设备驱动对应一个字符设备驱动文件。
注意:网络涉笔驱动没有设备文件。
① 字符设备 ---->>字符设备驱动 ---->>字符设备文件
② 网络设备 ----->>网络设备驱动
③ 块设备 ----->>块设备驱动 ---->>块设备文件
二、字符设备驱动
之后开始学习各个设备驱动的使用方法。因为 linux 操作系统内核是由 c 语言编写的,而驱动时基于内核来编写的,所以驱动也是使用的 c 语言,而 c 语言时一门编译性语言,所以需要对编写的.c 文件进行编译。
所以还要学习如何编译 c 语言代码,虽然与之前的编译应用层代码使用的是同一个编译器,但方法还是有所不同。直至拿到编译后的驱动,就应该学习怎么使用驱动。
1. 驱动编写
2. 驱动编译
3. 驱动使用
三、模块编写
既然要编译c语言代码,首先回顾应用层代码编写的思路,首先是编写 main 函数入口:void main(),编译之后就会按照入口函数进入的顺序一条条执行,而内核中编写驱动并不是如此执行,在此需要使用到一个内核模块的知识点。
1. 内核模块编程简介
linux 内核整体结构非常庞大,包含的组件非常多。我们怎样选择性的把需要的部分包含在内核中呢?
Linux 内核抛弃把所有功能模块都编译到内核的做法,采用了模块化的方法将各组件灵活添加和删减,并且驱动模块还可以动态加载、删除。
意为 Linux 并不会将所有都加载到内核中去,而是当需要哪一部分再加载哪一部分。
使用模块的好处:
1)内核体积小:不需要的组件可以不编入内核
2)开发灵活:模块可以同普通软件一样,从内核中添加或删除
3 )平台无关、节省内存...
2.三要素
总而言之,模块的编写首先需要一个入口(加载)和一个出口(卸载),当驱动模块加载时候,就会从入口开始执行,而卸载则会从出口开始执行。
最后需要遵循 GPL 协议,所以还要一个 GPL 协议申明。
(1)入口(加载)
module_init(入口函数名);
(2)出口(卸载)
module_exit(卸载函数名);
(3)GPL 协议申明
MODULE_LTCENSE(“GPL”);
而三要素都是内核提出的,所以需要遵循内核提供的宏函数来编写。其中入口(加载)通过 module_init(入口函数名);函数来编写,出口(卸载)通过module_exit(卸载函数名);
函数来编写,GPL 协议申明MODULE_LTCENSE(“GPL”);函数来编写。
同时出口,入口参数要传入对应的函数名,以此使得在出口和入口各调用对应的函数。
到此三要素介绍完毕,之后我们将考虑的是入口,卸载函数具体是什么类型,需要怎样的参数等等。