Linux驱动开发入门与hello驱动开发介绍
创始人
2025-05-29 21:57:14

文章目录

    • 1- Linux内核介绍
    • 2- 编写hello模块c文件
      • (1)Linux下编译运行
      • (2)imx6ull开发板运行


首先弄懂linux驱动的相关信息,然后介绍最简单的内核模块,让大家了解内核模块的编写、编译和使用。

1- Linux内核介绍

现在我们从一个比较高的高度来审视一下GNU/Linux操作系统的体系结构。如下图所示,最上面是用户(或应用程序)空间,这是用户应用程序执行的地方。用户空间之下是内核空间,Linux内核正是位于这里。C基础库(如glibc, eglibc, uclibc等)也属于应用程序空间,它提供了连接内核的系统调用接口,还提供了在用户空间应用程序和内核之间进行转换的机制。这点非常重要,因为内核和用户空间的应用程序使用的是不同的保护地址空间。每个用户空间的进程都使用自己的虚拟地址空间,而内核则占用单独的地址空间。

Linux 内核可以进一步划分成 3 层。最上面是系统调用接口(SCI,System Call Interface),它实现了一些基本的功能,例如 open()、read()、write()、close()等。系统调用接口之下是内核代码,可以更精确地定义为独立于体系结构的内核代码、这些代码是 Linux 所支持的所有处理器体系结构所通用的。在这些代码之下是依赖于体系结构的代码,构成了通常称为 BSP(Board SupportPackage)的部分。这些代码用作给定体系结构的处理器和特定于平台的代码。

在这里插入图片描述
Linux内核实现了很多重要的体系结构属性。在或高或低的层次上,内核被划分为多个子系统。Linux也可以看作是一个整体,因为它会将所有这些基本服务都集成到内核中。这与微内核的体系结构不同,后者会提供一些基本的服务,例如通信、I/O、内存和进程管理,更具体的服务都是插入到微内核层中的。每种内核都有自己的优点,不过这里并不对此进行讨论。随着时间的流逝,Linux内核在内存和 CPU 使用方面具有较高的效率,并且非常稳定。但是对于 Linux 来说,最为有趣的是在这种大小和复杂性的前提下,依然具有良好的可移植性。

在这里插入图片描述

系统调用接口

SCI 层提供了某些机制执行从用户空间到内核的函数调用。正如前面讨论的一样,这个接口依赖于体系结构,甚至在相同的处理器家族内也是如此。SCI 实际上是一个非常有用的函数调用多路复用和多路分解服务。
在应用层空间调用了系统调用,是在内核空间中运行了。我们可以time来看一下两个空间的运行时间:

wangdengtao@wangdengtao-virtual-machine:~$ time ping 4.2.2.2 -c 4
PING 4.2.2.2 (4.2.2.2) 56(84) bytes of data.
64 bytes from 4.2.2.2: icmp_seq=1 ttl=53 time=314 ms
64 bytes from 4.2.2.2: icmp_seq=2 ttl=53 time=297 ms
64 bytes from 4.2.2.2: icmp_seq=3 ttl=53 time=305 ms
64 bytes from 4.2.2.2: icmp_seq=4 ttl=53 time=287 ms--- 4.2.2.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3024ms
rtt min/avg/max/mdev = 287.472/300.647/313.502/9.614 msreal	0m3.287s//实际运行的时间
user	0m0.002s//用户空间运行的时间
sys	    0m0.000s//内核空间运行的时间

内存管理

内核所管理的另外一个重要资源是内存。为了提高效率,如果由硬件管理虚拟内存,内存是按照所谓的内存页方式进行管理的(对于大部分体系结构来说都是4KB)。Linux包括了管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。不过内存管理要管理的可不止4KB缓冲区。Linux提供了对4KB缓冲区的抽象,例如slab分配器。这种内存管理模式使用4KB缓冲区为基数,然后从中分配结构,并跟踪内存页使用情况,比如哪些内存页是满的,哪些页面没有完全使用,哪些页面为空,这样就允许该模式根据系统需要来动态调整内存使用。为了支持多个用户使用内存,有时会出现可用内存被消耗光的情况。由于这个原因,页面可以移出内存并放入磁盘中。这个过程称为交换,因为页面会被从内存交换到硬盘上, Linux系统中,被用于交换的分区叫swap分区,在windows系统下叫做虚拟内存。

文件系统

虚拟文件系统(VFS)是Linux内核中非常有用的一个方面,因为它为文件系统提供了一个通用的接口抽象。VFS在SCI和内核所支持的文件系统之间提供了一个交换层。在VFS上面,是对诸如open,close, read 和 write之类的函数的一个通用API抽象,在 VFS下面是文件系统抽象,它定义了上层函数的实现方式。
可以理解为有了文件系统只有我们对硬盘的操作操作路径就可以了。如果没有文件系统就没有路径的概念了。

