转:Linux设备驱动开发(1):内核基础概念
一.linux设备驱动的作用
内核:用于管理软硬件资源,并提供运行环境。如分配4G虚拟空间等。
linux设备驱动:是连接硬件和内核之间的桥梁。
linux系统按个人理解可按下划分:
应用层:包括POSIX接口,LIBC,图形库等,用于给用户提供访问 内核的接口。属于用户态,ARM运行在用户模式(usr)或 者系统模式(sys)下。
内核层:应用程序调用相关接口后,会通过系统调用,执行SWI指 令切换ARM的工作模式到超级用户(svc)模式下,根据用 户函数的要求执行相应的操作。
硬件层:硬件设备,当用户需要操作硬件时,内核会根据驱动接口 操作硬件设备
图结构如下:
举一个相对比较邪恶的类比:
在深圳的酒店经常会在门缝看到一些卡片,上面说可以通过打电话送货上门提供某中服务。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二.内核代码树介绍
linux-2.6.29
|-arch : 包含和硬件体系结构相关的代码
|-block : 硬盘调度算法,不是驱动
|-firmware : 固件,如BOIS
|-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:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三.内核补丁:
补丁一般都是基于某个版本内核生成的,用于升级旧内核。
打补丁需要注意:
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 //撤销补丁
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四.内核中的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一般的,我们会拷贝一个对应体系结构的配置文件到主目录下并改名为 .config,这样就在make menuconfig生成的图形配置中 已经有了一些默认的配置,减少用户的劳动量。不过这一步不做也没关系的。
2.make menuconfig
2.1、由总Makefile决定编译的体系结构(ARCH). 编译工具(CROSS_COMPILE),并知道需要进去哪些内核根下的哪些目录进行编译。
2.2、由arch/$(ARCH)/Makefile,决定arch/$(ARCH)下还有 的哪些目录和文件需要编译。
2.3、知道了需要编译的目录后,递归的进入哪些目录下,读取每一个Kconfig的信息,生成了图形配置的界面。
2.4、通过我们在图形配置界面中选项为[*]、[M]或者[]。
2.5、保存并退出配置,会根据配置生成一份新的配置文件.config,并在同时生成include/config/auto.conf(这是.config的去注释版)。文件里面保存着CONFIG_XXXX等变量应该取y还是取m。
3.make
3.1、根据Makefile包含的目录和配置文件的要求,进去个子目录进行编译,最后会在各子目录下 生成一个.o或者.a文件,然后总Makefile指定的连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过 压缩编程bzImage,或者按要求在对应的子目录下编译成 模块。。
但是,具体是怎么生成配置文件的呢?
注:我使用的内核是2.6.29。
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/
上面说明了,根目录下的init、driver、sound、firmware、net、lib、usr等目录,在编译时都会进去读取目录下的Makefile并进行编译。
2.在总Makefile中包含的目录还是不够的,内核还需要根据对应的CPU体系架构,
决定还需要将哪些子目录将要编译进内核。在总Makefile中有一个语句:
529 include $(srctree)/arch/$(SRCARCH)/Makefile //在这里,我定义SRCARCH = arm
可以看出,在总Makefile中进去读取相应体系 结构的Makefile->arch/$(SRCARCH)/Makefile。
arch/$(SRCARCH)/Makefile中指定arch/$(SRCARCH)路径下的哪些子目录需要被编译。
在 arch/arm/Makefile 下:
95 head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
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)
上面看到,指定需要进入arch/arm/kernel/、arch/arm/mm/、arch/arm/common/ 等目录编译,至于core-y、 core-$(CONFIG_FPE_NWFPE)这些是什么东西呢?
其中,y表示编译成模块,m表示编译进内核(上面没有,因为默认情况下ARM全部编译进 内核),但$(CONFIG_OPROFILE)又是什么呢? 这些是根据用户在make menuconfig中设置后,生成的值赋给了CONFIG_OPROFILE。
3.那make menuconfig后的配置信息是怎么来的?
这是由各子目录下的Kconfig提供选项功用户选择并配置。
如arch/arm/Kconfig。 所有的配置都是根据arch/$(ARCH)/Kconfig文件通过Kconfig的语法source读取 各个包含的子目录Kconfig来生成一个配置界面。每个Makefile目录下都有一个对应的Kconfig文件,用于生成配置界面来给用户决定内核如何配置,配置后会确定一个。CONFIG_XXX的的值(如上面的CONFIG_OPROFILE),来决定编译进内核,还是编译成模块或者不编译。
如在arch/arm/Kconfig下:
595 source "arch/arm/mach-clps711x/Kconfig"
596
597 source "arch/arm/mach-ep93xx/Kconfig"
598
599 source "arch/arm/mach-footbridge/Kconfig"
600
601 source "arch/arm/mach-integrator/Kconfig"
602
603 source "arch/arm/mach-iop32x/Kconfig"
604
605 source "arch/arm/mach-iop33x/Kconfig"
这些就是用来指定,需要读取以下目录下的Kconfig文件来生成一个使用make menuconfig时的配置界面。
至于子目录下的Kconfig是怎么样的,待会介绍。
总结Kconfig的作用:
3.1.在make menuconfig下可以配置选项;
3.2.在.config中确定CONFIG_XXX的的值。
4.只是读取以上的两个Makefile还是不够了,内核还会把包含的子目录一层一层的 读取它里面的Makefile和Kconfig。
上面啰啰嗦嗦地讲了这么久,无非就是想说,内核的编译并不是一个Makefile搞定的,需要通过根目录下的总Makefile来包含一下子Makefile(不管是根目录下的子目录还是/arch/arm中的子目录)。而Kconfig,为用户提供一个交互界面来选择如何配置并生成配置选项。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、子目录下的Makefile和Kconfig
上面我一直介绍的都是两个比较大的Makefile——总Makefile和 arch/$(ARCH)/Makefile。接下来看一下实例。
一、在makefile中,y表示编译进内核,m表示编译成模块,不写代表不编译。 所以,配置最简单的方法就是,直接修改子目录的Makefile 。
先看看arch/arm/Makefile:
/*arch/arm/mach-s3c2440/Makefile */
12 obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o
13 obj-$(CONFIG_CPU_S3C2440) += irq.o
的时钟进入模块
15 obj-$(CONFIG_S3C2440_DMA) += dma.o
如果我要取消s3c2440的时钟(当然这是必须要开的,只是举例)。 可以直接修改arch/arm/mach-s3c2440/Makefile 将obj-$( CONFIG_CPU_S3C2440) += clock.o改为
obj- += clock.o
如果你想编译成模块也可以修改成:
obj-m += clock.o
在这里 CONFIG_CPU_S3C2440的值默认是y,所以内核是要将时钟编译进内核的。也许有人会问,那我直接修改 CONFIG_CPU_S3C2440的值为m不就可以将时钟编译成模块了,何必修改Makefile这么麻烦呢?的确是这样,只要我们通过在”make menuconfig”的界面中配置后就能够改变 CONFIG_CPU_S3C2440的值。接下来看看如何实现。
二、在一般的编译内核时,我们都是通过”make menuconfig”进入图形界面面配置的, 接下来我实现一下如何将一个选项加入到图形配置界面中。
看看具体实现的步骤:
以下的执行环境是在PC机上,我使用的内核是linux-2.6.29:
2.1.进入内核目录
cd linux-2.6.29
2.2. 在driver目录下模拟一个名为test1驱动的文件夹
mkdir driver/test1
2.3. 在目录下随便些一个C文件,只要不报错。
vim test1.c
我的test1.c如下:
1 void foo()
2 {
3 ;
4 }
2.4vim Makefile //在目录下编写一个简单的Makefile
Makefile文件编写如下:
obj-$(CONFIG_TEST1) += test1.o
CONFIG_TEST1是决定test1是否编译进内核或者编译成模块的。这就是通 过同一目录下的Kconfig来在配置界面中生成选项,由用户在make menuconfig中选择。
2.5所以还要同一目录下写一个Kconfig:
vim Kconfig
Kconfig修改如下:
menu "test1 driver here" //这是在图形配置显示的
config TEST1
bool "xiaobai test1 driver" //这同样也是在图形配置显示的
help
This is test1 //这个也是在图形配置显示的。
说白了,就是在图形配置的driver下多了一个配置选项,用户配置后将 CONFIG_TEST1的值存放在.config中,Makefile通过读取.config的去注 释版include/config/auto.conf读取到CONFIG_TEST的值,再进行编译。
但是,以上几步还不能达到目的,因为虽然在总Makefile中已经包含了 目录driver,但是driver目录的Makefile中并没有包含test目录。因此 需要在driver/Makefile中添加:
103 obj-$(CONFIG_PPC_PS3) += ps3/
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中添加:
1230 menu "Device Drivers" //要在Device Drivers这个选项里面添加
1231
1232 source "drivers/base/Kconfig"
1233
1234 source "drivers/connector/Kconfig"
。。。。。。。。
1330 source "drivers/test/Kconfig" //这是我添加的
1331
1332 endmenu
大功告成!
这样,make menuconfig界面写的Driver Devices下就多了一个 "test1 friver here"的目录,里面有一个配置选项"xiaobai test1 driver"。
Kconfig文件的语法在documentation/kbuild/kconfig-language.txt文件中 有详细的讲解,上面我只是简单实现了一下,都是皮毛。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
六.内核和模块的编译
编译内核很简单,只需要配置完毕后执行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
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
七、总结
说了这么久估计都说糊涂了,其实我只是想表达一下内核编译时大体上究竟是怎么样的一个过程。
我以编译S3C2440的内核为例再说一遍:
一般我们会想将一份S3C2440的默认配置拷贝到内核跟目录下并改名为.config。
2.make menuconfig
2.1、由总Makefile决定编译的体系结构(ARCH). 编译工具(CROSS_COMPILE),并知道需要进去哪些内核根下的哪些目录进行编译。
2.2、由arch/$(ARCH)/Makefile,决定arch/$(ARCH)下还有 的哪些目录和文件需要编译。
2.3、知道了需要编译的目录后,递归的进入哪些目录下,读取每一个Kconfig的信息,生成了图形配置的界面。
2.4、通过我们在图形配置界面中选项为[*]、[M]或者[]。
2.5、保存并退出配置,会根据配置生成一份新的配置文件.config,并在同时生成include/config/auto.conf(这是.config的去注释版)。文件里面保存着CONFIG_XXXX等变量应该取y还是取m。
3.make
3.1、根据Makefile包含的目录和配置文件的要求,进去个子目录进行编译,最后会在各子目录下 生成一个.o或者.a文件,然后总Makefile指定的连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过 压缩编程bzImage,或者按要求在对应的子目录下编译成 模块。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
转:Linux设备驱动开发(1):内核基础概念的更多相关文章
- 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接
<Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...
- 《Linux设备驱动开发具体解释(第3版)》进展同步更新
本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...
- Hasen的linux设备驱动开发学习之旅--时钟
/** * Author:hasen * 參考 :<linux设备驱动开发具体解释> * 简单介绍:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:时钟 * Date ...
- Linux 设备驱动开发 —— platform设备驱动应用实例解析
前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...
- Linux设备驱动开发环境的搭建(转)
经过两周的摸索,终于对Linux设备驱动开发有了个初步的认识,下面对Linux设备驱动开发环境的搭建做个小结,以方便自己以后查询,同时也能给同道的初学者一点帮助. 刚接触Linux设备驱动时,初学者往 ...
- Linux设备驱动开发详解
Linux设备驱动开发详解 http://download.csdn.net/detail/wuyouzi067/9581380
- 《Linux设备驱动开发详解(第2版)》配套视频登录51cto教育频道
http://edu.51cto.com/course/course_id-379-page-1.html http://edu.51cto.com/course/course_id-379-page ...
- Linux设备驱动开发详解-Note(5)---Linux 内核及内核编程(1)
Linux 内核及内核编程(1) 成于坚持,败于止步 Linux 2.6 内核的特点 Linux 2.6 相对于 Linux 2.4 有相当大的改进,主要体现在如下几个方面. 1.新的调度器 2.6 ...
- Linux设备驱动开发详解-Note(11)--- Linux 文件系统与设备文件系统(3)
Linux 文件系统与设备文件系统(3) 成于坚持,败于止步 sysfs 文件系统与 Linux 设备模型 1.sysfs 文件系统 Linux 2.6 内核引入了 sysfs 文件系统,sysfs ...
随机推荐
- 异步FIFO空满设计延迟问题
由于设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢? 异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域 ...
- bzoj4918: 回文数对
Description 给定区间[L,R],请统计有多少对整数A,B(L<=A,B<=R)满足A xor B的值在二进制表示下,去掉所有前导0后是回文串 Input 第一行包含一个正整数T ...
- 如何缓解DDOS攻击
1.减少攻击面 (a) reduce the number of necessary Internet entry points,(b) eliminate non-critical Internet ...
- Android RILD运行机制详解
前言 在上一篇文章里(http://blog.csdn.net/jason_wzn/article/details/53232022),简要介绍了Android RIL的架构.这一篇文章,就来看一看R ...
- 第13章 TCP编程(4)_基于自定义协议的多线程模型
7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...
- Dependency Injection in ASP.NET Web API 2 Using Unity
What is Dependency Injection? A dependency is any object that another object requires. For example, ...
- USB接口程序编写
copy from http://blog.csdn.net/luckywang1103/article/details/12393889# HID是Human Interface Devices的缩 ...
- BI开发(ETL-DW)
来到公司已经参与开发了一段时间的BI项目,但是仅仅是按照需求开发,今天下午公司给大家培训数据仓库的知识,老大(女程序员)在上面讲,我们在下面听,2到3个小时吧,什么纬度,主题,几乎听的一脸茫然,最后演 ...
- Linux--多网卡的7种Bond模式和交换机配置
网卡bond是通过把多张网卡绑定为一个逻辑网卡,实现本地网卡的冗余,带宽扩容和负载均衡.在应用部署中是一种常用的技术,我们公司基本所有的项目相关服务器都做了bond,这里总结整理,以便待查. bond ...
- python之匿名函数和递归函数
递归函数 <1>什么是递归函数 通过前面的学习知道一个函数可以调用其他函数. 如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数. <2>递归函数的作用 ...