Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« February 2007 | (回到Blog入口) | April 2007 »

March 2007 歸檔

March 5, 2007

ELF 之 Program Loading 教學文件, #1: Segment 的觀念

「Program Loading」的議題在討論「如何將程式載入記憶體」,以便後續的「執行」。在「ELF(Executable and Linking Format)格式教學文件」第 1~8 篇文章裡,我們了解基本的 ELF 觀念,並建立所謂的「節區」知識。

本系列日記「ELF 之 Program Loading 教學文件」將會介紹有關程式載入(program loading)的核心主題,在此之前,請先閱讀 Jollen 的「Executable and Linking Format」專欄,以基本基礎的先備知識。

Segments

由 "execution view" 的角度來看程式(即 process),所謂的節區(section)已經被進化成區段(segment)的觀念了。廣義來說,section 可被分為以下 3 種 segment:

  • Text segment - 指存放唯讀(read-only)程式碼與資料的所有 section 。
  • Data segment - 指存放可寫(writable data)程式碼與資料的所有 section。
  • BSS segment - 即 .bss section。

另外,還有一類的 segment:

  • Dynamic segment - 用來存放 dynamic linking 資訊的 section。

不過,當我們講到「process segment」時,指的是 text segment、data segment 與 BSS segment;process address space 就是由這三種 segment 所構成。

Text segment 是由以下的 section 組成:

  • .text
  • .rodata
  • .hash
  • .dynsym
  • .dynstr
  • .plt
  • .rel.got

Data segment 是由以下的 section 組成:

  • .data
  • .dynamic
  • .got
  • .bss

依照 SystemV ABI 規格,我們看到 .bss section 被放到 data segment 裡,但嚴格來說,我們應把 .bss section 另外獨立出來討論。基本的 .bss section 觀念,可參考 Jollen 的「BSS Section Concepts」專欄。

此外,在 text segment 與 data segment 裡,包含了 4 個與 dynamic linking 有關的 section,整理如下圖。

dynamic_linking_sections.JPG

與 dynamic linking 有關的 4 個 section 並非另外組成所謂的 dynamic segment,這只是我們在研究 dynamic linking 時邏輯上的用語。

到這裡,我們了解了一個相當重要的一個觀念:當程式載入後,是以 "segment" 的觀念存在於記憶體中,並且也是以 segment 的觀念被 Linux kernel 管理。ELF 執行檔被載入到記憶體的過程中,最重要的資訊就是前導專欄裡所提到的「Program Header Table」;就 ELF execution view 來說,program header table 是必要的,下篇日記會針對此議題做介紹。

小聊 .dynstr 節區

昨天與同學小談了一下 .dynstr 節區,簡單紀錄如下。

我們知道 ld.so(dynamic linker/loader)與 ldd 指令,而這二個程式都會參考到 ELF 的 .dynstr(dynamic linking 的 string table)。以 ldd 來說,我們用 ldd 來找出 ELF 執行檔的相依動態程式庫:

# ldd hello
        libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

這個動作依賴 .dynstr 節區所提供的資訊,也就是說,當 .dynstr 的內容有誤或是不存在時,ld.so ldd 便無法正常工作。例如:

# strip -R .dynstr hello
# ldd hello
./hello: ./hello: no version information available (required by ./hello)
# ./hello
./hello: ./hello: no version information available (required by ./hello)
./hello: relocation error: ./hello: symbol , version  not defined in file  with link time reference

另外再補充一點,在 .dynamic 節區裡的列表中,包含了一筆指向 .dynstr 節區的紀錄。使用簡單的工具操作此觀念如下:

# readelf  -S hello
...
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048128 000128 000028 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          08048150 000150 000050 10   A  5   1  4
  [ 5] .dynstr           STRTAB          080481a0 0001a0 00004c 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          080481ec 0001ec 00000a 02   A  4   0  2
...

接著:

# readelf  -d hello

Dynamic segment at offset 0x414 contains 20 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048230
 0x0000000d (FINI)                       0x80483d4
 0x00000004 (HASH)                       0x8048128
 0x00000005 (STRTAB)                     0x80481a0
 0x00000006 (SYMTAB)                     0x8048150
 0x0000000a (STRSZ)                      76 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80494f0
 0x00000002 (PLTRELSZ)                   16 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048220
 0x00000011 (REL)                        0x8048218
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80481f8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80481ec
 0x00000000 (NULL)                       0x0

這裡的 "dynamic segment" 專指 ".dynamic" section,與上一篇日記提到的 "dynamic segment" 含義不同,特別一提,以避免混淆。

March 8, 2007

ELF 之 Program Loading 教學文件, #2: Program Header Table

