LINUX 内核移植

一、内核移植概述
二、Linux内核的目录
三、 内核配置
四、Kbuild Makefile
五、编译连接内核
六、内核启动过程
七、系统环境变量的设置方法
八、实验步奏
与其它操作系统相比,Linux最大的特点:它是一款遵循GPL(General Public License GNU通用公共许可证(简称为GPL),是由自由软件基金会发行的用于计算机软件的许可证。)的操作系统,我们可以自由地使用、修改、和扩展它。正是由于这一特色,Linux受到越来越多人士的青睐。于是,一个经常会被探讨的问题出现了,即关于Linux系统的移植。对于操作系统而言,这种移植通常是跨平台的、与硬件相关的,即硬件系统结构、甚至CPU不同。下面就让我们来看看在Linux系统移植方面,我们都需要做些什么。
Linux系统移植的两大部分
对于系统移植而言,Linux系统实际上由两个比较独立的部分组成:即内核部分和系统部分。
通常启动一个Linux系统的过程是这样的:
1、一个不隶属于任何操作系统的加载程序将Linux部分内核调入内存,并将控制权交给内存中Linux内核的第一行代码。
2、此后Linux要将自己的剩余部分全部加载到内存(如果有的话,视硬件平台的不同而不同),初始化所有设备,在内存中建立好所需的数据结构(有关进程、设备、内存等)。到此为止Linux内核的工作告一段落,内核已经控制了所有硬件设备。
3、至于操作和使用这些硬件设备,则轮到系统部分上场了。内核加载根设备并启动init守护进程,init守护进程会根据配置文件加载文件系统、配置网络、服务进程、终端等。一旦终端初始化完毕,我们就会看到系统的欢迎界面了。
小结一下:
(1)内核部分初始化和控制硬件设备,为内存管理、进程管理、设备读写等工作做好一切准备。
(2)系统部份加载必需的设备,配置各种环境以便用户可以使用整个系统。

一、内核移植概述

1、RedHat 分两个系列:
Red Hat Enterprise Linux(企业版) Red Hat 9.0 (2.4)
Fedora Core(桌面版) ( 2.6.18)

2、Linux 内核版本号:
Linux的版本号又分为两部分:内核(kernel)与发行套件 (distribution)版本。
内核版本指的是在Linus领导下的开发小组开发出的系统内核的版本号,目前最新的的 版本的序号大约是 2.6.34 (3.8.8 —2013-4-17发布)
X.Y.ZZ-WWW
X代表类型。X.Y是版本号。其中Y为偶数是稳定版本,奇数是开发版本,一般有一些新的 东西加入,是不一定很稳定的测试版本,如2.1,2.3。ZZ是次版本号,此版本号不分奇偶,每1-2个月发布一个。

测试版本为主版本号+次版本号+测试号。如 2.6.12-rcl.

而一些组织或厂家将Linux系统 内核与应用软件和文档包装起来,并提供一些安装界面和系统设定与管理工具,这样就构 成了一个发行套件,例如最常见的Slackware,RedHat,Debian等等。 实际上发 行套件就是Linux的一个大软件包而已。相对于 内核版本, 发行套件的版本号随发布者 的不同而不同,与系统内核的版本号是相对独立的,例如Slackware3.5,RedHat5.1, Debian1.3.1等等。

3、新版本的内核的发布有两种形式
一种是full/ Source版本,一般是tar.gz或者是.bz2文件。
另外一种是patch文件,即补丁。 patch文件一般只有几十K到几百K,但是patch文件是针对于特定的版本的,你需要找到自己对应的版本才能使用。
例如:你有2.6.9的源代码,但想移到2.6.10。就可以获得2.6.10的补丁文件,应用patch来修改2.6.9源文件。
$ cd /usr/src/linux
$ patch –pl < …/patch-2.6.10

4、linux内核和版本查询命令
方法一:
命令: uname -a
作用: 查看系统内核版本号及系统名称

方法二:
命令: cat /proc/version
作用: 查看目录"/proc"下version的信息,也可以得到当前系统的内核版本号及系统名称
5、LINUX内核特点:
1、可移植性
2、模块化
3、稳定性
4、开放源码。有各种驱动程序和应用程序可以利用。

6、内核移植
是指将软件从一个平台迁移到另一个平台:
1、从一个硬件平台移植到另一个硬件平台
2、从一个操作系统移植到另一个操作系统
3、从一种软件库环境移植到另一个软件库环境