网络管理
网络堆栈在设计上遵循模拟协议本身的分层体系结构。回想一下, Internet Protocol (IP)是传输协议(通常称为传输控制协议或TCP)下面的核心网络层协议,TCP上面是socket层,它是通过SCI进行调用的。socket是网络子系统的标准API。它为各种网络协议提供了一个用户接口,从原始帧访问到IP协议数据单元( PDU) ,再到TCP和User Datagram Protocol (UDP) , socket层提供了一种标准化的方法来管理连接,并在各个终点之间移动数据。

设备管理(驱动程序)
Linux内核中有大量代码都在设备驱动程序中,它们能够运转特定的硬件设备。Linux源码树提供了一个驱动程序子目录,这个目录又进一步划分为各种支持设备,例如 Bluetooth,l2C,serial 等,设备驱动程序的代码可以在/linux/drivers中找到。


2- 编写hello模块c文件

直接上代码(代码中注释解释的听清楚的,可以仔细看看):

#include 
#include 
#include /*定义了个名为 xxx_init 的驱动入口函数,并且使用了“__init”来修饰.*/
static __init int hello_init(void)
{/*在 Linux 内核中没有 printf 这个函数。printk 相当于 printf 的孪生兄妹,printf运行在用户态,printk运行在内核态。*/printk(KERN_ALERT "Hello world\n");return 0;
}/*定义了个名为 xxx_exit 的驱动出口函数,并且使用了“__exit”来修饰.*/
static __exit void hello_exit(void)
{printk(KERN_ALERT "Goodbye world\n");
}
/*
module_init函数用来向 Linux内核注册一个模块加载函数,参数 xxx_init就是需要注册的具体函数,当使
用“ insmod”命令加载驱动的时候 xxx_init这个函数就会被调 用。 module_exit()函数用来向 Linux内核
注册一个模块卸载函数,参数 xxx_exit就是需要注册的具体函数,当使用“ rmmod”命令卸载具体驱动的时
候 xxx_exit函数就会被调用。所以一般在xxx_init函数里进行一些驱动的初始化工作,在xxx_exit里面就
需要对驱动程序的卸载做一些回收工作。
*/
/*调用函数 module_init 来声明 xxx_init 为驱动入口函数,当加载驱动的时候 xxx_init函数就会被调用.*/
module_init(hello_init);
/*调用函数module_exit来声明xxx_exit为驱动出口函数,当卸载驱动的时候xxx_exit函数就会被调用.*/
module_exit(hello_exit);/*添加LICENSE和作者信息,是来告诉内核,该模块带有一个自由许可证;没有这样的说明,在加载模块的时内核会“抱怨”.*/
MODULE_LICENSE("Dual BSD/GPL");//许可 GPL、GPL v2、Dual MPL/GPL、Proprietary(专有)等,没有内核会提示.
MODULE_AUTHOR("WangDengtao");//作者
MODULE_VERSION("V1.0");//版本

(1)Linux下编译运行

Makefile:

KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
obj-m := hello.oall:$(MAKE) -C $(KERNAL_DIR) M=$(PWD) modulesclean:@rm -f *.o *.cmd *.mod *.mod.c@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f@rm -f .*ko.cmd .*.o.cmd .*.o.d@rm -f *.unsigned@rm -f *.ko

然后执行make:
在这里插入图片描述
警告
这里出现了警告,主要是gcc版本不匹配,应该不影响吧,继续操作。
可以看见我们的文件夹中多了很多文件,至于其他的文件,不要紧,最主要的看hello.ko文件。

wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver$ ls
hello.c  hello.ko  hello.mod  hello.mod.c  hello.mod.o  hello.o  Makefile  modules.order  Module.symvers

我们可以看见hello.ko文件。我们可以使用insmod和rmmod命令加载或者卸载驱动。

wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver$ sudo insmod hello.ko
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver$ sudo rmmod hello
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver$ sudo insmod hello.ko
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver$ dmesg | tail -3
dmesg: 读取内核缓冲区失败: 不允许的操作
wangdengtao@wangdengtao-virtual-machine:~/wangdengtao/driver$ sudo dmesg | tail -3
[ 8577.201090] Hello world
[ 8580.442205] Goodbye world
[ 8582.097892] Hello world

用lsmod命令查看当前linux内核安装了的内核模块:
在这里插入图片描述

(2)imx6ull开发板运行

首先需要在linux上安装内核源码树的目录,也就是arm架构的。这个就需要根据开发板自身需求去安装了。
Makefile:

