1、linux内核
www.kernel.org
$:'uname -a
$:'uanme -r
// 查看linux内核版本,开发板上进入linux后是同样的命令。
早起经常使用的版本:linux 2.6.x
开发板上使用的版本:linux 3.4.39
linux最新版本:linux 4.x
2、linux内核的5大功能
1)内存管理功能;
2)进程管理及进程间通信;
3)虚拟文件子系统;
4)设备驱动管理;
5)网络子系统;
嵌入式平台有操作系统,优势在于:
1)编程简单,有库函数可以调用;
2)可以实现更加复杂的业务逻辑;// 多进程 多线程
3、linux内核的编译
env/kernel.tar.bz2
$:'cp /mnt/hgfs/porting/env/kernel.tar.bz2 .
$:' tar xvf *.bz2
$:' cd kernel/
$:'find ./ -type f | wc -l
// 26129
$:'cp arch/arm/configs/x6818_defconfig .config
$:'vi Makefile
// 195 ARCH ?= arm
// 196 CROSS_COMPILE ?= arm-cortex_a9-linux-gnueabi-
$:'make uImage -j4
// arch/arm/boot/uImage is ready
$:'make menuconfig -j4
// 此项为内核功能配置,可删减(内核裁剪)
** 编译异常问题解决 **
问题1:
>>如果出现"mkuimage"command not find
解决办法:
$:'sudo cp ~/project/uboot/tools/mkimage /usr/bin/
$:'make uImage
问题2:
>>Your display is too small...
解决办法:
缩小字体显示比例。
验证uImage的有效性:
把uImage烧写0x800扇区开始的分区位置。
通过加载的方式:
cp arch/arm/boot/uImage /tftpboot/
tftp 48000000 uImage
bootm 48000000
setenv bootcmd tftp 48000000 uImage \; bootm 48000000
3.1 内核的配置
【什么是配置?】
假设linux内核中提供了10000种功能,但是具体到开发板上只需要其中2000个功能。
1)裁剪掉内核中不需要的功能
不需要挂载U盘
// 内核中关于FAT32类型文件系统相关的代码的支持就不需要
2)驱动程序的裁剪
// 产品中没有键盘,裁剪掉内核中的键盘驱动
【总之】根据目标产品的需要,以及开发板的实际硬件,裁剪掉不需要的内核代码。
【如何完成配置?】
找一个相近的配置文件,在此基础上进行修改。
【去哪里找相近的配置?】
1)去内核源码目录 arch/arm/configs/xxx_defconfig
2)上游厂家提供
【如何修改配置?】
cp 相近的配置.config kernel/.config
a)$:' make menuconfig
// 模拟菜单显示界面
General setup --->
System Type --->
ARM system type (SLsiAP S5P6818) --->
Boot options --->
Device Drivers ---> // 硬件驱动设备
File systems ---> // 文件系统
b)$:' make config
// 纯文字提示,基本不用
c)$:' make xconfig
// 真实图形显示菜单界面,基本不用
"make help" 查看内核make帮助命令。
内核本质也是一个裸板程序。
.o ... .o ---> elf 文件 (vmlinux)
vmlinux ---> Image(.bin) ---> zImage ---> uImage
zImage:解压缩代码 + 压缩后的Image数据
uImage:64Bytes + zImage
// 64Bytes:内核的加载地址、内核版本 ... 一般给uboot使用。
$:'du -h arch/arm/boot/Image
// 11M
$:'du -h arch/arm/boot/zImage
// 5.2M <-b> 5400688,压缩后的Image
$:' du -harch/arm/boot/uImage
// 5.2M <-b> 5400752
<tips>
$:'du -h
// 以兆(M)显示文件大小 和 文件名
$:'du -b
// 以字节数显示文件大小 和 文件名
3.2 linux内核的启动过程
阅读代码,组织工程。
linux : vim + ctags
$:'rm arch/arm/mach-s5p6818/prototype/prototype
$:'ctags -R *
windows : source insight
入口点文件:
$:'rm vmlinux
$:'make uImage V=1
// V=1 显示详细的编译过程
$:'vi arch/arm/kernel/vmlinux.lds
.head.text : {
_text = .;
*(.head.text)
}
// 整个linux内核的入口点文件:arch/arm/kernel/head.S
head.S:
设置SVC模式
__lookup_processor_type // 检查CPU的ID看内核的状态
bl __create_page_tables // 创建页表
ldr r13, =__mmap_switched
b __enable_mmu
。。。
。。。
b __turn_mmu_on
450 ENTRY(__turn_mmu_on)
457 mov r3, r13
458 mov pc, r3 //跳转到__mmap_switched
__mmap_switched:
b start_kernel
start_kernel
-->rest_init
{
//创建一个新的内核线程
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
}
kernel_init( )
{
prepare_namespace( )
{
//根据bootargs的参数,挂载根文件系统
mount_root( );
}
init_post( )
{
//执行用户空间的1号进程
run_init_process("/sbin/init");
}
}
系统上电--->uboot完成硬件的初始化--->执行bootcmd中的命令--->加载操作系统到内存--->传递参数给内核,并启动内核--->检查CPU ID当前内核是否支持--->创建页表开启MMU--->start_kernel--->创建一个新的内核线程--->根据参数去挂载根文件系统--->从根文件系统中找到1号进程--->创建执行1号进程--->1号进程后续会创建子进程/bin/bash--->用户可以输入命令
移植内核的关键文件:arch\arm\mach-s5p6818\cpu.c
其中的关键代码信息:
static void __initcpu_init_machine ( void ) {
...
nxp_cpu_devs_register ( );
nxp_board_devs_register ( );
}
// 该函数是在 kernel_init 中被调用,即系统启动过程中被调用。
kernel_init ( ) {
do_basic_setup ( ) {
do_initcalls ( ) {
...
cpu_init_machine ( ); // 移植的重点。要改的函数。
}
}
}
4、配置过程是如何影响编译过程的
$:' make menuconfig
Device Drivers --->
Character devices --->
-*- LED Support --->
// LED Support for GPIO connected LEDs ---> help
---> 变量:CONFIG_LEDS_GPIO
$:' vi .config
CONFIG_LEDS_GPIO=y // <*>
CONFIG_LEDS_GPIO=m // <M>
# CONFIG_LEDS_GPIO is not set // < >
【结论1】配置的结果是以变量取不同的值的形式保存在 .config 文件中。
【结论2】内核中几乎所有的目录下都有Makefile文件。
【结论3】各个子目录下的Makefile决定了当前目录下的文件(夹)是否参与编译过程。
$:'grep "CONFIG_LEDS_GPIO" * -nRw
// grep显示行号-递归-内容检索:
drivers/leds/Makefile:22行使用了该变量
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
<*> CONFIG_LEDS_GPIO=y ' boj-y += leds-gpio.o
<M> CONFIG_LEDS_GPIO=y ' boj-m += leds-gpio.o
< > CONFIG_LEDS_GPIO=y ' boj- += leds-gpio.o
【结论4】
在编译内核时 boj-y += xxx.o 代表 xxx.c 参与编译,并最终链接到内核uImage;
在编译内核时 boj-m += xxx.o 代表 xxx.c 参与编译,但不会链接进内核uImage,生成一个独立的xxx.o;
在编译内核时 boj- += xxx.o 代表 xxx.c 不参与编译。
5、如何将一个.c文件编译进内核
// env/led_drv.c
$:'cp /mnt/hgfs/porting/env/led_drv.c drivers/char/
$:'vi drivers/char/Makefile
增加1行:obj-y += led_drv.o
$:'make uImage
$:' cp arch/arm/boot/uImage /tftpboot/
#:' tftp 48000000 uImage
#:' bootm 48000000
#:'dmesg >1.txt
#:'dmesg | grep "led_init"
www.kernel.org
$:'uname -a
$:'uanme -r
// 查看linux内核版本,开发板上进入linux后是同样的命令。
早起经常使用的版本:linux 2.6.x
开发板上使用的版本:linux 3.4.39
linux最新版本:linux 4.x
2、linux内核的5大功能
1)内存管理功能;
2)进程管理及进程间通信;
3)虚拟文件子系统;
4)设备驱动管理;
5)网络子系统;
嵌入式平台有操作系统,优势在于:
1)编程简单,有库函数可以调用;
2)可以实现更加复杂的业务逻辑;// 多进程 多线程
3、linux内核的编译
env/kernel.tar.bz2
$:'cp /mnt/hgfs/porting/env/kernel.tar.bz2 .
$:' tar xvf *.bz2
$:' cd kernel/
$:'find ./ -type f | wc -l
// 26129
$:'cp arch/arm/configs/x6818_defconfig .config
$:'vi Makefile
// 195 ARCH ?= arm
// 196 CROSS_COMPILE ?= arm-cortex_a9-linux-gnueabi-
$:'make uImage -j4
// arch/arm/boot/uImage is ready
$:'make menuconfig -j4
// 此项为内核功能配置,可删减(内核裁剪)
** 编译异常问题解决 **
问题1:
>>如果出现"mkuimage"command not find
解决办法:
$:'sudo cp ~/project/uboot/tools/mkimage /usr/bin/
$:'make uImage
问题2:
>>Your display is too small...
解决办法:
缩小字体显示比例。
验证uImage的有效性:
把uImage烧写0x800扇区开始的分区位置。
通过加载的方式:
cp arch/arm/boot/uImage /tftpboot/
tftp 48000000 uImage
bootm 48000000
setenv bootcmd tftp 48000000 uImage \; bootm 48000000
3.1 内核的配置
【什么是配置?】
假设linux内核中提供了10000种功能,但是具体到开发板上只需要其中2000个功能。
1)裁剪掉内核中不需要的功能
不需要挂载U盘
// 内核中关于FAT32类型文件系统相关的代码的支持就不需要
2)驱动程序的裁剪
// 产品中没有键盘,裁剪掉内核中的键盘驱动
【总之】根据目标产品的需要,以及开发板的实际硬件,裁剪掉不需要的内核代码。
【如何完成配置?】
找一个相近的配置文件,在此基础上进行修改。
【去哪里找相近的配置?】
1)去内核源码目录 arch/arm/configs/xxx_defconfig
2)上游厂家提供
【如何修改配置?】
cp 相近的配置.config kernel/.config
a)$:' make menuconfig
// 模拟菜单显示界面
General setup --->
System Type --->
ARM system type (SLsiAP S5P6818) --->
Boot options --->
Device Drivers ---> // 硬件驱动设备
File systems ---> // 文件系统
b)$:' make config
// 纯文字提示,基本不用
c)$:' make xconfig
// 真实图形显示菜单界面,基本不用
"make help" 查看内核make帮助命令。
内核本质也是一个裸板程序。
.o ... .o ---> elf 文件 (vmlinux)
vmlinux ---> Image(.bin) ---> zImage ---> uImage
zImage:解压缩代码 + 压缩后的Image数据
uImage:64Bytes + zImage
// 64Bytes:内核的加载地址、内核版本 ... 一般给uboot使用。
$:'du -h arch/arm/boot/Image
// 11M
$:'du -h arch/arm/boot/zImage
// 5.2M <-b> 5400688,压缩后的Image
$:' du -harch/arm/boot/uImage
// 5.2M <-b> 5400752
<tips>
$:'du -h
// 以兆(M)显示文件大小 和 文件名
$:'du -b
// 以字节数显示文件大小 和 文件名
3.2 linux内核的启动过程
阅读代码,组织工程。
linux : vim + ctags
$:'rm arch/arm/mach-s5p6818/prototype/prototype
$:'ctags -R *
windows : source insight
入口点文件:
$:'rm vmlinux
$:'make uImage V=1
// V=1 显示详细的编译过程
$:'vi arch/arm/kernel/vmlinux.lds
.head.text : {
_text = .;
*(.head.text)
}
// 整个linux内核的入口点文件:arch/arm/kernel/head.S
head.S:
设置SVC模式
__lookup_processor_type // 检查CPU的ID看内核的状态
bl __create_page_tables // 创建页表
ldr r13, =__mmap_switched
b __enable_mmu
。。。
。。。
b __turn_mmu_on
450 ENTRY(__turn_mmu_on)
457 mov r3, r13
458 mov pc, r3 //跳转到__mmap_switched
__mmap_switched:
b start_kernel
start_kernel
-->rest_init
{
//创建一个新的内核线程
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
}
kernel_init( )
{
prepare_namespace( )
{
//根据bootargs的参数,挂载根文件系统
mount_root( );
}
init_post( )
{
//执行用户空间的1号进程
run_init_process("/sbin/init");
}
}
系统上电--->uboot完成硬件的初始化--->执行bootcmd中的命令--->加载操作系统到内存--->传递参数给内核,并启动内核--->检查CPU ID当前内核是否支持--->创建页表开启MMU--->start_kernel--->创建一个新的内核线程--->根据参数去挂载根文件系统--->从根文件系统中找到1号进程--->创建执行1号进程--->1号进程后续会创建子进程/bin/bash--->用户可以输入命令
移植内核的关键文件:arch\arm\mach-s5p6818\cpu.c
其中的关键代码信息:
static void __initcpu_init_machine ( void ) {
...
nxp_cpu_devs_register ( );
nxp_board_devs_register ( );
}
// 该函数是在 kernel_init 中被调用,即系统启动过程中被调用。
kernel_init ( ) {
do_basic_setup ( ) {
do_initcalls ( ) {
...
cpu_init_machine ( ); // 移植的重点。要改的函数。
}
}
}
4、配置过程是如何影响编译过程的
$:' make menuconfig
Device Drivers --->
Character devices --->
-*- LED Support --->
// LED Support for GPIO connected LEDs ---> help
---> 变量:CONFIG_LEDS_GPIO
$:' vi .config
CONFIG_LEDS_GPIO=y // <*>
CONFIG_LEDS_GPIO=m // <M>
# CONFIG_LEDS_GPIO is not set // < >
【结论1】配置的结果是以变量取不同的值的形式保存在 .config 文件中。
【结论2】内核中几乎所有的目录下都有Makefile文件。
【结论3】各个子目录下的Makefile决定了当前目录下的文件(夹)是否参与编译过程。
$:'grep "CONFIG_LEDS_GPIO" * -nRw
// grep显示行号-递归-内容检索:
drivers/leds/Makefile:22行使用了该变量
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
<*> CONFIG_LEDS_GPIO=y ' boj-y += leds-gpio.o
<M> CONFIG_LEDS_GPIO=y ' boj-m += leds-gpio.o
< > CONFIG_LEDS_GPIO=y ' boj- += leds-gpio.o
【结论4】
在编译内核时 boj-y += xxx.o 代表 xxx.c 参与编译,并最终链接到内核uImage;
在编译内核时 boj-m += xxx.o 代表 xxx.c 参与编译,但不会链接进内核uImage,生成一个独立的xxx.o;
在编译内核时 boj- += xxx.o 代表 xxx.c 不参与编译。
5、如何将一个.c文件编译进内核
// env/led_drv.c
$:'cp /mnt/hgfs/porting/env/led_drv.c drivers/char/
$:'vi drivers/char/Makefile
增加1行:obj-y += led_drv.o
$:'make uImage
$:' cp arch/arm/boot/uImage /tftpboot/
#:' tftp 48000000 uImage
#:' bootm 48000000
#:'dmesg >1.txt
#:'dmesg | grep "led_init"