7、软件进行移植的容易程度即可移植性
内核移植工作主要是修改跟硬件平台相关的配置(主要是开发板初始化和驱动程序),一般不涉及linux内核通用的程序。

在Linux内核里,每一个处理器指令集对应一个独立的体系结构architecture,比如alpha, arm, i386, mips, ppc。每个体系结构可以有若干变种variant,或不同配置的硬件machin统称sub-architecture。

由于linux可移植的特点,并且已经支持了各种体系结构的很多公板。找到类似的公板做为参考。
Linux 2.6 内核对已经支持S3C2410处理器的多种公板。(/arch/arm/mach-s3c2410/mach- )例如SMDK2410、Simtec-BAST、Thorcom-VR1000,还支持常用的学习板,如mini2440。
Cpu级的移植  板级的移植

8、选择参考板的原则:
处理器相同或类似
外围接口电路相同或类似
已经支持参考板
已驱动基本接口

9、Linux操作系统移植包括:
工具链移植
内核移植
应用程序移植

10、Linux内核的平台相关代码
Linux内核对多平台有很好的支持
内核的对外部接口是统一的,并且与平台无关 open close read write
内核的大多数代码也是与平台无关的
主要的体系结构相关代码存在于
arch/architecture
include/asm-architecture
比如arm体系的平台相关代码主要是
arch/arm
include/asm-arm

