linux设备驱动归纳总结(一):内核驱动的相关基础概念
2010-12-01 16:38
295 查看
linux设备驱动归纳总结(一):内核的相关基础概念
1.
内核与
linux
设备驱动的作用与关系
内核:用于管理软硬件资源,并提供运行环境。如分配
4G
虚拟空间等。
linux
设备驱动:是连接硬件和内核之间的桥梁。
linux
系统按个人理解可按下划分:
1).
应用层:包括
POSIX
接口,
LIBC
,图形库等,用于给用户提供访问内核的接口。属于用户态
ARM
运行在用户模式
(usr)
或者系统模式
(sys)
下。
2).
内核层:应用程序调用相关接口后,会通过系统调用,执行
SWI
指令切换
ARM
的工作模式到超级用户
(svc)
模式下,根据用户函数的要求执行相应的操作。
3).
硬件层:硬件设备,当用户需要操作硬件时,内核会根据驱动接口操作硬件设备
图结构如下:
举一个相对比较邪恶的类比,某牛人跟我说的:
在深圳的酒店经常会在门缝看到一些卡片,上面说可以通过打电话送货上门提供某中服务。
===============================================
2.
内核代码树介绍
linux-2.6.29
|-
arch
:
包含和硬件体系结构相关的代码
|-
block
:
硬盘调度算法,不是驱动
|-
firmware
:
固件
|-
Documentation
:
标准官方文档
|-
dirver
: linux
设备驱动
|-
fs
:
内核所支持的文件体系
|-
include
:头文件。
linux/module.h linux/init.h
常用库。
|-
init
:库文件代码,
C
库函数在内核中的实现。
init/main.c ->start_kernel->
内核执行第一条代码
|-
ipc
:
进程件通信
|-
mm
:内存管理
|-
kernel
:
内核核心部分,包括进程调度等
|-
net
:网络协议
|-
sound
:
所有音频相关
其中,跟设备驱动有关并且经常查阅的文件夹有:
init
include : linux, asm-arm
drivers:
arch:
===============================================
3.
内核补丁:
补丁一般都是基于某个版本内核生成的,用于升级旧内核。
打补丁需要注意:
1.
对应版本的补丁只能用于对应版本的内核。
2.
如果在已打补丁的内核再打补丁,需要先卸载原来补丁。
打补丁的方法:
1.
制作补丁:
diff -Nur linux-2.6.30/ linux-2.6.30.1/ > linux-2.6.30.1.patch
//N
为新加的文件全部修改
//linux-2.6.30
旧版本
//linux-2.6.30.1
新版本
//
目标补丁
2.
打补丁:
cd linux-2.6.30
//
!!注意在原文件夹的目录中打补丁
patch -p1 < ../linux-2.6.30.1.patch
//-p1
是忽略一级目录
3.
恢复:
cd linux-2.6.30
//
!!注意在原文件夹的目录中打补丁
patch -R < ../linux-2.6.30.1.patch
//
撤销补丁
===============================================
4.
内核中的
Makefile
:
对于内核,
Makefile
分为
5
类:
Documentation/kbuild/makefiles.txt
描述如下:
简单来说,编译内核会执行以下事情。
1.make menuconfig
1.1
拷贝一个对应体系结构的配置文件到主目录下并改名为
.config
,这样就在
make
menuconfig
生成的图形配置中已经有了一些默认的配置,减少用户的劳动量。
1.2
从内核顶层目录的
Makefile
决定编译的体系结构
(ARCH).
编译工具
(CROSS_COMPILE)
和需要进去编译的目录。
1.3
根据总
Makefile
的
ARCH
变量,进入相应体系结构的目录,读取
arch/$
(ARCH)/Makefile,
决定对应的体系结构下还有哪些目录需要编译。
1.4
根据
arch/$(ARCH)/Makefile
,一个一个地递归进入指定的目录下调用该目录下的
makefile
,并根据目录下的
Kconfig
生成配置界面并由用户决定将该文件编译成模块还是编译进内核。
1.5
配置完毕后保存退出,会更改原来的
.config
的内容。
2.make
1.1
将生成的
.config
去掉注释,新建一份配置文件,文件名为
include/config/auto.conf
。
1.2
根据配置文件的要求,将需要编译的文件的各个子目录下生成一个
.o
或者
.a
文件,然后由总
Makefile
指定的连接脚本
arch/$(ARCH)/kernel/vmlinux.lds
生成
vmlinux
,并通过压缩变成
bzImage
,或者按要求在对应的子目录下编译成模块。
但是,具体是怎么生成配置文件的呢?
注:我使用的内核是被修改过的,可能有些地方和原内核不一样,如我内核里面
$(ARCH)
写成
$(SRCARCH)
。还有在文件中的行数和原内核不一致,但这些不影响分析,搜索一下就出来了。
1.
在总
Makefile
中,根据以下语句进入需要编译的目录
这样,就根据了体系结构决定了需要进去编译的目录了。
2.
在总
Makefile
中包含的目录还是不够的,内核还需要根据对应的
CPU
体系架构,决定还需要将哪些子目录将要编译进内核,在总
Makefile
中进去读取相应体系结构的
Makefile
:
arch/$(SRCARCH)/Makefile
。
在总
Makefile
和体系架构下的
arch/(SRCARCH)/Makefile
中包含的子目录会根据该目录下的
Makefile
的要求编译成模块还是编译进内核,当然也可以不编译。
如在
arch/arm/Makefile
下:
其中,
y
表示编译成模块,
m
表示编译进内核
(
上面没有,因为默认情况下
ARM
全部编译进内核
)
,但
$(CONFIG_OPROFILE)
又是什么呢?
这些是根据用户在
make menuconfig
中设置后,生成的值赋给了
CONFIG_OPROFILE
。这是由各子目录下的
Kconfig
提供选项功用户选择并配置。如
arch/arm/Kconfig
。
另外有些配置会根据
arch/$(ARCH)/Kconfig
文件通过
Kconfig
的语法
source
读取各个包含的子目录
Kconfig
来生成一个配置界面。每个
Makefile
目录下都有一个对应
Kconfig
文件,用于生成配置界面来给用户决定内核如何配置。
总结
Kconfig
的作用:
2.1.
在
make menuconfig
下可以配置选项
;
2.2.
在
.config
中确定
CONFIG_XXX
的的值。
3.
只是读取以上的两个
Makefile
还是不够了,内核还会把包含的子目录一层一层的读取它里面的
Makefile
和
Kconfig
。
假设我现在配置内核
1.
最简单的方法,直接修改子目录的
Makefile
如在我要取消
s3c2440
的时钟
(
当然这是必须要开的,只是举例
)
。
可以直接修改
arch/arm/mach-s3c2440/Makefile
将
obj-$(CONFIG_CPU_S3C2440)
+= clock.o
改为
obj-
+= clock.o
也可以编译成模块:
obj-m
+= clock.o
2.
当然有更方便的通过图形界面,
make menuconfig
,接下来我实现一下如何将一个选项加入到图形配置界面中。
2.1.
进入内核目录
cd linux-2.6.29
2.2.
在
driver
目录下模拟一个名为
test1
驱动的文件夹
mkdir driver/test1
2.3.
在
test1
目录下随便写个
C
文件
cd driver/test1
vim test1.c
2.4.
在目录下编写一个简单的
Makefile
vim Makefile
CONFIG_TEST1
是决定
test1
是否编译进内核或者编译成模块的。这就通过
Kconfig
由用户在
make menuconfig
中选择。
2.5.
所以还要在目录下写一个
Kconfig
vim Kconfig
说白了,就是在图形配置的
driver
下多了一个配置选项,用户配置后将
CONFIG_TEST1
的值存放在
.config
中,
Makefile
通过读取
.config
的去注释版
autoconf
读取到
CONFIG_TEST
的值,再进行编译。但是,以上几步还不能达到目的,因为虽然在总
Makefile
中已经包含了目录
driver,
但是
driver
目录的
Makefile
中并没有包含
test
目录。因此需要在
driver/Makefile
中添加:
2.6.vim driver/Makefile
再最后加上一句:
虽然
Makefile
中已经包含了,但这样还是不行。因为当需要配置
ARM
时,
ARM
结构下的
Kconfig
并没有包含
test
的
Kconfig
。这样的话就不会出现在图形配置界面中,因此在
arch/arm/Kconfig
中添加语句。
2.7.vim arch/arm/Kconfig
大功告成!
这样,
make menuconfig
界面写的
Driver Devices
下就多了一个
"test1 driver here"
的目录,里面有一个配置选项
"xiaobai test1 driver"
。
Kconfig
文件的语法在
documentation/kbuild/kconfig-language.txt
文件中有详细的讲解,上面我只是简单实现了一下
,
但都是皮毛。
==============================================
5.
编译内核
编译内核很简单,只需要配置完毕后执行
make
命令,将指定的文件编译进内核
bzImage
或者编译成模块。
make = make bzImage + make modules
因此如果值编译内核,即只编译配置文件中
-y
选项,可以直接用命令
make bzImage
如果值编译模块,即只编译配置文件中的
-m
选项,可以之直接使用命令
make modules
模块可以编译当然也可以清除,使用命令
make modules clean
如果只想单独编译一个模块,可以使用命令
make M=drivers/test/ modules
//
只单独编译
drivers/test
中的
.ko
make M=drivers/test/ modules clean
//
清除
上面的是在内核目录下的操作,但当我写驱动时,我并不可能在内核目录下编
写,但我编译时却要依赖内核中的规则和
Makefile
,所以就有了以下的方法,
同时这也是一般的编写驱动时
Makefile
的格式。
指定内核
Makefile
并单独编译
make -C /root/linux-2.6.29 M=`pwd` module
make -C /root/linux-2.6.29 M=`pwd` module clean
//-C
指定内核
Makefile
的路径,可以使用相对路径。
//-M
指定要编译的文件的路径,同样课使用相对路径。
编译生成的模块可以指定存放的目录
make -C /root/linux-2.6.29 M=`pwd` modules_install INSTALL_MOD_PATH=/nfsroot
=======================================================================
以上都是个人理解,如内核的
Makefile
配置和编译时一个复杂的过程,我简单地描述了一下,并不保证一定正确。
错误地方,请指正。
1.
内核与
linux
设备驱动的作用与关系
内核:用于管理软硬件资源,并提供运行环境。如分配
4G
虚拟空间等。
linux
设备驱动:是连接硬件和内核之间的桥梁。
linux
系统按个人理解可按下划分:
1).
应用层:包括
POSIX
接口,
LIBC
,图形库等,用于给用户提供访问内核的接口。属于用户态
ARM
运行在用户模式
(usr)
或者系统模式
(sys)
下。
2).
内核层:应用程序调用相关接口后,会通过系统调用,执行
SWI
指令切换
ARM
的工作模式到超级用户
(svc)
模式下,根据用户函数的要求执行相应的操作。
3).
硬件层:硬件设备,当用户需要操作硬件时,内核会根据驱动接口操作硬件设备
图结构如下:
---------------------------------------------------------------------- POSIX LIBS 图形库 -------->用户态(usr sys) ---------------------------SWI---------------------------------------- 系统调用:用户态进入内核态的唯一途径 -------->内核态(svc) -------------------------------------------------- | 系统调用接口 | -------------------------------------------------- ----------------------------------------------------- ------------ | 网络协议 内存管理 文件系统 | 注意: | 进程管理 设备控制 等 | 内核并不会主动提供 ----------------------------------------------------- ------------- 服务,只有当用户态 或者硬件有要求时, ----------------------------------------------------- -- 内核才会执行相应的 | 驱动接口 | 操作。 ----------------------------------------------------- -- ---------------------------------------------------------------------- 内核可以根据用户要求操作硬件设备。 设备也可以通过中断通知内核。 ------------------------------------------------------- | 硬件设备 | ------------------------------------------------------- |
在深圳的酒店经常会在门缝看到一些卡片,上面说可以通过打电话送货上门提供某中服务。
---------------------------------------------------------------------- 在门缝收到的某卡片,提供了电话号码 -------->客户 ---------------------------打电话---------------------------------------- 打电话:客户寻求某服务的途径 -------->服务商 -------------------------------------------------- | 接电话处 | -------------------------------------------------- ----------------------------------------------------- ------------ | 提供上门服务,只要一个电话, | 注意: | 30分钟内即到。 | 服务商并不会主动提 ----------------------------------------------------- ------------- 供服务,只有当客户 或者有要求时, 服务商 ----------------------------------------------------- -- 才会接送工作者到指定 | 联系服务工作者 | 地点。 ----------------------------------------------------- -- ---------------------------------------------------------------------- 服务商可以根据用户要求接送指定工作者。 服务工作者有需要时也可以要求服务商帮忙安排工作。 ------------------------------------------------------- | 某服务工作者 | ------------------------------------------------------- |
2.
内核代码树介绍
linux-2.6.29
|-
arch
:
包含和硬件体系结构相关的代码
|-
block
:
硬盘调度算法,不是驱动
|-
firmware
:
固件
|-
Documentation
:
标准官方文档
|-
dirver
: linux
设备驱动
|-
fs
:
内核所支持的文件体系
|-
include
:头文件。
linux/module.h linux/init.h
常用库。
|-
init
:库文件代码,
C
库函数在内核中的实现。
init/main.c ->start_kernel->
内核执行第一条代码
|-
ipc
:
进程件通信
|-
mm
:内存管理
|-
kernel
:
内核核心部分,包括进程调度等
|-
net
:网络协议
|-
sound
:
所有音频相关
其中,跟设备驱动有关并且经常查阅的文件夹有:
init
include : linux, asm-arm
drivers:
arch:
===============================================
3.
内核补丁:
补丁一般都是基于某个版本内核生成的,用于升级旧内核。
打补丁需要注意:
1.
对应版本的补丁只能用于对应版本的内核。
2.
如果在已打补丁的内核再打补丁,需要先卸载原来补丁。
打补丁的方法:
1.
制作补丁:
diff -Nur linux-2.6.30/ linux-2.6.30.1/ > linux-2.6.30.1.patch
//N
为新加的文件全部修改
//linux-2.6.30
旧版本
//linux-2.6.30.1
新版本
//
目标补丁
2.
打补丁:
cd linux-2.6.30
//
!!注意在原文件夹的目录中打补丁
patch -p1 < ../linux-2.6.30.1.patch
//-p1
是忽略一级目录
3.
恢复:
cd linux-2.6.30
//
!!注意在原文件夹的目录中打补丁
patch -R < ../linux-2.6.30.1.patch
//
撤销补丁
===============================================
4.
内核中的
Makefile
:
对于内核,
Makefile
分为
5
类:
Documentation/kbuild/makefiles.txt
描述如下:
50 The Makefiles have five parts: 51 52 Makefile 总Makefile,控制内核的编译 53 .config 内核配置文件,配置内核时生成,如make menuconfig后 54 arch/$(ARCH)/Makefile 对应体系结构的Makefile 55 scripts/Makefile.* Makefile共用的规则,如图形配置界面。 56 kbuild Makefiles 各子目录下的Makefile,被上层的Makefile调用 |
1.make menuconfig
1.1
拷贝一个对应体系结构的配置文件到主目录下并改名为
.config
,这样就在
make
menuconfig
生成的图形配置中已经有了一些默认的配置,减少用户的劳动量。
1.2
从内核顶层目录的
Makefile
决定编译的体系结构
(ARCH).
编译工具
(CROSS_COMPILE)
和需要进去编译的目录。
1.3
根据总
Makefile
的
ARCH
变量,进入相应体系结构的目录,读取
arch/$
(ARCH)/Makefile,
决定对应的体系结构下还有哪些目录需要编译。
1.4
根据
arch/$(ARCH)/Makefile
,一个一个地递归进入指定的目录下调用该目录下的
makefile
,并根据目录下的
Kconfig
生成配置界面并由用户决定将该文件编译成模块还是编译进内核。
1.5
配置完毕后保存退出,会更改原来的
.config
的内容。
2.make
1.1
将生成的
.config
去掉注释,新建一份配置文件,文件名为
include/config/auto.conf
。
1.2
根据配置文件的要求,将需要编译的文件的各个子目录下生成一个
.o
或者
.a
文件,然后由总
Makefile
指定的连接脚本
arch/$(ARCH)/kernel/vmlinux.lds
生成
vmlinux
,并通过压缩变成
bzImage
,或者按要求在对应的子目录下编译成模块。
但是,具体是怎么生成配置文件的呢?
注:我使用的内核是被修改过的,可能有些地方和原内核不一样,如我内核里面
$(ARCH)
写成
$(SRCARCH)
。还有在文件中的行数和原内核不一致,但这些不影响分析,搜索一下就出来了。
1.
在总
Makefile
中,根据以下语句进入需要编译的目录
470 # Objects we will link into vmlinux / subdirs we need to visit 471 init-y := init/ 472 drivers-y := drivers/ sound/ firmware/ 473 net-y := net/ 474 libs-y := lib/ 475 core-y := usr/ 476 endif # KBUILD_EXTMOD 639 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ #另外还有一个体系相关的arch目录 529 include $(srctree)/arch/$(SRCARCH)/Makefile |
2.
在总
Makefile
中包含的目录还是不够的,内核还需要根据对应的
CPU
体系架构,决定还需要将哪些子目录将要编译进内核,在总
Makefile
中进去读取相应体系结构的
Makefile
:
arch/$(SRCARCH)/Makefile
。
在总
Makefile
和体系架构下的
arch/(SRCARCH)/Makefile
中包含的子目录会根据该目录下的
Makefile
的要求编译成模块还是编译进内核,当然也可以不编译。
如在
arch/arm/Makefile
下:
187 # If we have a machine-specific directory, then include it in the build. 188 core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ 189 core-y += $(machdirs) $(platdirs) 190 core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/ 191 core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) 192 core-$(CONFIG_VFP) += arch/arm/vfp/ 193 194 drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ 195 196 libs-y := arch/arm/lib/ $(libs-y) |
y
表示编译成模块,
m
表示编译进内核
(
上面没有,因为默认情况下
ARM
全部编译进内核
)
,但
$(CONFIG_OPROFILE)
又是什么呢?
这些是根据用户在
make menuconfig
中设置后,生成的值赋给了
CONFIG_OPROFILE
。这是由各子目录下的
Kconfig
提供选项功用户选择并配置。如
arch/arm/Kconfig
。
另外有些配置会根据
arch/$(ARCH)/Kconfig
文件通过
Kconfig
的语法
source
读取各个包含的子目录
Kconfig
来生成一个配置界面。每个
Makefile
目录下都有一个对应
Kconfig
文件,用于生成配置界面来给用户决定内核如何配置。
总结
Kconfig
的作用:
2.1.
在
make menuconfig
下可以配置选项
;
2.2.
在
.config
中确定
CONFIG_XXX
的的值。
3.
只是读取以上的两个
Makefile
还是不够了,内核还会把包含的子目录一层一层的读取它里面的
Makefile
和
Kconfig
。
假设我现在配置内核
1.
最简单的方法,直接修改子目录的
Makefile
如在我要取消
s3c2440
的时钟
(
当然这是必须要开的,只是举例
)
。
可以直接修改
arch/arm/mach-s3c2440/Makefile
12 obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o 13 obj-$(CONFIG_CPU_S3C2440) += irq.o 14 obj-$(CONFIG_CPU_S3C2440) += clock.o 15 obj-$(CONFIG_S3C2440_DMA) += dma.o |
obj-$(CONFIG_CPU_S3C2440)
+= clock.o
改为
obj-
+= clock.o
也可以编译成模块:
obj-m
+= clock.o
2.
当然有更方便的通过图形界面,
make menuconfig
,接下来我实现一下如何将一个选项加入到图形配置界面中。
2.1.
进入内核目录
cd linux-2.6.29
2.2.
在
driver
目录下模拟一个名为
test1
驱动的文件夹
mkdir driver/test1
2.3.
在
test1
目录下随便写个
C
文件
cd driver/test1
vim test1.c
1 void foo() 2 { 3 ; 4 } |
在目录下编写一个简单的
Makefile
vim Makefile
obj-$(CONFIG_TEST1) += test1.o |
是决定
test1
是否编译进内核或者编译成模块的。这就通过
Kconfig
由用户在
make menuconfig
中选择。
2.5.
所以还要在目录下写一个
Kconfig
vim Kconfig
1 menu "test1 driver here" 2 config TEST1 3 bool "xiaobai test1 driver" 4 help 5 This is test1 6 endmenu |
driver
下多了一个配置选项,用户配置后将
CONFIG_TEST1
的值存放在
.config
中,
Makefile
通过读取
.config
的去注释版
autoconf
读取到
CONFIG_TEST
的值,再进行编译。但是,以上几步还不能达到目的,因为虽然在总
Makefile
中已经包含了目录
driver,
但是
driver
目录的
Makefile
中并没有包含
test
目录。因此需要在
driver/Makefile
中添加:
2.6.vim driver/Makefile
再最后加上一句:
104 obj-$(CONFIG_OF) += of/ 105 obj-$(CONFIG_SSB) += ssb/ 106 obj-$(CONFIG_VIRTIO) += virtio/ 107 obj-$(CONFIG_STAGING) += staging/ 108 obj-y += platform/ 109 obj-$(CONFIG_TEST1) += test1/ //添加这句 |
Makefile
中已经包含了,但这样还是不行。因为当需要配置
ARM
时,
ARM
结构下的
Kconfig
并没有包含
test
的
Kconfig
。这样的话就不会出现在图形配置界面中,因此在
arch/arm/Kconfig
中添加语句。
2.7.vim arch/arm/Kconfig
1230 menu "Device Drivers" 1231 1232 source "drivers/base/Kconfig" ….............................................. 1328 source "drivers/staging/Kconfig" 1329 1330 source "drivers/test1/Kconfig" 1331 1332 endmenu |
这样,
make menuconfig
界面写的
Driver Devices
下就多了一个
"test1 driver here"
的目录,里面有一个配置选项
"xiaobai test1 driver"
。
Kconfig
文件的语法在
documentation/kbuild/kconfig-language.txt
文件中有详细的讲解,上面我只是简单实现了一下
,
但都是皮毛。
==============================================
5.
编译内核
编译内核很简单,只需要配置完毕后执行
make
命令,将指定的文件编译进内核
bzImage
或者编译成模块。
make = make bzImage + make modules
因此如果值编译内核,即只编译配置文件中
-y
选项,可以直接用命令
make bzImage
如果值编译模块,即只编译配置文件中的
-m
选项,可以之直接使用命令
make modules
模块可以编译当然也可以清除,使用命令
make modules clean
如果只想单独编译一个模块,可以使用命令
make M=drivers/test/ modules
//
只单独编译
drivers/test
中的
.ko
make M=drivers/test/ modules clean
//
清除
上面的是在内核目录下的操作,但当我写驱动时,我并不可能在内核目录下编
写,但我编译时却要依赖内核中的规则和
Makefile
,所以就有了以下的方法,
同时这也是一般的编写驱动时
Makefile
的格式。
指定内核
Makefile
并单独编译
make -C /root/linux-2.6.29 M=`pwd` module
make -C /root/linux-2.6.29 M=`pwd` module clean
//-C
指定内核
Makefile
的路径,可以使用相对路径。
//-M
指定要编译的文件的路径,同样课使用相对路径。
编译生成的模块可以指定存放的目录
make -C /root/linux-2.6.29 M=`pwd` modules_install INSTALL_MOD_PATH=/nfsroot
=======================================================================
以上都是个人理解,如内核的
Makefile
配置和编译时一个复杂的过程,我简单地描述了一下,并不保证一定正确。
错误地方,请指正。
相关文章推荐
- inux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一)内核的相关基础概念【转】
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- 【Linux开发】linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(一):内核的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念
- linux设备驱动归纳总结(二):模块的相关基础概念