KERNAL_DIR ?= /home/wangdengtao/imx6ull/imx6ull/bsp/kernel/linux-imx
PWD :=$(shell pwd)
obj-m := hello.oall:$(MAKE) -C $(KERNAL_DIR) M=$(PWD) modulesclean:@rm -f *.o *.cmd *.mod *.mod.c@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f@rm -f .*ko.cmd .*.o.cmd .*.o.d@rm -f *.unsigned@rm -f *.ko

将hello.ko文件移动到tftpboot目录下,方便开发板能够tftp获取到。

开发板获取到了我们就加载和卸载驱动来测试一下:

cp hello.ko /home/wangdengtao/wangdengtao/tftpboot/
root@igkboard:~# tftp -gr hello.ko 192.168.10.168
root@igkboard:~# ls
hello.koroot@igkboard:~# insmod hello.ko                
root@igkboard:~# dmesg | tail -1
[  820.486004] Hello worldroot@igkboard:~# lsmod 
Module                  Size  Used by
hello                  16384  0
rtl8188fu             999424  0
imx_rngc               16384  0
rng_core               20480  1 imx_rngc
secvio                 16384  0
error                  20480  1 secvioroot@igkboard:~# rmmod hello.ko 
root@igkboard:~# dmesg | tail -1
[  861.499208] Goodbye world

测试成功。


相关内容

热门资讯

【实验报告】实验一 图像的... 实验目的熟悉Matlab图像运算的基础——矩阵运算;熟悉图像矩阵的显示方法࿰...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
大模型落地比趋势更重要,NLP... 全球很多人都开始相信,以ChatGPT为代表的大模型,将带来一场NLP领...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
kuernetes 资源对象分... 文章目录1. pod 状态1.1 容器启动错误类型1.2 ImagePullBackOff 错误1....
STM32实战项目-数码管 程序实现功能: 1、上电后,数码管间隔50ms计数; 2、...
TM1638和TM1639差异... TM1638和TM1639差异说明 ✨本文不涉及具体的单片机代码驱动内容,值针对芯...
Qt+MySql开发笔记:Qt... 若该文为原创文章,转载请注明原文出处 本文章博客地址:https://h...
Java内存模型中的happe... 第29讲 | Java内存模型中的happen-before是什么? Java 语言...
《扬帆优配》算力概念股大爆发,... 3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.0...
CF1763D Valid B... CF1763D Valid Bitonic Permutations 题目大意 拱形排列࿰...
SQL语法 DDL、DML、D... 文章目录1 SQL通用语法2 SQL分类3 DDL 数据定义语言3.1 数据库操作3.2 表操作3....
文心一言 VS ChatGPT... 3月16号,百度正式发布了『文心一言』,这是国内公司第一次发布类Chat...
CentOS8提高篇5:磁盘分...        首先需要在虚拟机中模拟添加一块新的硬盘设备,然后进行分区、格式化、挂载等...
Linux防火墙——SNAT、... 目录 NAT 一、SNAT策略及作用 1、概述 SNAT应用环境 SNAT原理 SNAT转换前提条...
部署+使用集群的算力跑CPU密... 我先在开头做一个总结,表达我最终要做的事情和最终环境是如何的,然后我会一...
Uploadifive 批量文... Uploadifive 批量文件上传_uploadifive 多个上传按钮_asing1elife的...
C++入门语法基础 文章目录:1. 什么是C++2. 命名空间2.1 域的概念2.2 命名...
2023年全国DAMA-CDG... DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义...
php实现助记词转TRX,ET... TRX助记词转地址网上都是Java,js或其他语言开发的示例,一个简单的...
【分割数据集操作集锦】毕设记录 1. 按要求将CSV文件转成json文件 有时候一些网络模型的源码会有data.json这样的文件里...
Postman接口测试之断言 如果你看文字部分还是不太理解的话,可以看看这个视频,详细介绍postma...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
4、linux初级——Linu... 目录 一、用CRT连接开发板 1、安装CRT调试工具 2、连接开发板 3、开机后ctrl+c...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
天干地支(Java) 题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:...
SpringBoot雪花ID长... Long类型精度丢失 最近项目中使用雪花ID作为主键,雪花ID是19位Long类型数...
对JSP文件的理解 JSP是java程序。(JSP本质还是一个Servlet) JSP是&#...
【03173】2021年4月高... 一、单向填空题1、大量应用软件开发工具,开始于A、20世纪70年代B、20世纪 80年...
LeetCode5.最长回文子... 目录题目链接题目分析解题思路暴力中心向两边拓展搜索 题目链接 链接 题目分析 简单来说࿰...