二、Linux内核的目录
Linux内核源代码位于/usr/src/linux目录下
/arch 子目录包含了所有硬件结构特定的内核代码。内核中与具体CPU和体系结构相关的代码以单独目录存放,而相应的头文件.h则分别放在/include/asm
§/arch/arm/boot 自解压相关代码(不同的片上系统又有不同的自解压代码,我们用的是 ./compressed/head.s)。
§/arch/arm/kernel 内核的入口点。激活处理器、启动init线程等。内核刚启动时运行此目录下的 head.S (后缀armo—26位的老式的ARM,armv—32位) entry-armv.S 异常处理、中断处理的代码。
§/arch/arm/mm linux核心实现内存管理中体系结构相关的部分。
§/arch/arm/configs 各种公板默认的配置文件
§/arch/arm/fastfce,rwfce 用软件模拟浮点运算。
§/arch/arm/Lib 与CPU相关的函数库
§/arch/arm/Mach-。。。 各种CPU的一些相关实现。
/crypto 这是内核本身所用的加密 API。还有一些压缩和CRC校验算法。
Documentation 文档。这个目录中包含很多关于配置内核、运行 ramdisk 等任务的实用信息(但通常是过时的)。不过,与不同配置选项相应的帮助条目并不在这里 —— 它们在每个源代码目录的 Kconfig 文件中。
/drivers 在此目录的子目录中可以找到运行外围设备的代码。包括视频驱动程序、网卡驱动程序、底层 SCSI 驱动程序,以及其他类似的驱动程序。例如,在 drivers/net 中可以找到大部分网卡驱动程序。
/Firmware firmware 名字含义是固件,Linux 引用这个功能可能从2.6.x开始;
它的作用是让硬件驱动通过kernel加载设备固件驱动;
在实际应用中,我们系统中PCI,USB设备或许需要自己的驱动固件,
设备只有通过这个固件才能驱动自己;由于固件也在升级中,或者因为生产时
减少烧录固件这道工序,所以人们选择在加载驱动的时候通过程序将固件
传给设备. 所以Linux 提供了 firmware load 函数给驱动程序使用.
现在的网卡,尤其是智能网卡、高速网卡,硬件性能越来越强大,承载的功能也越来越多。开发者对网卡内部功能的增加或修改,对已知bug的修正都离不开对网卡Firmware的更新。Linux操作系统对网卡 Firmware的更新提供了一整套机制,允许网卡驱动在必要时可以动态加载新的网卡Firmware。最新的内核中,动态加载Firmware的功能被加入到了Ethtool框架中,使得在用户空间加载网卡Firmware的操作标准化。
/fs 子目录包含了所有的文件系统实现的代码。通用文件系统的代码(称做 VFS,即 Virtual File System)和各个不同文件系统的代码都可以在这个目录中找到。ext2 文件系统是在 Linux 中最常广泛使用的文件系统之一。
/include 子目录包含了建立内核代码时所需的大部分包含文件。
Asm 为链接目录,编译之后会链接到具体的asm-XXX。
/init 子目录包含了内核的初始化代码。
/ipc IPC 的意思是 进程间通信(interprocess communication)。它包含了共享内存、信号量以及其他形式 IPC 的代码。
/kernel 不适合放在任何其他位置的通用内核级代码位于此处。这里有高层系统调用代码,调度程序、信号处理代码,等等。文件名包含很多信息。
/lib 库文件.。这里是对所有内核代码都通用的实用例程。常见的字符串操作、调试例程,以及命令行解析代码都位于此处。
/mm 子目录包含了所有内存管理代码. 这个目录中是高层次内核管理代码。联合使用这些例程以及底层的与体系结构相关的例程(通常位于 arch//mm/ 目录中)来实现虚拟内存(Virtual memory,VM)。在这里会完成早期内存管理(在内存子系统完全建立起来之前需要它),以及文件的内存映射、页高速缓存管理、内存分配、RAM 中页的清除(还有很多其他事情)。
/net子 目录包含了内核的网络连接代码 。这里是高层网络代码。底层网络驱动程序与此层次代码交换数据包,这个层次的代码可以根据数据包将数据传递给用户层应用程序,或者丢弃数据,或者在内核中使用它。net/core 包含大部分不同的网络协议都可以使用的代码,和某些位于 net/ 目录本身中的文件一样。特定的网络协议在 net/ 的子目录下实现。例如,在 net/ipv4 目录中可以找到 IP(版本 4)代码。
/scripts 这个目录中包含的脚本可用于内核的构建,但并不将任何代码加入到内核本身之中。例如,各种配置工具可以将它们的文件放在这里。
/security 在这里可以找到不同 Linux 安全模型的代码,主要包含SELinux模块。
 /sound 这里放置的是声卡驱动程序和其他常用设备驱动。
/usr 此目录中的代码用于构建包含 root 文件系统映像的 cpio-格式 的归档文件,用于早期用户空间。
由下图可以看出,LINUX内核主要由进程调度、内存管理、虚拟文件系统、网络接口和进程间通讯等5个子系统组成。

进入arch目录,每个体系结构代码都有一个子目录

进入arm目录,在arm体系结构下我们可以看到很多sub-arch的子目录

三、 内核配置
1、编译内核需要考虑:
自己定制编译的内核运行更快
节省内存、系统将拥有更多的内存
不需要的功能编译进入内核可能会增加被系统攻击者利用的漏洞
将某种功能编译为模块方式会比编译到内核内的方式速度要慢一些。
2、内核配置系统
Linux内核有上千个配置选项,配置复杂。通过配置系统简化内核配置。
内核配置系统可生成内核配置菜单。
配置系统包含:
Makefile
Kconfig 配置文件
配置工具
内核配置方法:
(1)克隆建立自己的目标平台
(a)将 linux-2.6.32.2/arch/arm/mach-s3c2440/目录下的 mach-smdk2440.c 复制一份。命名为mach-mini2440.c
(b)看机器码和时钟要不要修改。
(2)直接修改Kconfig —一个选项通常对应一个功能模块
(3)参考公板\arch\arm\configs\ s3c2410_defconfig的配置拷贝自己的配置,并导入菜单。
(4)直接修改菜单
通过(2)-(4)作更改的结果都会保存到.config中,可在菜单中更改一些选项,保存退出后把.config和原来的.config比较看更改的效果。
MAKEFILE会根据.config文件的配置决定编译哪些文件。
(5) 添加或者修改设备驱动

1、Makefile
分布在 Linux 内核源代码中的 Makefile,定义 Linux 内核的编译规则。
顶层目录的Makefile管理整个Linux内核的配置编译。
顶层 Makefile 递归的进入到内核的各个子目录中,分别调用位于这些子目录中的 Makefile。至于到底进入哪些子目录,取决于内核的配置。在顶层 Makefile 中,有一句:include ( s r c t r e e ) / a r c h / (srctree)/arch/ (srctree)/arch/(SRCARCH)/Makefile,包含了特定 CPU 体系结构下的 Makefile,这个 Makefile 中包含了平台相关的信息。
位于各个子目录下的 Makefile 同样也根据 .config 给出的配置信息,构造出当前配置下需要的源文件列表。
Makefile 的作用是根据配置的情况,构造出需要编译的源文件列表,然后分别编译,并把目标代码链接到一起,最终形成 Linux 内核二进制文件。

(2)配置工具
包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config、Make menuconfig 和 make xconfig)。
  这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统。