了解系統行為的研究方法,我認為有效的步驟應分成三個階段來進行。

初次入門:一開始進行研究時,因為對於系統的基本觀念還不夠完備,因此「學中做、做中學」成了最有效率的入門方式,透過「概念的實作」與「實作讀到的概念」的過程,最可以幫助我們在短時間內掌握重要的核心知識。以 ELF 專欄為例,在入門時期,我用了 loader 0.1~0.5 共 5 個小範例來陳述 ELF 的格式以及 section 的觀念。

掌握觀念:首先發表一個自己的看法。「將所有理論或概念全部動手實作一遍」,在我看來,並不是很有效率的辦法,這種做法應當很有幫助,但是卻會延緩學習速度,因此,這個時 期的重點如果是在「通盤掌握整體的重要關鍵」,那麼「善用工具來做分析」自然是最有成 效的方式。以「工具來操作並驗證觀念」是建議的做法,另外一個理由是,這種做法比較能貼近實務面。在「ELF 之 Program Loading 教學文件」的日記裡,我將會以此做法來分享教學文件。

思考與研究:這個階段有點像是「實驗室」的做法,在這裡不再贅述。

Program Header Table

這裡有幾個重要的觀念:

1. Program header table 是程式要能執行的重要資訊,program header table 紀錄 ELF image 裡的 'segment' 分佈,請參考 Jollen's Blog「ELF 之 Program Loading 教學文件, #1: Segment 的觀念」的說明。

2. 對 dynamic loader/linker 來說,ELF 的 'section' 是通透性的(transparent),也就是在整個載入的過程裡,dynamic loader/linker 並不會知道他所載入的 'segment' 與實際的 section 有何關係。整個載入過程只讀取 program header table 所紀錄的 'segment'。

3. 也就是說,section header table 在此時期是用不到的,就算沒有 section header table 也不影響程式載入與執行;這個觀念可參考 Jollen's Blog「「Truncate It」小技倆的原始碼與原理」。

使用 readelf 來觀察執行檔的 program header table:

# readelf -l hello

Elf file type is EXEC (Executable file)
Entry point 0x8048278
There are 6 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4
  INTERP         0x0000f4 0x080480f4 0x080480f4 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00408 0x00408 R E 0x1000
  LOAD           0x000408 0x08049408 0x08049408 0x00100 0x00104 RW  0x1000
  DYNAMIC        0x000414 0x08049414 0x08049414 0x000c8 0x000c8 RW  0x4
  NOTE           0x000108 0x08048108 0x08048108 0x00020 0x00020 R   0x4

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame
   03     .data .dynamic .ctors .dtors .jcr .got .bss
   04     .dynamic
   05     .note.ABI-tag

Program header table 的 data structure 如下(/usr/include/elf.h):

/* Program segment header.  */

