1、移植的基本概念
移植是指將軟體從一個平台遷移到另一個平台,包括以下幾個方面:
-- 從一個硬體平台移植到另一個硬體平台;
-- 從一個作業系統移植到另一個作業系統;
-- 從一種軟體庫環境移植到另一個軟體庫環境。
2、Linux 硬體平台
在 Linux 核心裡,每一個處理器指令集對應一個獨立的體繫結構(architecture),比如 alpha、arm、i386、mips、
ppc。每個體繫結構可以有若干變種 variant,或不同配置的硬體 machine,統稱 sub-architecture。以 ARM 體繫結
構舉例:
-- variants 包括 arm7tdmi、arm926ejs、strongarm、xscale;
-- machine 包括 edb7312、smdk2410、omap-h2。
3、硬體平台對 C 程式的影響
處理器字長,定義為處理器一次能處理的資料位元數。字長等於處理器內部資料通路的寬度,一般可以通過通用寄存器的寬度來判斷。處理器字長會影響 int、
long 等 C 類型的長度。C 代碼當中需要使用確定大小的資料類型,可以使用顯式長度的類型,如:u8、s8、u16、s16、u32、s32、
u64、s64。
4、資料對齊
資料對齊是指資料區塊的地址是某個特定大小的整數倍,如:
-- 32 位處理器字對齊 n*4
-- 頁對齊 n*PAGESIZE
-- Cache line 對齊 n*CLINESIZE
資料訪問要求至少是字對齊的,多數情況下編譯器會處理資料訪問的對齊。不對齊訪問的例子:
-- char a[10];
-- unsigned long* pl = (unsigned long *)(a+1);
-- unsigned l = *pl;
5、位元組順序
位元組順序 byte order 是指一個字中位元組排列的順序。不同硬體可能採用不同的 byte order,如:
-- x86 little-endian
-- ppc big-endian
Linux
核心將硬體的 byte order 放在 <asm/byteorder.h> 裡面定義,__BIG_ENDIAN 或
__LITTLE_ENDIAN。在 include/linux/byteorder/ 裡面有幾個標頭檔,定義了:
-- u23 __cpu_to_be32(u32);/* convert cpu's byte order to bigendian*/
-- u32 __cpu_to_le32(u32);/* convert cpu's byte order to littleendian*/
-- u32 __be32_to_cpu(u32);/* convert big-endian to cpu's byte order */
-- u32 __le32_to_cpus(u32);/* convert little-endian to cpu's byte order */
6、時間
軟體中與時間相關的代碼也會影響移植。採用平台無關的時間表達方法可以提高代碼可移植性。Linux 核心裡面採用 HZ 來表示每秒鐘有多少個內部時鐘滴答,以下對時間的描述是平台無關的:
-- HZ /* one second */
-- (2*HZ) /* two seconds */
-- (HZ/2) /* half a second */
-- (HZ/100) /* 10 ms */
-- (2*HZ/100) /* 20 ms */
7、記憶體頁面大小
Linux
使用虛擬記憶體機制來管理記憶體,記憶體的使用基於頁面。不同的體繫結構有不同的頁面大小。常用的32位處理器使用4kB頁面大小。部分體繫結構也可以支援多種
頁面大小。核心在 <asm/page.h> 裡面定義 PAGE_SIZE 和 PAGE_SHIFT:
-- PAGE_SIZE 表示頁面大小;
-- PAGE_SHIFT 表示頁面號在地址中的位移量;
-- PAGE_SIZE = 2^ PAGE_SHIFT。
8、Linux 作業系統移植
(1)工具鏈移植
-- binutils (assembler, linker..)
-- gcc (compiler, libgcc)
-- glibc/uclibc
(2)核心移植
-- arch implementation
-- drivers porting
(3)應用程式移植
-- C program recompile
-- Implement absent library
9、Linux 核心的平台相關代碼
Linux 核心對多平台有很好的支援。核心的對外部介面是統一的,並且與平台無關。核心的大多數代碼也是與平台無關的。主要的體繫結構相關代碼存在於:
-- arch/architecture
-- include/asm-architecture
比如 ARM 體系的平台相關代碼主要是:
-- arch/arm
-- include/asm-arm
10、已有代碼向 Linux 核心移植
將已有代碼向核心中移植有一些限制:
(1)核心中沒有標準 C 庫支援;
(2)核心中沒有象使用者程式那樣的記憶體保護;
(3)核心中不便使用浮點操作;
(4)核心的堆棧是固定大小的,並且比較有限;
(5)在核心中需要編程者考慮並髮帶來的競爭與冒險,以及同步問題。
11、Linux 核心移植
Linux 核心代碼可以分為平台相關部分和平台無關部分。Linux 核心絕大部分代碼是平台無關的,可以被各種平台所共用,如:
-- 調度演算法
-- 儲存空間管理
-- I/O子系統
-- 網路通訊協定棧
依賴於特定硬體的代碼在 Linux 中採用條件編譯的方式區分:
-- ARCH = x86 即開啟 x86 特有的代碼
-- ARCH = arm 即開啟 arm 特有的代碼
12、Linux 核心的 arch 目錄
進入 arch 目錄,每個體繫結構代碼都有一個子目錄:
進入 arm 目錄,在 arm 體繫結構下可以看到很多 sub-arch 的子目錄:
13、實現 sub-arch
在 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
14、mach-smdk2410
在 mach-smdk2410.c 中,我們要定義以下幾個內容:
smdk2410_iodesc,描述了所有保留的裝置 IO 地址。這個描述符是我們移植一個特定目標板非常重要的地方。在這個描述檔案中還要定義:
-- phys_ram
-- phys_io
-- io_pg_offst
-- boot_params
-- map_io
-- init_irq
-- timer
15、map_io
map_io 裡面需要實現裝置 IO 的初始化。在這裡要用到 smdk2410_iodesc 描述符。該描述符是一個數組,其中每一項都描述了一個裝置的 IO 映射。另外,時鐘 PLL 的設定,UART 的設定都可以在 map_io 中調用。
16、init_irq
在這個調用裡面,關於中斷的初始化將會被完成:
-- 清除中斷 pending 寄存器
-- 註冊主要的中斷處理常式
-- 設定系統中的裝置中斷
17、timer
timer 是一個 sys_timer 類型的結構,它包含以下成員:
-- init,調用執行硬體相關的timer初始化;
-- offset,調用返回自從上次 timer 中斷以來經過的微秒數;
-- resume,調用執行系統喚醒後的timer恢複操作,一般實現上和init裡面的初始化一樣。
18、應用程式移植
最理想情況下,程式可以不作更改,或僅僅打一些補丁,然後告訴編譯環境按照目標環境要求編譯即可。
-- busybox
-- bash
-- sysv init
依賴某些平台特性的應用程式移植起來往往難度更大。如:
-- 圖形庫
-- 為速度進行最佳化的代碼,比如轉碼器
軟體程式設計語言的跨平台性直接影響軟體的可移植性。此外還有其他因素,如軟體協議/原始碼的開放程度。
19、應用程式移植常見問題
依賴軟體造成移植性問題:
-- C庫版本問題
-- 圖形庫帶來的問題
-- 軟體依賴某些服務帶來問題
網路應用在 little-endian 平台上的處理,網路傳遞資料是 big-endian 的。
軟體依賴特定平台的特性。
平台的資料一致性模型差異。