不同的内核配置方式,通过不同的配置工具完成。script目录下提供了这些配置工具。如menuconfig目标使用mconf(script/kconfig/)。
用到的配置工具有:
◆ config 基于交互式的文本配置界面。每个问题以线形格式出现,并被一个一个地回答,而且一旦作出了回答就不能再修改了。
◆ oldconfig 同config相似,但是使用原有的配置文件,而且只会提问有关新内核特性的问题,对于内核升级很方便。
◆ menuconfig 一个文本模式、选单驱动的配置界面。
◆ xconfig 基于Tcl/Tk的X图形配置界面。
现在开始配置内核,使用的工具为menuconfig。在命令行模式下执行下面的命令:
#make menuconfig

(3)Kconfig文件
arch/$(ARCH)/Kconfig文件是主Kconfig文件,主Kconfig文件调用其他目录的Kconfig文件。
这些Kconfig文件形成树状关系–>树状菜单。

选择相应的配置时,有三种选择,它们分别代表的含义如下:
  Y--将该功能编译进内核
  N--不将该功能编译进内核
  M--将该功能编译成模块,可以在需要时动态插入到内核中。
选项中是[ ]的,要么是空,要么是“” ;
是<>的,可以是空、“
” 或者“M”
是()的,可以在所提供的几个选项中选择一项。

配置的原则
大部分选项可以使用其缺省值,只有小部分需要根据用户不同的需要选择。
将与内核其它部分关系较远且不经常使用的部分功能代码编译成为可加载模块。
有利于减小内核的长度,减小内核消耗的内存;
不需要的功能就不要选;
与内核关心紧密而且经常使用的部分功能代码直接编译到内核中。

3、菜单项:
(1)、单个配置选项的定义:关键字是config,后面几行是这个选项的属性。

config ARCH_RPC //定义的宏
bool “RiscPC” // 选项的类型 选择提示
select ARCH_ACORN //依赖关系 depends 依赖关系
select FIQ
select TIMER_ACORN
help //帮助信息
On the Acorn Risc-PC, Linux can support the internal IDE disk and
CD-ROM interface, serial and parallel port, and the floppy drive.
default y //默认值
range 2 32 //数字范围

(2)、菜单依赖关系语法说明:
= 相等则返回 “y”,否则”n”;
!=相等则返回 “n”,否则”y”;
( ) 返回表达式的值;
&& 返回 min 的结果
||返回 max 的结果

当表达式的值为 “m”“y”时,菜单显示。为“n”不显示。

(3)、菜单的组织结构
1)显示地声明为菜单
Menu “Network device support”
Depends NET
Config NETDEVICES
……
Endmenu

Menu 与Endmenu 之间 构成Network device support的子菜单,所有子项继承这菜单的依赖关系。

2)通过依赖关系确定菜单的结构
config REISERFS_FS
tristate “Reiserfs support”
help
Stores not just filenames but the files themselves in a balanced
tree. Uses journaling.

config REISERFS_CHECK
bool “Enable reiserfs debug mode”
depends on REISERFS_FS

REISERFS_CHECK依赖于REISERFS_FS,只有REISERFS_FS不是“n”,才显示。

各配置选项的意思见〈Linux内核的主要配置〉文档。

3)添加与开发板平台支持的选项:
/arch/arm/kconfig 是内核主配置文件。从文件中可以找到:
menu “System Type” #定义了各个菜单项
choice #choice 语句可以在菜单中生成一个多选项
prompt “ARM system type” #prompt 选择提示 #系统平台选择项列表
default ARCH_RPC
。。。
config ARCH_S3C2410
bool “Samsung S3C2410” #对于S3C2410 处理器的支持
。。。
source “arch/arm/mach-s3c2410/Kconfig” #定义了各种S3C2410处理器开发板的选项,和其支持的选项。

4)除了手动配置菜单外,还可以通过导入预配置文件(顶层目录的 .config)进行配置,菜单会自动读取指定的配置文件:

5)/arch/arm/mach-s3c2410 目录专门用来保存 s3c2410系列处理器相关的程序。
其中:1、makefile和kconfig 是用于内核编译的。
2、处理器相关的,如:clock.c, clock.h, cpu.c, cpu.h, s3c2410.c