typedef struct
{
  Elf32_Word    p_type;                 /* Segment type */
  Elf32_Off     p_offset;               /* Segment file offset */
  Elf32_Addr    p_vaddr;                /* Segment virtual address */
  Elf32_Addr    p_paddr;                /* Segment physical address */
  Elf32_Word    p_filesz;               /* Segment size in file */
  Elf32_Word    p_memsz;                /* Segment size in memory */
  Elf32_Word    p_flags;                /* Segment flags */
  Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;

'readelf' 的列表與 program header table 的 data structure 相符,每個 field 所紀錄的資訊可參考註解說明,或是參照完整的 System V ABI 文件。

幾個重要資訊說明如下。

1. Segment 的個數與大小紀錄在 ELF 檔頭裡:

# readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048278
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1784 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         25
  Section header string table index: 24

2. Program header table 的 p_type 欄位必須先進行研究(/usr/include/elf.h):

/* Legal values for p_type (segment type).  */

#define PT_NULL         0               /* Program header table entry unused */
#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_NOTE         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define PT_PHDR         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific */

標示紅色的部份是我們在 'readelf -l' 列表中所看到的 type。

3. p_flags 的意義也要先行了解(/usr/include/elf.h):

/* Legal values for p_flags (segment flags).  */

#define PF_X            (1 << 0)        /* Segment is executable */
#define PF_W            (1 << 1)        /* Segment is writable */
#define PF_R            (1 << 2)        /* Segment is readable */
#define PF_MASKOS       0x0ff00000      /* OS-specific */
#define PF_MASKPROC     0xf0000000      /* Processor-specific */

Segment Permissions

Segment permission 是 p_flags 的組合,在 'readelf -l' 列表中則是相對應於 Flg 一欄。

Segment Contents

Segment 由哪些 section 所構成,同樣可由 'readelf -l' 列表的「Section to Segment mapping」部份觀察。

小結

以下表格整理目前為止所得到的結論。

Segment Sections Type Flg
00   PHDR PF_R + PF_X
01 .interp INTERP PF_R
02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame LOAD PF_R + PF_X
03 .data .dynamic .ctors .dtors .jcr .got .bss LOAD PF_R + PF_W
04 .dynamic DYNAMIC PF_R + PF_W
05 .note.ABI-tag NOTE PF_R

下則日記將會針對 segment type 所討論。

Process Creation, #6:Exec System Call 的觀念

有關 exec system call 的前言,除了以下日記外:

也請閱讀以下日記:

Exec system call 的 service routine 'sys_execve' 主要的工作是「將外部程式(ELF image)取代掉原來的 process」,以 kernel 的角度來說,「原來的 process」就是 current。以下簡單分析 exec system call 重要關鍵,我們由 do_execve 函數看起。

do_execve 負責執行 sys_execve 的主要工作,在 do_execve 的工作流程中,我們不難發現,do_execve 最後會呼叫一個很重要的 API -  search_binary_handler();在 search_binary_handler() API 裡,我們發現,此 API 會呼叫到 kernel space 的 ELF loader 來載入 ELF image 至 process memory。

Kernel 2.6 的 ELF loader 實作位於 load_elf_binary() ,此 API 實作於 fs/binfmt_elf.c 檔案。ELF loader 並非直接在前面所提到的 search_binary_handler() 裡呼叫。

ELF loader 與其它執行檔格式的 loader,是在 kernel 開機時,註冊至 kernel 裡:

static struct linux_binfmt elf_format = {
		.module		= THIS_MODULE,
		.load_binary	= load_elf_binary,
		.load_shlib	= load_elf_library,
		.core_dump	= elf_core_dump,
		.min_coredump	= ELF_EXEC_PAGESIZE
};

static int __init init_elf_binfmt(void)
{
	return register_binfmt(&elf_format);
}

exec system call 被叫用時,最終就會在  search_binary_handler()callback ELF loader 來載入 ELF image。

Kernel space 的 ELF loader

Kernel space 的 ELF loader 是以 segment 的觀念來讀取執行檔(ELF image),相關觀念請見以下日記:

load_elf_binary() 所做的工作有:

1. 讀取 ELF image 的 segment 資訊(由 program header table)。

2. 讀出 segment 內容。

3. 判斷是否有 program interpreter;若有,再讀取 program interpreter。

4. 將讀取到的 segment 內容取代掉 current 的內容,這個動作就是 exec system call 的重要特性。

Program interpreter 就是 dynamic loader/linker,即 /lib/ld-linux.so.2。Program interpreter 的作用是「loading shared libraries(dynamic linking)」。

我們看到 current 被外部(stored)的 ELF image 取代了,這是一個重要的里程碑,背後的意義是:ELF image 被載入到記憶體後,議題將由 ELF image 被帶入更高層次的主題 - Process Memory

當 ELF image 成為 process memory 的形式後,就能開始研究 process memory、VMA 與 program execution(例如:scheduling)有關的議題了。

See also.

March 9, 2007

ELF 之 Program Loading 教學文件, #3: Segment Type 與 Kernel Space Loader

Segment Type

Program loading 時期會處理以下 4 種 segment:

#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_PHDR         6               /* Entry for header table itself */

簡要說明這 4 種 segment 的作用如下。

PT_LOAD:即 text segment 或 data segment。

PT_DYNAMIC:若 ELF 有此 segment,則表示 .dynamic section 會獨立成一個實體的 segment。PT_DYNAMIC 僅包含 .dynamic section。

PT_INTERP:紀錄 program interpreter 路徑與檔名的 segment。PT_INTERP 僅包含 .interp section。

PT_PHDR:Program header table segment。

Segment type 是很重要的資訊,kernel 的 ELF loader 與 program interpreter(ld.so)都是透過 segment type 來判斷 segment 的用途。

Kernelprogram interpreter 對不同的 segment 都有不同的處理方式。

另外,PT_NOTE 並不是一個必要的 segment,實際上它也沒有重要的作用,因此忽略不做討論。

更多有關 Kernel ELF Loader

回到 Jollen's blog「Process Creation」專欄所談論到的「Program 載入流程」裡,相當重要的一個觀念就是 "exec" system call。程式的載入涉及 exec systegm call service,exec system call 是 machine-dependent 的實作,以 x86 來說,此服務實作於 linux/arch/i386/kernel/process.c 檔案;在 SystemV ABI 規格中,program loading 的章節 被撰寫在 'processor-specific' 文件裡。

在目前所討論的 ELF loading 主題中,我們還看不到與 processor 相關的議題,例如 .got/.plt 節區的用途;相關的技術細節在此較不適合做深入討論,未來會以分享一份技術簡報的方式來呈現,並且以 ARM 做為討論標地。

Exec system call 叫用 kernel space 的 ELF loader 載入 ELF image,此觀念請參考 Jollen's Blog「Process Creation, #6:Exec System Call 的觀念」。我們整理過 kernel 的 ELF loader 所做的工作有:

1. 讀取 ELF image 的 segment 資訊(由 program header table)。

2. 讀出 segment 內容。

3. 判斷是否有 program interpreter;若有,再讀取 program interpreter。

4. 將讀取到的 segment 內容取代掉 current 的內容,這個動作就是 exec system call 的重要特性。

項目 1. 是由 ELF image 的 program header table 分析執行檔的 segment 資訊,並判斷 segment type;ELF loader 會依照 segment 的類型來做不同的處理,說明如後(項目 2.)。

項目 3. 則是在找到 PT_INTERP  segment 時,呼叫 interpreter loader 將 interpreter 載入。Program interpreter 我們先前曾提過,就是 ld.so(dynamic loader/linker),這個部份將在下一篇日記再做說明。

PT_INTERP

若 ELF loader 判斷此 segment 為 PT_INTERP,則叫用 interpreter 的 loading 來做載入的工作,在標準的 GNU/Linux 系統底下,由於 program interpreter 也是 ELF 格式,因此叫用 kernel 的 load_elf_interp() API(fs/binfmt_elf.c)將 program interpreter 讀取並載入。

PT_LOAD

若 ELF loader 判斷此 segment 為 PT_LOAD,表示這是 text segment 或 data segment,也就是最主要的程式碼與資料區段,此時便透過 do_mmap() 來將 text/data segment mapping 到記憶體。

依照 ELF 規格,text segment 會由 virtual address 0x0804_8000 的地方開始 mapping,data segment 則是緊接在 text segment 之後。

另外,text segment 與 data segment 都是 PT_LOAD 類型,因此需要根據 Flg 欄位的屬性來知道該 segment 是 text segment 還是 data segment。詳見前一篇日記有關 segment permissions 的說明。

小結

總結本專欄到目前為止所得到的資訊如下。灰色部份是 interpreter 負責的工作,將在下篇日記討論。

請注意,本日記是針對 kernel 本身所做的 ELF loading 工作來做整理,即 kernel-space 的 program loading。另外一半的 program loading 是由 user-space 的 dynamic loader/linker 所完成,這裡所提的 dynamic loader/linker 就是我們不斷提到的 program interpreter。

Segment Type Kernel or Program Interpreter
00 PHDR Interpreter 用來計算 base address 用。
01 INTERP Kernel ELF loader 用來載入 interpreter,並交由 interpreter 做 shared library 的 dynamic linking。
02 LOAD Kernel 將此 segment mapping 為新的 text segment 或 data segment。
03 LOAD Kernel 將此 segment mapping 為新的 text segment 或 data segment。
04 DYNAMIC 由 interpreter 處理

March 13, 2007

ELF 之 Program Loading 教學文件, #4: Program Loader 整體流程

Program loader 的整體流程如下:

1. 使用者在 shell 模式下執行外部程式(stored program)。

2. shell 以 fork+exec system call 的方式執行外部程式。

3. 透過 0x80H 軟體中斷(x86)叫用 kernel 的 exec(sys_execvp)system call service。

Kernel Space Program Loader

4. Exec system call 呼叫 program loader(ELF loader),將 process image(ELF image)載入。

5. Program loader 找到 PT_INTERP segment。

6. Program loader 將 PT_LOAD  segment mapping 為新的 text/data segment,text segment 由 vaddr. 0x0804_8000 開始,data segment 緊接其後。

7. Program loader 呼叫 interpreter loader 將 program interpreter(ld.so)載入,並 mapping 到 process memory;interpreter 的 text segment 由 vaddr. 0x4000_0000 開始,interpreter 的 data segment 緊接其後。

8. Program loader 將 BSS segment 準備好。

9. Program loader 將 process 的 register %eip(user-mode)修改為 program interpreter 的進入點;並將 %sp 設定為 user mode 的 stack。

10. 所以任何 shared library 的外部程式,一開始都是由 program interpreter 開始執行!

User Space Program Loader / Linker

11. Program interpreter 會找到 process 所需的 shared library(名稱及其路徑)。

12. Program interpreter 使用 mmap system call 將 shared library mapping 到 process memory,以完成整個 process image 的建立。

13. 更新 shared library 的符號表。

14. Program interpreter 'jump' 到 process 的進入點(紀錄在 ELF header 裡的 entry point)。

15. 真正開始執行程式!

步驟 1.~6. 已在以下的日記介紹過:

步驟 7.~15. 將會在後續的日記再做整理。以整體的 program loader 流程來說,可分為 kernel-space 與 user-space 二大階段:

  • 步驟 4.~9. 由 kernel 的 ELF loader 所負責。
  • 步驟 11.~14 由 user-space 的 program interpreter(dynamic loader/linker,也就是 /lib/ld-linux.so.2)所負責。

Program interpreter 需要 dynamic segment 裡的相關資訊來完成後續的工作,此部份日後再做整理分享。

來源: www.jollen.org

March 16, 2007

小談 mmap() 與 VMA

最近在 program loader 專欄裡,整理並分享了 kernel 的 ELF loader 主題;接著下來就是 dynamic linking 的議題了。在正式介紹 dynamic linking 前,先來簡單提一下「memory mapping」的觀念;近期在進行 Linux programming 的教育訓練,正好可以跟同學做個小討論。

在 IPC 的課程中提到 mapped memory 的行程間通訊機制,是透過「shared file」來做訊息的傳遞;將 shared file mapping 到 process address space 的 system call 為 'mmap()'。以下是其中一個簡單的程式範例 (mmap_write.c):

#include <stdio.h> 
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define FILE_LENGTH 0x400

int main(int argc, char *argv[])
{
   int fd;
   void *map_memory;

   /* Open a file to be mapped. */
   fd = open("/tmp/shared_file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
   lseek(fd, FILE_LENGTH+1, SEEK_SET);
   write(fd, "", 1);
   lseek(fd, 0, SEEK_SET);

   /* Create map memory. */
   map_memory = mmap(0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
   close(fd);

   /* Write to mapped memory. */
   if (strlen(argv[1]) < FILE_LENGTH)
      sprintf((char *)map_memory, "%s", argv[1]);

   sleep(10);

   exit(0);
}

當程式將檔案 mapping 到自己的記憶體空間(process address space)時,mmap system call 便會建立相對應的 VMA 區段;觀念上來說,只要透過 mmap system call 將檔案 mapping 到記憶體,便會產生對應的 VMA。

修改一下範例程式,讓程式在結束前小睡 10 秒鐘,並做觀察:

# ./mmap_write hello&
[1] 14667
# cat /proc/14667/maps
08048000-08049000 r-xp 00000000 16:42 213757     /tmp/mmap_write
08049000-0804a000 rw-p 00000000 16:42 213757     /tmp/mmap_write
40000000-40015000 r-xp 00000000 16:42 12828674   /lib/ld-2.3.2.so
40015000-40016000 rw-p 00014000 16:42 12828674   /lib/ld-2.3.2.so
40016000-40017000 rw-p 00000000 00:00 0
40017000-40018000 -w-s 00000000 16:42 213753     /tmp/shared_file
42000000-4212e000 r-xp 00000000 16:42 13991938   /lib/tls/libc-2.3.2.so
4212e000-42131000 rw-p 0012e000 16:42 13991938   /lib/tls/libc-2.3.2.so
42131000-42133000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0

除了 mmap system call 外,IPC 技術中的 'shared memory' 同樣也能觀察到對應的 VMA 區段。

延伸閱讀

March 20, 2007

OpenMoko 接受社群的建議加入 WiFi 模組

OpenMoko 專案最近透過他們自己的 ecosystem 向社群請求有關 GPL WiFi 模組的廠商以及驅動程式,昨天 OpenMoko 宣佈了以下這則消息:

主旨: [openmoko-announce] RE: Crossroads 	完全表頭
 
Dear Community,

A big thanks again for all your feedback! We're meeting with vendors
this week and are optimistic about our chances to find a WiFi module.
We'll keep you all posted and announce the winner of the free phone once
we find the right solution.

As for the UI / Application developer work request, we're still
processing these emails. Please bare with us as we are just overwhelmed
with tasks trying to get the devices built this month.

-Sean

從「request for GPL WiFi module」到「meeting with vendors」也不過短短幾天,再度另人見識到開放源碼社群的力量。由於 OpenMoko 是一個完全開放的 Linux 手機平臺,並且 OpenMoko 所形成的 ecosystem 正在快速成熟穩定中,因此可以預見不久的未來,Linux phone 的發展與應用將會更加完整,解決方案(solution)的可獲取性(availability)也會更好。

延伸閱讀

March 21, 2007

哪些是 Free / Open Source 軟體人員的基本條件?

前陣子 OpenMoko 在社群裡徵求 Free / Open Source 的開發人員,讓我們來看一下,對於一個這麼有經驗的 Free / Open Source 團隊而言,他們開出的基本條件是什麼(引用 mailing-list 原文):
2) We don't have enough UI / Application developers -- If anybody
 meets (or knows somebody who can meet) the following qualifications:

        * >= 2 years experience with GTK
        * object oriented design and implementation w/ GObject
        * experience with writing GUI applications from scratch,
        * has software quality assets like:
         o writing maintainable and reusable code
         o refactoring
         o design patterns
         o identifying and extracting common application code
           into frameworks

 Familiarity with collaborative development tools such as:

        * bugtracker,
        * source control management,
        * wiki,
        * mailing lists

就非技術面的條件來看,幾個重要的「協同開發工具」成為了 Free / Open Source 開發者的必要技能,包含撰寫 Wiki 的能力也被列入考量的項目了。由這則徵人啟事的內容來看,幾個值得「傳統軟體開發人員」重視的條件有:

1. Free / Open Source 開發人員需要「refactoring」的技能:在不變動介面的前提下,進行現有程式碼的改善。

2. Free / Open Source 開發人員必須具備「辨認以及取出」程式碼至「framework」的能力:避免重造車輪,不要再去做別人已經做好的工作。

3. 會使用 wiki:非常贊成將編寫 wiki 的能力納入考量。

4. 會使用 mailing lists:這是基本條件,透過 mailing list 與 Free / Open Source 專案保持連絡,並與世界同步。

今天與幾位朋友分享並討論到這個議題,希望也能與大家分享,或是交流想法。

March 23, 2007

小結 Program Loading 觀念

由「Program Loading」專欄所得到的觀念如下。

外部程式(stored program)的執行是透過 fork system call,先將 current process 複製一份成為他的 child process。接著再透過 exec system call 將外部程式的 ELF image 載入,並取代原來的 process。Kernel ELF loader 會讀取 ELF image,並將 text segment 與 data segment 重新 mapping,接著再找到程式的 program interpreter。

外部程式的最初執行是由 program interpreter 開始,program interpreter 透過 dynamic segment 完成 dynamic linking,此時完整的 process image 已建立完成;program interpreter 再跳到程式的進入點,也就是 C run-time library(crt?.o)。

以上二段文字說明了 program loading 的核心觀念,每一句子觀念都能做出一份不算短的分析報告;此階段只整理出 program loading 的主要重點,接著再把「dynamic linking」的主要觀念做研究後,大家應該就可以大致掌握「程式執行」的系統行為了。

另外,補充 program interpreter 的幾個觀念:

  • program interpreter(ld.so)的存在形式有 shared object 與獨立執行檔二種。
  • 當 program interpreter 是以獨立執行檔執行時,表示將 program interpreter 當成 dynamic loader/linker 來使用;通常是經由使用者直接下指令的方式來執行,program interpreter 在發展與測試階段時也使用這種方式。
  • 當 program interpreter 是以 shared object 形式存在時,表示 program interpreter 是經由 kernel 來取得控制權;這種方式是將 program interpreter 當成 dynamic linker 來使用,這是較常見的做法。

以下是將 program interpreter 當做獨立執行檔的使用範例:

$ ldd hello
        libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
$ /lib/ld-linux.so.2 ./hello
Hello, World!

關於 program loading 的分享到此做個小結,接下來將會整理有關 dynamic linking 的日記。

March 24, 2007

看 Linux kernel 應具備的首要觀念是?

Linux kernel 是狀態機還是結構化程式流程?

作業系統(Operating System)的研究,我們以 Linux kernel 的探討來說明一些應有的正確觀念。

對於 Linux kernel 的研究,最經常聽到有人提起「kernel source code」的研讀與分析,並且最常看到的研究方式為「尋找 kernel 進入點,並依照程式流程(flow)做循序研究」,不過,這卻是一種「大部份情況下都錯誤」的研究方式。

由於電腦系統是一種 foreground-background system,並且 Linux kernel 是在此系統上的作業系統,因此整體的 kernel 行為是「control patch」,也就是「控制權的轉移」與「系統狀態的改變」,並不是「程式(函數)流程(flow)」,或是「程式結構(structure)」的問題;最近有朋友問起這方面的議題, 正好也在進行 Linux device driver 的 training,因此特別整理這則日記,來與大家分享,若有任何論述上的失誤,或是有不同的觀點,歡迎在這裡留言 分享。

許多人對於「作業系統」的觀念可能真的有點薄弱;具體來說,對於 kernel 內部原理的研究,其方法應該是:

1. kernel 的開機階段,是以「流程(flow)」的角度來做討論。

2. kernel 完成開機後,會執行 init process,由此正式切換到 F/B system 的觀念,也就是整個系統是一個偌大的狀態機,整個系統內部是一連串複雜的控制權轉移動作。

最近朋友希望我能針對「kernel 的執行流程」做簡單介紹,但是這個問題在命題上應當有修正或是觀念澄清的空間。由於電腦本身是一種 foreground-background system,因此 foreground 的工作會影響 CPU 將控制權移交到 background 的哪一個部份;如果把 user-space 當成 foreground,kernel-space 當成 background,那麼整體系統便能以下圖表示。

fb_system.jpg

Background 受到 foreground 行為的影響,拿以下二個 process 為例,雖然執行結果相同,但因為 foreground 內部的行為不同,因此 control path 也會不同:

/* Process P */

int main(void)
{
   char buf[] = "Hello, World!";

   printf("%s", buf);
   return 0;
}



/* Process Q */

#define STR "Hello, World!"

int main(void)
{
   char *buf;

   buf = malloc(strlen(STR)+1);
   strcpy(buf, STR);
   
   printf("%s", buf);
   return 0;
}

此時,仍是以「流程圖」邏輯來思考的話,當然無法領會箇中奧妙。簡單二個不同的 process,CPU 將控制權移轉到作業系統的路徑(也就是 kernel 執行了哪些程式碼),居然有這麼大的差異。如果再把排程器(scheduler)加入,那麼整個控制路徑很可能「不同時間執行同一程式」也會不同。Tricky!

以研究方法來說,命題時觀念的失誤,終將無法得到正確且完善的結論,因此「希望就 kernel 的執行流程」做討論的命題,應當做修正,並且給定一個更明確的題目; 再者,若無法體認 kernel 是一個大型的狀態機,而仍以「流程與程式結構」的觀念來思考,除了在問題的描述會有相當大的誤差外,可能也會對「如何了解 kernel source code」的方法摸不著頭緒。

由此可知,整體系統在 kernel 開完機並執行 init process 後,就必須針對其「行為」做分析與研究,而不是徘徊在「結構化 C 程式」的圈圈裡;這個觀念證明了「逐行看 code」並不是研究 kernel 原理的正確方法。

Kernel 本身的行為是一連串複雜的狀態改變與控制權轉移,因此是狀態機的觀念。至於,研究方法是採用流程或是控制路徑的觀念進行,就看我們想要讀的是哪一個部份的 source code。前面提到研究 kernel 的二個階段與方式,當中的轉捩點為 init process,接續 init process,建議可先由 program execution(process creation)的觀念開始切入,以了解整個作業系統的行為。

所以,kernel 的研究,是以其動態(run-time)時期行為(behavior)的分析與觀察為主。

來源: www.jollen.org

March 29, 2007

Kernel 2.6.21 將正式加入 VMI(Virtual Machine Interface)

在 LinuxDevices.com 上的一則新聞「Linux kernel gains paravirtualization option」指出,kernel 2.6.21 將加入 VMI(virtual machine interface)的功能,讓 kernel 得以「 modify itself for faster performance when run under a hypervisor」另外可參考這篇較完整的報導「Linux Kernel to Add VMI」。VMI 是由 VMware 公司所貢獻的實作,VMware 是知名度相當高的虛擬機器軟體商,同時也是老牌子的虛擬機器軟體,目前,Intel 與 AMD 已經將虛擬化技術列入重要的產品規劃(roadmap)項目裡了,再加上 VMware 公司的貢獻,可見虛擬化技術真的是 2007 年的重要技術發展趨勢之一。

上面所提到的「Hypervisor」指的是支援虛擬化處理器的虛擬機器管理軟體(Virtual Machine Management),這種技術其實已經存在非常久的時間了,只是以前都只應用在伺服器(RISC 架構)的領域,直到近年 Intel 與 AMD 開始將 hypervisor 技術導入其一般用途的處理器設計中,才引發一波新的風潮。

Hypervisor 技術能讓作業系統在 VMM 上執行,也就是作業系統虛擬化,這類的 VMM 較知名的有 VMware 與 Xen。「Virtualized operating system(VOS)」能解決執行效能與裝置管理面的問題;簡單表達 VMI 與 hypervisor 技術的架構關係如下:

+----------------------------+  \
|    Linux Kernel            |    \
+----------------------------+      => Virtualized OS
|    VMI                     |    /
+----------------------------+  /
|    Hypervisors             |  ====> VMM (eg. Xen)
+----------------------------+
|   虛擬化功能 CPU           |
+----------------------------+

而其中最主要的概念就是「VOS 與 VMM 之間的通訊介面」與「通訊協定」,也就是必須修改傳統的作業系統,讓 OS 能與「各種」VMM 更快、更有效率地溝通,這種技術便稱為「paravirtualization」。Linux kernel 的 paravirtualization 框架為 'paravirt_ops'。

未來,「虛擬化」將成為嵌入式系統的重要技術之一,不但是應用上將取得重要突破,就連傳統的「開發模式」也會有重大的變革。近期在許多嵌入式計算的會議發現,虛擬化的議題己成為核心議題之一;由於現代人類的溝通方式與協調技術進步很快,再加上數以千萬計的聰明大腦,「未來」可能只是一年後。

期待 Embedded Linux 第二個時代的來臨。

March 31, 2007

愚人節的禮物?OrzLab Launch Day

好友 [jserv] 兄稍早捎來一則訊息...。以下引述 jserv 來信的部份內容:

最近跟朋友弄了一個實驗室,想法是能讓更多創意透過自由軟體,落實於嵌入式裝置或者特定的硬體中,這個實驗室名稱為 "Open RazzmatazZ Laboratory ",簡稱 OrzLab,一言以蔽之,訴求就是: "Open RazzmatazZ Laboratory (OrzLab) funds an environment for freedom and creativity based on Free Software and Open Source efforts."

此外,jserv 提到他的願景:

避免重複的專案開發,並針對嵌入式系統或系統整合廠商需求,提出有價值的解決方案,建構一系列自由軟體為基礎的系統架構。也因此,針對特定主題,開發出足夠好且高度創意的專案,並確保以 GPL / BSD License / MIT X License 釋出。

習慣閱讀 jserv's blog 的朋友應該都會知道 jserv 兄有個很神秘的「實驗室」,並且,jserv 本年度的許多重點工作也會以「實驗室」的形態呈現。就在愚人節前幾日,收到 jserv 的訊息,他的神秘實驗室將在愚人節當天正式對外發佈,這真是一個愚人節的好禮物!因此,特別寫了這篇祝賀文,並將 jserv 兄的實驗室介紹給大家認識。

Jserv 的實驗室正式名稱為「OrzLab」,是的,就叫做「Orz」實驗室,「Orz」有個好聽的全名「Open RazzmatazZ Laboratory」。Jserv 曾提及他成立實驗室的想法,我的感覺是,這是熱情與使命感驅使下的產物。OrzLab 是非營利的「創作型」實驗室,並且訴求「自由」與「創意」;為 OrzLab 加入生命的是一群充滿技術情熱與想法的自由軟體開發者。

目前,OrzLab 已上線的專案計畫都是針對嵌入式系統的應用為主,並且每個專案技術都在儘力解決一些關鍵性的技術議題,這樣的工作確實呈現了實驗室的本質,這些專案的原發想法都頗具創意。此外,OrzLab 更是一個技術同好的交誼平臺,任何新奇有趣的專案計畫,都可以加入 OrzLab 成為正式的專案項目之一。

在這樣的想法之下所呈現的是一個相當自由的「創作平臺」,大家都能在此平臺上看到由 OrzLab 所「創作」的各種「Open Source / Free Software」;對於 Open Source / Free Software 充滿熱情的創作者,也能透過 OrzLab 讓大家看到自己的創作成果。

再引述一段 jserv 的來信:

OrzLab 成員來自台灣的自由軟體貢獻者,以 Linux/BSD 為主要的開發環境,對嵌入式系統開發、多國語文處理、Web 應用程式,或者是低階系統控制皆有一定的掌握度。

最後,大家最想知道的一定是「OrzLab」的網站。OrzLab 開發者的 blog 如下:

http://orzlab.blogspot.com/

OrzLab 現階段的開發方向為:

1. Realtime Linux/BSD
2. Embedded i18n
3. Embedded Web/Ajax framework
4. System emulation & Optimization

基於既定的開發方向,OrzLab 希望能取得硬體廠商的支持,提供硬體與技術支援,當然最終成果也會是以自由軟體的形式釋出。OrzLab 的協調人暨聯絡人為 jserv 兄,可透過 email 與他連繫:jserv.tw (at) gmail (dot) com。

Happy hacking and enjoy free / open source software ;-)

關於 March 2007

此頁面包含了在March 2007發表於Jollen's Blog的所有日記,它們從老到新列出。

前一個存檔 February 2007

後一個存檔 April 2007

更多信息可在 主索引 頁和 歸檔 頁看到。

Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org