文章目录
- 基本概念
- 内核源码目录结构
- 内核配置
- 主目录Makefile
- 各子目录Makefile
- 如何配置内核?
- 1. 配置仓库选取
- 2.交叉编译器的修改
- 3.体系结构体的选择
- 4.修改配置文件
- 内核编译
- 编译结果:几种linux内核文件的区别
- 开发板上U-Boot启动linux内核
- 内核Kconfig语法使用
- 1.make menuconfig 是如何找到 Kconfig?
- 2.Makefile 要编译一个 .o 文件是如何在Kconfig中配置的?
- 添加驱动到内核
- 概念
- 添加步骤
- 1.将我们开发的驱动 myleddev.c 放到能自述其意的目录下,如 /drive/char/myled 目录
- 2.在myled 目录下增加并编辑 Makefile 文件,将 myleddev.c 和 Makefile 关联起来:
- 3. 使用 make menuconfig
- 编译
- 执行应用程序
- 1.将编译后的内核下载到开发板上
- 2.确定内核是否运行了LED驱动
- 3.创建LED驱动程序与应用程序直接衔接的设备节点
- 4.测试
- 图
- 参考资料
基本概念
linux内核特性:
1.可移植性,支持的硬件平台广泛
2.超强的网络功能
3.多任务多用户系统
4.模块化的设计
五大子系统:
1.进程管理子系统
2.内存管理子系统
3.文件系统子系统
4.网络协议子系统
5.设备管理子系统
获取linux内核:
1.内核官方发布点
2.芯片厂商提供
内核源码目录结构
内核配置
- Makefile 和 make
由主目录 Makefile 和 子目录 Makefile 共同指定内核的编译规则
2.通过 make menuconfig 及 Kconfig 修改内核的配置单,从而自动完成内核配置
选择哪些文件被编译进内核
主目录Makefile
Makefile文件位置: linux-3.0.8/Makefile
以下为Makefile文件中的部分代码,根据arch平台,配置包含子目录的头文件目录和子目录的Makefile
根据SUBARCH配置 SRCARCH,SUBARCH默认 X86
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) // 交叉编译器设置
# Architecture as present in compile.h
UTS_MACHINE := $(ARCH)
SRCARCH := $(ARCH)
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
SRCARCH := x86
endif
ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
包含子目录的头文件目录 arch/arm/include:
-I$(srctree)/arch/$(hdr-arch)/include
包含子目录 arch/arm/Makefile 文件:
include $(srctree)/arch/$(SRCARCH)/Makefile
SRCARCH = arm
hdr-arch arm
各子目录Makefile
obj-y : 编译进内核
obj- : 不编译进去
obj-m : 以模块形式编译
如何配置内核?
1. 配置仓库选取
1、SUBARCH 默认就是X86的,内容默认配置安装X86
ARCH 这个变量改为 arm 单词
交叉编译 CROSS_COMPILE:在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码
2、哪些文件会被编译?
这些文件是否被编译是由子目录中的Makfile文件中 CONFIG_xxx 变量是否被定义而决定(obj-y编译进内核,obj-m以模块形式编译)
主Makefile是如何将这些文件引入到我们的整个环境变量中?通过配置单 中定义的 CONFIG_xxx
3、配置单: 由芯片商提供BSP时会提供参考配置单,能够大大简化我们的配置过程
cs@cspc:/mnt/d/linux/linux-3.0.8/arch/arm$ ls # arm架构下的子目录
Kconfig common mach-at91 mach-dove mach-gemini mach-iop32x mach-kirkwood mach-mmp mach-netx mach-orion5x mach-s3c2400 mach-s3c2443 mach-s5pv210 mach-spear6xx mach-versatile nwfpe plat-omap plat-samsung vfp
Kconfig-nommu configs mach-bcmring mach-ebsa110 mach-h720x mach-iop33x mach-ks8695 mach-msm mach-nomadik mach-pnx4008 mach-s3c2410 mach-s3c24a0 mach-sa1100 mach-tcc8k mach-vexpress oprofile plat-orion plat-spear
Kconfig.debug include mach-clps711x mach-ep93xx mach-imx mach-ixp2000 mach-l7200 mach-mv78xx0 mach-nuc93x mach-pxa mach-s3c2412 mach-s3c64xx mach-shark mach-tegra mach-vt8500 plat-iop plat-pxa plat-tcc
Makefile kernel mach-cns3xxx mach-exynos4 mach-integrator mach-ixp23xx mach-loki mach-mx5 mach-omap1 mach-realview mach-s3c2416 mach-s5p64x0 mach-shmobile mach-u300 mach-w90x900 plat-mxc plat-s3c24xx plat-versatile
boot lib mach-davinci mach-footbridge mach-iop13xx mach-ixp4xx mach-lpc32xx mach-mxs mach-omap2 mach-rpc mach-s3c2440 mach-s5pc100 mach-spear3xx mach-ux500 mm plat-nomadik plat-s5p tools
cs@cspc:/mnt/d/linux/linux-3.0.8/arch/arm$ cd configs/
cs@cspc:/mnt/d/linux/linux-3.0.8/arch/arm/configs$ ls # 配置单所在目录 configs
acs5k_defconfig at91sam9260ek_defconfig cerfcube_defconfig cpu9g20_defconfig exynos4_defconfig h7202_defconfig ixp4xx_defconfig magician_defconfig mx3_defconfig nuc960_defconfig pxa168_defconfig s3c2410_defconfig spear3xx_defconfig usb-a9260_defconfig
acs5k_tiny_defconfig at91sam9261_defconfig cm_x2xx_defconfig da8xx_omapl_defconfig ezx_defconfig hackkit_defconfig jornada720_defconfig mainstone_defconfig mx51_defconfig omap1_defconfig pxa255-idp_defconfig s3c6400_defconfig spear6xx_defconfig versatile_defconfig
afeb9260_defconfig at91sam9263_defconfig cm_x300_defconfig davinci_all_defconfig footbridge_defconfig imote2_defconfig kirkwood_defconfig mini2440_defconfig mxs_defconfig omap2plus_defconfig pxa3xx_defconfig s5p64x0_defconfig spitz_defconfig vexpress_defconfig
ag5evm_defconfig at91sam9g20ek_defconfig cns3420vb_defconfig dove_defconfig fortunet_defconfig integrator_defconfig ks8695_defconfig mmp2_defconfig neponset_defconfig orion5x_defconfig pxa910_defconfig s5pc100_defconfig stamp9g20_defconfig viper_defconfig
am200epdkit_defconfig at91sam9rlek_defconfig colibri_pxa270_defconfig ebsa110_defconfig g3evm_defconfig iop13xx_defconfig lart_defconfig msm_defconfig netwinder_defconfig palmz72_defconfig qil-a9260_defconfig s5pv210_defconfig tct_hammer_defconfig xcep_defconfig
ap4evb_defconfig at91x40_defconfig colibri_pxa300_defconfig edb7211_defconfig g4evm_defconfig iop32x_defconfig loki_defconfig mv78xx0_defconfig netx_defconfig pcm027_defconfig raumfeld_defconfig sam9_l9260_defconfig tegra_defconfig zeus_defconfig
assabet_defconfig badge4_defconfig collie_defconfig em_x270_defconfig h3600_defconfig iop33x_defconfig lpd270_defconfig mx1_defconfig nhk8815_defconfig pcontrol_g20_defconfig realview-smp_defconfig shannon_defconfig trizeps4_defconfig
at91cap9adk_defconfig bcmring_defconfig corgi_defconfig ep93xx_defconfig h5000_defconfig ixp2000_defconfig lubbock_defconfig mx21_defconfig nuc910_defconfig pleb_defconfig realview_defconfig shark_defconfig u300_defconfig
at91rm9200_defconfig cam60_defconfig cpu9260_defconfig eseries_pxa_defconfig h7201_defconfig ixp23xx_defconfig mackerel_defconfig mx27_defconfig nuc950_defconfig pnx4008_defconfig rpc_defconfig simpad_defconfig u8500_defconfig
配置单文件的后缀 “_defconfig” 表示默认配置,且每个配置单对应 arm 架构下的目录,如 “ixp2000_defconfig” 对应
“mach-ixp2000”
配置单内容如下(为y则代表其对应的文件会被编译进内核):
cs@cspc:/mnt/d/linux/linux-3.0.8/arch/arm/configs$ cat s5pv210_defconfig
CONFIG_EXPERIMENTAL=y
CONFIG_SYSFS_DEPRECATED_V2=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_KALLSYMS_ALL=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_BLK_DEV_BSG is not set
...
我们将将配置单导出到内核的主目录下 .config
4、修改defconfig 配置单变成适应于板子的文件
2.交叉编译器的修改
回到内核的主目录,修改Makefile
修改以下两行:
ARCH ?= $(SUBARCH) # 设置架构体系, 如直接设 arm;也可以在make中修改
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) # 设置交叉编译器
修改后的结果如:
ARCH ?= arm
CROSS_COMPILE ?= /opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-
3.体系结构体的选择
将要移植的平台的参考配置单文件拷贝到主目录下:
cs@cspc:/mnt/d/linux/linux-3.0.8$ cp arch/arm/configs/s5pv210_defconfig .config
cs@cspc:/mnt/d/linux/linux-3.0.8$ ls -a
. .. .config .gitignore .mailmap COPYING CREDITS Documentation Kbuild Kconfig MAINTAINERS Makefile README REPORTING-BUGS arch block crypto drivers firmware fs include init ipc kernel lib mm net samples scripts security sound tools usr virt
4.修改配置文件
可以使用 make menuconfig 以桌面窗口形式对.config 文件进行修改, 这比以文本方式修改.config更方便
关系: make menuconfig —解析Kconfig后修改—> .config —作用于—> Makefile
而且 make menuconfig中出现大量字符串,其来自解析的 Kconfig 文件
内核编译
- 将芯片厂商提供的配置单文件名更改为 .config
- 查看 Makefile中的交叉编译器是否设置正确
- make menuconfig 设置 .config
4.make编译文件
内核编译(make)之后会生成两个文件,一个Image,一个zImage,
其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M
uImage又是什么的?
它是uboot专用的映像文件,它是在zImage之前加上一个长度为64字节的“头”,说明这个内核的版本、加载位置、生成时间、大小等信息;其0x40之后与zImage没区别。
生成uImage文件:
1.首先在uboot的/tools目录下寻找mkimage文件,把其copy到系统/usr/local/bin目录下,这样就完成制作工具
2.在内核目录下运行make uImage,如果成功,便可以在arch/arm/boot/目录下发现uImage文件,其大小比zImage多64个字节
有了uImage头部的描述, u-boot就知道对应Image的信息, 如果没有头部则需要自己手动去设置那些参数
vmlinum --> Image --> zImage --> uImage:
编译结果:几种linux内核文件的区别
1、vmlinux :编译出来后未压缩最原始的elf内核文件,该文件不能烧写到flash中
2、Image: vmlinux 经过 OBJCOPY 之后会产生的文件,比较大,但已经可以具备烧写到flash
2、zImage :Image经过gzip压缩后的文件。
3、bzImage:bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。
4、uImage : U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的TAG。使得内核可以启动
5、vmlinuz: bzImage/zImage文件的拷贝或指向bzImage/zImage的链接。
6、initrd ,: “initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态
一般情况下都在生成vmlinux后,再对内核进行压缩成为zImage,压缩的目录是kernel/arch/arm/boot。
下载到 flash 中的是压缩后的zImage文件,zImage是由压缩后的vmlinux和解压缩程序组成的。
开发板上U-Boot启动linux内核
查看 2440 的 datasheet , 发现内存映射的基址是 0x3000 0000 , 那么0x30008000 又是如何来的呢?
在内核文档kernel/Document/arm/Booting 文件中有:
Calling the kernel image
Existingboot loaders: MANDATORY New boot loaders: MANDATORY There are two options for calling the kernel zImage. If the zImage is stored inflash, and is linked correctly to be run from flash, then it is legal for the boot loader to call the zImage in flashdirectly. The zImage may also be placed in system RAM (at any location) and called there.Note that the kernel uses 16K of RAM below the image to store page tables. The recommended placement is 32KiBinto RAM.
看来在image下面用了32K(0x8000)的空间存放内核页表,0x30008000 就是 2440 的内核在 RAM 中的启动地址,这个地址就是这么来的
启动内核:
1、下载uImage.bin到SDRAM的0x30008000处,tftp是一个类似于ftp的下载软件,在uboot的shell下,输入:
tftp 0x30008000 uImage
2、启动内核,从0x30008000
bootm 0x30008000
内核Kconfig语法使用
1.make menuconfig 是如何找到 Kconfig?
make menuconfig界面选中(*代表选中)配置项时,.config配置单中的配置项也会更改为 “y”(对应的文件会被编译进内核)
make menuconfig --> Kconfig —> Makefile xxx.c 的过程 是如何组织的?
- make menuconfig中查找串口驱动的 宏CONFIG_SERIAL_SAMSUNG 配置项:
- 宏CONFIG_SERIAL_SAMSUNG 在对应的 Kconfig 中应该有这样的语法:
config SERIAL_SAMSUNG # make menuconfig运行时会在 .config 中自动生成 CONFIG_SERIAL_SAMSUNG 配置项
3.查找 Samsung SoC serial support 关键字,获得所在的Kconfig文件:
4.查看Kconfig文件获得 CONFIG_SERIAL_SAMSUNG 对应的 "config SERIAL_SAMSUNG"语法:
5.在Kconfig的同级目录下查看Makefile文件中的 “CONFIG_SERIAL_SAMSUNG” 配置项:
这里就将 samsung.c 文件编译进来了
2.Makefile 要编译一个 .o 文件是如何在Kconfig中配置的?
即: Makefile .o —> Kconfig, 则可以倒着查找
添加驱动到内核
概念
相比于裸机的驱动,内核驱动需要很多冗余的信息:
裸机的硬件发生改变则驱动也会改变; 而内核驱动则不会因为硬件改变而需要改变,因为其涉及一个复杂的驱动框架
如有驱动程序 myleddev.c 和应用程序 myledtest.c
添加步骤
1.将我们开发的驱动 myleddev.c 放到能自述其意的目录下,如 /drive/char/myled 目录
2.在myled 目录下增加并编辑 Makefile 文件,将 myleddev.c 和 Makefile 关联起来:
obj-$(CONFIG_MYLEDTEST) += myleddev.o // 前缀必须为 "CONFIG_",如果$(CONFIG_MYLEDTEST)为y,即"obj-y"则指示编译器将myleddev.c被编译进内核
在myled目录的上级目录的Makefile中添加:
obj-y += myled/ # 内核会以myled目录作为下一个目标进入
或者
obj-$(CONFIG_MYLEDTEST) += myled/ # 需要将 CONFIG_MYLEDTEST 配置项设置为 "y"
3. 使用 make menuconfig
1.在myled目录下增加并编辑 Kconfig 文件:
menu "my char device"
config MYLEDTEST
bool "Support myled device driver"
help
Support led device driver for S5PV210
endmenu
2.在上级目录的 Kconfig 文件中包含子目录 myled 的 Kconfig文件:
source "drivers/char/myled/Kconfig"
3.使用make menuconfig将.config配置单中的 CONFIG_MYLEDTEST 配置为 y:
编译
使用 make uImage 将驱动编译进内核,目录下驱动对应的 .o 文件则代表驱动成功编译进内核:
执行应用程序
1.将编译后的内核下载到开发板上
2.确定内核是否运行了LED驱动
$ cat /proc/devices # devices文件记录了内核支持的设备信息
3.创建LED驱动程序与应用程序直接衔接的设备节点
# mknode /dev/led1 c 253 1 # 创建设备节点,名为 /dev/led1
4.测试
1.需要将应用程序放在开发板上运行,可以先将PC的目录挂载到开发板的 /mnt 下:
1.PC开启 ntf服务, netstat -tua | grep nfs: 检查ntf服务是否开启
2.exportfs 查看PC当前 NFS 共享的文件系统列表
3.将共享目录挂载到开发板的 /mnt 下(注意开发板要设置IP地址): mount -t nfs -o nolock 192.168.10.10:/work/ /mnt
2.将应用程序复制到共享目录下
3.PC上对应用程序进行交叉编译
arm-linux-gcc -o ledtest myledtest.c
4.开发板上执行应用程序
./ledtest
图
添加驱动到内核的过程:
参考资料
https://www.youtube.com/watch?v=qNeLmPCK7xE&list=PLljKjXpjNpgdY8fNIhHQG0ErUDldOCQxh&index=12