内核中已经支持了各种平台。在Makefile 中可以通过配置来选择编译不同的目录。/arch/arm/makefile 完成这项任务:

machine-KaTeX parse error: Expected 'EOF', got '#' at position 48: …410 #̲定义machine-y = s…(machine-y),)
//MACHINE := arch/arm/mach-KaTeX parse error: Expected 'EOF', got '#' at position 22: …ne-y)/ #̲包含mach-s3c2410 …(word 1,$(machine-y))/ //2.6.32
else
MACHINE :=
endif

然后编译生成顶层的vmlinux 映像。再由vmlinux压缩为zImage.

四、Kbuild Makefile

Makefile 包含5部分:
1、.config 内核配置文件
2、Arch/arm/Makefile 对应的体系结构的Makefile
3、顶层目录的Makefile
4、Scripts/makefile.* 所有Kbuild Makefile 的通用规则等定义
5、各目录下的Makefile

Kbuild 编译过程:
1、取得.config
2、保存版本信息
3、创建连接符号 include/asm ,连接 /include/asm-$(ARCH)
4、递归地调用各子目录编译所有目标
5、生成顶层目录 vmlinux
6、编译生成最终的引导映像

五、编译连接内核

先编译连接生成顶层目录的vmlinux ,再把vmlinux 压缩和加上自引导程序连接成 /arch/($ARCH)/boot/zImage。

1、编译连接vmlinux
2、生成vmlinux.lds连接脚本(由vmlinux.lds.S生成)。
(了解 arch/arm/kernel/vmlinux.lds.S)
3、连接生成zImage
在 arch/arm/Makefile 中:

zImage 的前提是vmlinux,vmlinux编译通过了才能生成zImage

zImage Image xipImage bootpImage uImage: vmlinux
4、system.map
system.map 是一个特定内核的符号表,它包含内核全局变量和函数的地址信息。是编译过程中生成的。

六、内核启动过程:

1、如果使用mkimage生成内核镜像文件的话,会在内核的前头加上了64byte的信息,供建立tag之用。bootm命令会首先判断bootm xxxx 这个指定的地址xxxx是否与-a指定的加载地址相同。而这个tag建议是由bootloader提供的,在u-boot下默认是由bootm命令建立的.
(1)、在make zImage之后,用u-boot/tools/mkimage这个工具为内核加上u-boot引导所需要的文件头:
/mini2440/u-boot-201003-suok/tools/mkimage -n ‘linux-2.6.32’ -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage.img
(2)、下载内核时:
nand erase 0x80000 300000
nand write 0x30008000 0x80000 300000
set bootcmd ‘nand read 0x30008000 0x80000 300000;bootm 0x30008000’
2、典型的内核映象是zImage,它是经过压缩的、具备自引导能力的linux内核映象。当CPU跳转到zImage的时候,第一个执行的是自引导程序;
(1)自引导程序的主要责任是解压zImage中的vmlinux,并且引导vmlinux;
(2)自引导程序的入口在 arch/$(ARCH)/boot/compressed/head.S文件中。
它将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,然后使用在打印出信息“Uncompressing Linux…”后,调用gunzip()。将内核放于指定的位置。

3、 CPU跳转到vmlinux的入口地址,顺序执行内核启动程序,其中包括:
(1)初始化内核各个子系统;
(2)初始化驱动程序;
(3)挂载根文件系统;
(4)启动用户空间的init进程等。
内核首先检查在命令行参数中有没有指定内核执行的第一个应用程序;
如果没有指定,则顺序执行/sbin/init, /etc/init, /bin/init, /bin/sh;
4、 Linux内核在挂载根文件系统之后,要执行文件系统中的应用程序。

具体代码如下:
1、Start:->2、decompress_kernel->3、call_kernel->
/arch/arm/boot/compress/head.s/(其中decompress_kerne 是在/arch/arm/boot/compress /misc.c 实现)

start: // 1

wont_overwrite: mov r0, r4
mov r3, r7
bl decompress_kernel // 2 调用解压内核映像的C函数
// 在 arch/arm/boot/compressed/misc.c 实现
b call_kernel // 3 调用跳转到内核映像入口的子程序

call_kernel: bl cache_clean_flush // 3
bl cache_off
mov r0, #0
mov r1, r7 @ restore architecture number
mov pc, r4 @ call kernel // 5 调用内核

  • r4 = kernel execution address // 6 内核执行的地址

