嵌入式Linux的移植的理论问题
2010年12月24日
[b]移植的基本概念:
[/b]移植是指将软件从一个平台迁移到另一个平台
Ø从一个硬件平台移植到另一个硬件平台
Ø从一个操作系统移植到另一个操作系统
Ø从一种软件库环境移植到另一个软件库环境
软件进行移植的容易程度即可移植性
[b]Linux硬件平台:
[/b]在Linux内核里,每一个处理器指令集对应一个独立的体系结构architecture,比如alpha, arm,i386, mips, ppc
每个体系结构可以有若干变种variant,或不同配置的硬件machine
统称sub-architecture。以arm体系结构举例
Øvariants 包括arm7tdmi, arm926ejs, strongarm,xscale
Ømachine 包括edb7312, smdk2410, omap-h2
[b]硬件平台对C程序的影响:
[/b]处理器字长,定义为处理器一次能处理的数据位数。
字长等于处理器内部数据通路的宽度,一般可以通过通用寄存器的宽度来判断
处理器字长会影响int, long等C类型的长度
C代码当中需要使用确定大小的数据类型,可以使用显式长度的类型u8, s8, u16, s16, u32, s32, u64, s64
[b]数据对齐:[/b]
数据对齐是指数据块的地址是某个特定大小的整数倍
Ø32位处理器字对齐n*4
Ø页对齐n*PAGESIZE
ØCache line对齐n*CLINESIZE
数据访问要求至少是字对齐的,多数情况下编译器会处理数据访问的对齐。不对齐访问的例子
Øchar a[10];
Øunsigned long* pl = (unsigned long *)(a+1);
Øunsigned l = *pl;
[b]字节顺序:
[/b]字节顺序byte order是指一个字中字节排列的顺序
不同硬件可能采用不同byte order
Ø x86 little-endian
Ø ppc big-endian
Linux内核将硬件的byte order放在 里面定义,__BIG_ENDIAN或__LITTLE_ENDIAN
在include/linux/byteorder /里面有几个头文件,定义了
Ø u23 __cpu_to_be32(u32);
Ø u32 __cpu_to_le32(u32);
Ø u32 __be32_to_cpu(u32);
Ø u32 __le32_to_cpus(u32);
[b]时间:[/b]
软件中的与时间相关的代码也会影响移植
采用平台无关的时间表达方法可以提高代码可移植性
Linux内核里面采用HZ来表示每秒钟有多少个内部时钟滴答,以下对时间的描述是平台无关的
Ø HZ
Ø (2*HZ)
Ø (HZ/2)
Ø (HZ/100)
Ø (2*HZ/100)
[b]内存页面大小:
[/b]Linux使用虚拟内存机制来管理内存,内存的使用基于页面。
不同的体系结构有不同的页面大小
常用的32位处理器使用4kB页面大小
部分体系结构可以支持多种页面大小
内核在里面定义PAGE_SIZE,PAGE_SHIFT
Ø PAGE_SIZE表示页面大小
Ø PAGE_SHIFT表示页面号在地址中的偏移量
Ø PAGE_SIZE=2^ PAGE_SHIFT
[b]Linux操作系统移植:[/b]
工具链移植
Øbinutils (assembler, linker..)
Øgcc (compiler, libgcc)
Øglibc/uclibc
内核移植
Øarch implementation
Ødrivers porting
应用程序移植
ØC program recompile
ØImplement absent library
[b]Linux内核的平台相关代码:[/b]
Linux内核对多平台有很好的支持 内核的对外部接口是统一的,并且与平台无关
内核的大多数代码也是与平台无关的主要的体系结构相关代码存在于
Øarch/architecture
Øinclude/asm-architecture
比如arm体系的平台相关代码主要是
Øarch/arm
Øinclude/asm-arm
[b]已有代码向Linux内核移植:[/b]
将已有代码向内核中移植有一些限制
Ø内核中没有标准C库支持
Ø内核中没有象用户程序那样的内存保护
Ø内核中不便使用浮点操作
Ø内核的堆栈是固定大小的,并且比较有限
Ø在内核中需要编程者考虑并发带来的竞争与冒险,以及同步问题
[b]Linux内核移植:
[/b]Linux内核代码可以分为平台相关部分和平台无关部分
Linux内核绝大部分代码是平台无关的,
可以被各种平台所共享
Ø 调度算法
Ø 存储器管理
Ø I/O子系统
Ø 网络协议栈
依赖于特定硬件的代码在Linux中采用条件编译的方式区分
Ø ARCH = x86 即打开x86特有的代码
Ø ARCH = arm 即打开arm特有的代码
[b]Linux内核的arch目录:[/b]
进入arch目录,每个体系结构代码都有一个子目录
进入arm目录,在arm体系结构下我们可以看到很多sub-arch的子目录
[b]实现sub-arch:[/b]
在sub-arch子目录下,以mach-s3c2410为例 一个硬件平台支持需要实现以下几个硬件相关的文件
Ø mach-s3c2410.c, irq.c, clock.c, dma.c, gpio.c, pm.c,sleep.c, time.c
Ø 同时在include/asm-arm/arch-s3c2410要实现
Low-level IRQ helper macros
Debug output macros
Irq number definations
DMA definations
Memory mapping/translation
Reset operation
IDLE function
[b]mach-smdk2410.c:[/b]
在mach-smdk2410.c中,我们要定义以下几个内容
smdk2410_iodesc,描述了所有保留的设备io地址。这个描述符是我们移植一个特定目标板非常重要的地方
在这个板描述文件中还要定义
.phys_ram
.phys_io
.io_pg_offst
.boot_params
.map_io
.init_irq
.timer
[b]map_io:[/b]
map_io里面需要实现设备io的初始化
在这里要用到smdk2410_iodesc描述符。该描述符是一个数组,其中每一项都描述了一个设备的IO映射
时钟pll的设置、uart的设置都可以在map_io中调用
[b]init_irq:[/b]
在这个调用里面,关于中断的初始化将会被完成
Ø清除中断pending寄存器
Ø注册主要的中断处理程序
Ø设置系统中的设备中断
[b]timer:[/b]
timer是一个sys_timer类型的结构,它包含以下成员
-init 调用执行硬件相关的timer初始化
-offset 调用返回自从上次timer中断以来经过的微秒数
-resume 调用执行系统唤醒后的timer恢复操作,一般实现上和init里面的初始化一样
[b]应用程序移植:
[/b]最理想情况下,程序可以不作更改,或仅仅打一些补丁,然后告诉编译环境按照目标环境要求编译即可
Øbusybox
Øbash
Øsysv init
依赖某些平台特性的应用程序移植起来往往难度更大
Ø图形库
Ø为速度进行优化的代码,比如编解码器
软件编程语言的跨平台性直接影响软件的可移植性。此外还有其他因素
软件协议/源代码的开放程度
[b]应用程序移植常见问题:
[/b]1依赖软件造成移植性问题
ØC库版本问题
Ø图形库带来的问题
Ø软件依赖某些服务带来问题
2网络应用在little-endian平台上的处理
Ø网络传递数据是big-endian的
3软件依赖特定平台的特性
4平台的数据一致性模型差异