arch/arm/boot/compressed/misc.c
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, // 2
int arch_id)
{
arch_decomp_setup(); /解压前的初始化/
makecrc(); /CRC 校验/
putstr(“Uncompressing Linux…”);
gunzip(); /*调用解压函数 */
putstr(" done, booting the kernel.\n");
return output_ptr;
}

现在总结一下在进入解压后的内核入口前都做了些什么:
(1)保存从uboot中传入的参数
(2)执行一段处理器相关的代码
(3)接着会判断一下要不要重定位,我们这里是不需要重定位,所以开始对bss段清零。
(4)之后初始化页表,进行1:1映射。因为打开cache前必须打开mmu,所以这里先对页表进行初始化,然后打开mmu和cache。
(5)这些都准备好后,判断一下解压内核是否会覆盖未解压的内核映像。如果会,则进行一些调整,然后开始解压内核;如果不会,则直接解压。最后是刷新cache,关闭mmu和dcache,使 cache和tlb内容无效,跳到解压后的内核入口执行arm相关的内核代码。
4、Stext->5、b start_kernel->6、setup_arch()->7、rest_init()->8、init()->9、do_basic_setup()->10、prepare_namespace()->11、execve()
/arch/arm/kernel/head.s

ENTRY(stext) /* 4 内核入口点 */
。。。
#include “head-common.S”

/arch/arm/kernel/head-common.S
b start_kernel /* 5 由此跳到C 代码 */

/init/main.c
asmlinkage void __init start_kernel(void) /* 5 C 代码的入口 /
{…
setup_arch(&command_line); /
6 激活处理器 /

rest_init(); /
7 调用此函数,启动 init 线程*/
}

static void noinline rest_init(void)
{
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); /*3 启动init 线程 */……
}

tatic int __init kernel_init(void * unused)
{
do_basic_setup(); /* 9 初始化设备驱动*/
……………….
prepare_namespace(); /* 10 挂载根文件系统*/
………………….
init_post(); /* 启动进程 /
}
static noinline int init_post(void)
{
run_init_process(execute_command); /
启动用户空间的init 进程*/
/init 进程是通过执行根文件系统中的init程序启动的/
/*接下来顺序执行/sbin/init, /etc/init, /bin/init, /bin/sh; */
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic(“No init found. Try passing init= option to kernel.”);
}
init的进程号是1,从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序。init程序需要读取配置文件/etc/inittab。

static void run_init_process(char init_filename) //内核首先检查在命令行参数中有没有指定内核执行的第一个应用程序;
{
argv_init[0] = init_filename;
kernel_execve(init_filename, argv_init, envp_init); /
11 */
}


/arch/arm/kernel/sys_arm.c
int kernel_execve(…)
{
…………
ret = do_execve((char *)filename, (char __user * __user *)argv,
(char __user * _user )envp, &regs); / do execve 是执行用户空间的程序 */
}

/fs/exec.c
int do_execve(char * filename,…) executes a new program.
{

}

七 、 系统环境变量的设置方法
1、配置路径
#echo $PATH

vi ~/.bashrc

在其内添加:
export PATH=/usr/local/arm/4.3.2/bin:$PATH
#reboot

2、在编译的时候配置
#export PATH=/usr/local/arm/3.4.1/bin:$PATH

注意:
如果修改了 .bashrc路径,需要重新启动linux

3、编译之前显示编译的版本信息
#arm-linux-gcc –v

八、实验步奏(MINI2440的)
1、cp config_mini2440_x35 .config
2、进入内核根目录看Makefile中关于编译器的前缀是否如下(这里是OK的,不用改):

3、查看机器码和UBOOT中的是否一致,一致内核才能启动(这里是OK的,不用改):
uboot/include/asm-arm/mach-types.h #define MACH_TYPE_MINI2440 1999 2.6.32/arch/arm/tools/mach-types mini2440 MACH_MINI2440 MINI2440 1999
查看UBOOT的信息也可看到:
即0x7cf=1999
4、make menuconfig
5、make zImage
进入/arch/arm/boot目录,用u-boot/tools/mkimage这个工具为内核加上u-boot引导所需要的文件头:
/mini2440/u-boot-201003-suok/tools/mkimage -n ‘linux-2.6.32’ -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage.img
(解释一下参数的意义:(具体可读mkimage使用详解.docx )
-A ==> set architecture to ’arch’
-O ==> set operating system to ’os’
-T ==> set image type to ’type’
-C ==> set compression type ’comp’
-a ==> set load address to ’addr’ (hex) 参数后是内核的运行地址
-e ==> set entry point to ’ep’ (hex) 参数后是入口地址
-n ==> set image name to ’name’
-d ==> use image data from ’datafile’

1)如果我们没用mkimage对内核进行处理的话,那直接把内核下载到0x30008000再运行就行,内核会自解压运行(不过内核运行需要一个tag来传递参数,而这个tag建议是由bootloader提供的,在u-boot下默认是由bootm命令建立的)。
2)如果使用mkimage生成内核镜像文件的话,会在内核的前头加上了64byte的信息,供建立tag之用。bootm命令会首先判断bootm xxxx 这个指定的地址xxxx是否与-a指定的加载地址相同。
(1)如果不同的话会从这个地址开始提取出这个64byte的头部,对其进行分析,然后把去掉头部的内核复制到-a指定的load地址中去运行之
(2)如果相同的话那就让其原封不同的放在那,但-e指定的入口地址会推后64byte,以跳过这64byte的头部。)

6、下载内核
tftp 0x30008000 uImage.img
(1)下载完后可烧进flash
nand erase 0x80000 300000
nand write 0x30008000 0x80000 300000
set bootcmd ‘nand read 0x30008000 0x80000 300000;bootm 0x30008000’
saveenv
reset
(2)也可以直接跳到刚才下载内核的地址去执行
Bootm 0x30008000

7、设置启动参数
setenv bootargs root=/dev/nfs nfsroot=192.168.17.253:/opt/rootfs/ ip=192.168.17.100 init=/linuxrc console=ttySAC0,115200 display=sam320
saveenv
reset
6、对W35屏的补充:W35屏是320240的,而X35是240320的,所以如果是W35的屏,应对内核的/arch/arm/mach-s3c2440/mach-mini2440.c 作如下修改,其他的不变:
#elif defined(CONFIG_FB_S3C2410_X240320)
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
为:
#elif defined(CONFIG_FB_S3C2410_X240320)
#define LCD_WIDTH 320
#define LCD_HEIGHT 240
八、实验步奏(GEC2440的)
1、安装3.4.1 工作链
2、配置路径
3、解压内核原码
#mkdir /root/build_kernel
#mkdir /root/build_kernel/linux (路经无关)
#cd /root/build_kernel/linux

拷贝gec2440-linux-2.6.12.tar.bz2
#tar -jxvf gec2440-linux-2.6.12.tar.bz2

4、查看makefile

vi makefile

查看选项:
ARCH :=arm //即打开arm特有的代码
CROSS_COMPILE :=/usr/local/arm/3.4.1/bin/arm-linux-
如果不是,则改过来。

5、使用默认的配置文件
#cp /arch/arm/configs/smdk2410_defconfig .config
(.config 会自动转换成 include/linux/autoconf.h 头文件。在 include/linux/config.h 文件中,将包含使用 include/linux/autoconfig.h 头文件.
另外,.config文件也会在下面的配置菜单中导进来。

(每种参考板都有省缺内核配置文件,在/arch/$(ARCH)/configs 目录下。Smdk2410的省缺文件就是/arch/arm/configs/smdk2410_defconfig )
或者执行如下命令使省缺配置生效:
#make smdk2410_defconfig

6、打开配置菜单,重新调整配置菜单
#make menuconfig (此时很多警告!)

SYSTEM TYPE ---->
(0) S3C2410 UART to use for low-level messages
(ctrl + 数据)
7、编译:
#make

编译结束,将生成 /vmlinux及 arch/arm/boot/zImage 等。

五、下载内核
#tftp 30008000 zImage
#nand erase 40000 1c0000
#nand write 30008000 40000 1c0000

#setenv bootcmd nand read 30008000 40000 1c0000;go 30008000
#saveenv

补充:Makefile 中变量的赋值
1、“=” 直接赋值

2、“:=” 前面的变量不能使用后面的变量。
例如: y:=$(x)bar
X:=foo
则 y=bar 而不是 y=foobar

3、“?=” 例如: foo ?= bar
如果foo 没被定义过,则 foo = bar
否则 用之前定义过的值

4、“+=” 将右边的变量值附加给左边的变量
例如 foo = string1
Foo+=string2
则 foo = string1string2

Logo

更多推荐