2011年10月4日 星期二

Xen-ARM簡介

(一)簡介 

Xen-ARM是一種在ARM架構上實作的Hypervisor,能在一台機器上同時運行多個作業系統。在Hypervisor的分類當中,屬於Type1的Hypervisor[1] ,而在Hypervisor上運行的Guest OS是需要被Patch過的,在系統虛擬化技術分類上屬於Para-virtualizaiton(半虛擬化)。

Xen-ARM是基於Xen [2] (一款在x86平台上開發的Hypervisor)。不同於Xen是由XenSource公司所主導,Xen-ARM的開發者是以Samsung為主體,而近幾年來有關Xen-ARM的Paper多由Samsung所發表。

談到Xen-ARM與Xen社群的關係,自兩者官網的關係來看,Xen-ARM的官網是被當成Xen官網上的一個分頁,而在XenWiki當中,也有一頁是寫有關Xen-ARM的內容,顯見Xen社群是知道有Xen-ARM的存在,並且是近乎把Xen-ARM當做Xen一部分的方式放置在Xen的官網。然而,Xen-ARM迄今仍未被整合進Xen的主線當中,依據Xen-ARM官網上的說法,他們希望能於2012年進入Xen的主線。

在早期Xen-ARM開發時,只支援FreeScale的開發板,而至今Xen-ARM已經支援Xscale PXA310, Versatile, Realview-PB, Cortex-A9等SoC平台的硬體架構,也支援Goldfish(QEMU emulator patched for Android)。在Guest OS的部份,早期僅支援2.6.11的版本,時至今日,Xen-ARM官方目前已支援Linux v2.6.11, v2.6.18, v2.6.21, v2.6.24, v2.6.27的版本。

(二)Xen-ARM歷史發展

於2007年的Xen秋季高峰會上,三星提出他們成功把Xen移植到ARM處理器架構之下,三星把此計畫稱之為Xen-ARM。在2007年當時,三星團隊在ARM9(ARMv5)的FreeScale開發板上實作Xen-ARM,當時他們是以Xen-3.0.2為基礎移植至FreeScale上,在Hypervisor上運行的Guest OS則是Linux 2.6.11。

然而,雖然2007年當時三星就有到Xen Summit Autumn發表Xen-ARM當時開發的成果,但真正開放原始碼的時間則是到2008上半年才有開放。於08年所釋出的第一版Xen-ARM,僅支援FreeScale的硬體平台,其所支援的Guest OS也僅為miniOS。

也因為沒有釋出Patch過能在Guest mode運行的Linux實有不便,因此於同年(2008年)的下半年,三星又釋出了第二版的Xen-ARM,在這個版本當中,即有提供Para-virtualized Linux 2.6.24來做為Guest OS,同時也釋出了Xen tool。

於2009年下半年,Xen-ARM推出第三版,當中最主要的更新是支援ARM11MPCore(ARMv6)的平台。最新的版本則是2010年推出的版本,此版本已經支援Cortex-A9(ARMv7)的硬體平台,Guest OS也已經有支援Linux v2.6.11, v2.6.18, v2.6.21, v2.6.24, v2.6.27的版本。

(三)Xen-ARM的架構[3]


Xen-ARM的架構與Xen相仿,是在硬體上直接架一層Hypervisor來實際管控所有硬體資源,然後Guest OS再架上Guest OS。當Guest OS在執行Non-sensitive instructions時,由於此種指令不會更動到關鍵的硬體資源,故即讓其native-run。然而當執行到sensitive instruction時,由於此種指令會更動到系統的關鍵資源,而我們不希望Guest OS有權直接存取硬體資源,故此時我們會讓sensitive instruction強制trap進Hypervisor,交由Hypervisor來處理,而此種行為我們會稱為Guest OS發一個hyper-call進hypervisor,如同在OS中我們會稱user-space程式會發system-call進kernel一樣。


1. CPU virtualization




在Xen-ARM當中將CPU的指令分為三種:Xen-ARM mode、virtual kernel mode與virtual user mode,分別給hypervisor、kernel-space of guest OS、user-space of guest OS使用,而對應實際ARM的instruction mode分別為supervisor mode、user mode與user mode。我們可以看到virtual kernel mode和virtual user mode皆是使用user mode,即non-privilege mode,故實際上virtual kernel mode和virtual user mode的差別只是邏輯上的分別而已。


2. Memory virtualization



我們知道在Memory virtualization當中,我們需要於Hypervisor中建立一套shadow page table,讓Guest OS能夠管理自己的Memory,但又不會直接接觸到實際的記憶體配置,而是利用shadow page table來轉換到實體的記憶體配置。在Xen-ARM當中也利用一樣的技術,在Xen-ARM當中去將實體記憶體切割成數塊,分別定址給各個VM,還有一部分的記憶體會被分配給Hypervisor本身


3. I/O virtualization


在Xen-ARM的架構上,他會把Guest OS切成兩種,一種是負責其他VM和Hypervisor溝通的VM,我們稱其為Dom0或VM0,另一種VM則是無法直接對Hypervisor溝通的VM,我們稱呼其為DomU(在一個Hypervisor上只有兩個VM運行時,我們也多直接成其為Dom1或VM1)。當VM1發出一個I/O request時,他會透過他自己的I/O裝置驅動程式打入Xen-ARM之中,但Xen-ARM只幫忙傳遞訊息給VM0,由VM0當中的Back-end driver去負責模擬虛擬的I/O裝置給VM1使用,而真正接觸到實際硬體的則是交由VM0當中的Native driver去負責。

2011年9月30日 星期五

The Process Address Space

  • The process address space
      • abstract:
        • 系統除了管理physical memory之外, 它也會管理user-space process的記憶體, 這部份又被叫作process address space. Linux是一個virtual memory OS, process看見的記憶體都是虛擬過的, 每個process都以為擁有全部的記憶體, 而且單一process所看見的記憶體可能比physical memory還要大.
        • 要用什麼來描述一個process address space? 而且總不可能讓process access所有address吧, 那可用的memory又要用什麼來描述? 怎麼對映到physical address? 後面討論的是這些.

    一. Address space

      • Address space
        • Process address space是process可以讀取或使用的記憶體. 不同的process中, 彼此的memory address是不相關的. 也有所謂的thread, 可以共享彼此的address space.
        • 雖然32位元process可以最多指到4GB的位址, 但process沒有權限存取所有的記憶體. 一個process所能合法存取的記憶體區塊, 又稱作memory area. 透過kernel, process可以在它的address space裡動態增減memory area.
        • Memory area包含:
          • text section, a memory map of the executable file’s code.
          • data section, a memory map of the initialized global variables.
          • bss section, a memory map of the zero page containing uninitialized global variables.
          • Others.
      • Memory descriptor
        • 已經知道了process address space, 那kernel用什麼來保存它呢? 用memory descriptor: mm_struct. 這個資料結構中保存了所有process address space的資訊, 位於linux/mm_types.h底下, 其中幾個element:
          • mm_user, 指的是使用這個address space的process個數.
          • mm_count, primary reference count for the mm_struct. ex. 若有9個thread, 則mm_user=9, mm_count=1. 只有當mm_user=0, mm_count才會變0.
          • mmap 和 mm_rb 都是用來存memory area的資訊. 不同的是mmap採用linked list, mm_rb採用紅黑樹.
      • Allocating a memory descriptor
        • memory descriptor存放在task中process descriptor裡的mm欄. 因此, current->mm 就可以指到現在process的memory descriptor. 函式copy_mm用來複製parent的memory descriptor給child. 透過kernel/fork.c裡的allocate_mm()可以從mm_cache中得到一個mm_struct.
      • Destroy a memory descriptor
        • 當一個process離開, 會呼叫kernel/exit.c裡的mm_exit(), 當中會呼叫mm_put(), 用來減mm_user. 當mm_user變0, mm_drop()就會被呼叫來減mm_count. 當mm_count變0, 就會呼叫free_mm(), 透過kmem_cache_free()將mm_struct丟回mm_cache.
      • mm_struct and kernel thread
        • Kernel thread沒有process address space, 也就沒有memory descriptor, 因此kernel thread的 process descriptor 的mm欄位是NULL. Kernel thread的定義亦是: 沒有user context的process.
        • Kernel thread 沒有 address space 會有影響嗎? OK的, 因為它不會存取user-space memory. 因為 kernel thread 沒有 user-space pages, 也就不用memory descriptor 和 page tables. 但kernel thread 仍然需要page table等資料, 為了節省記憶體, 以及節省switch的時間, kernel threads 會直接使用前一個process的memory descriptor.
        • 當一個process被schedule到, 它會先load mm所指的address space, 再將active_mm指到新的address space上. 而當kernel thread被schedule到, 它看見mm是NULL則會保留原本load的address space, 然後將active_mm指到前一個process的memory descriptor. 這樣kernel thread 就能使用需要的page tables. Kernel thread只會從process address space中拿出屬於kernel memory的資訊, 並不會存取user-space memory. 而且所有process幫忙存的這個資訊是一樣的.

    二. memory area

      • Virtual memory area
        • Kernel如何描述address space中的memroy region呢? 用linux/mm_type.h中的vm_area_struct, 一般也稱作virtual memory area (VMA). 系統把每個memory area都當成一個物件. 每一個物件都有相對應的屬性與操作函式.
      • VMA flags
        • VM_READ, VM_WRITE, VM_EXEC 這三個是常見的flag, 用來指定memory area中的pages是可以read, 可以write, 或可以執行的.
      • Memory area in real life
        • 使用cat proc//maps, 可以看見process address space中的memory area. pmap指令也有一樣的功能.

    三. Page tables.

      • Page tables
        • 雖然程式都在virtual memory中執行, 但processor卻會在physical memory上直接操作, 因此當一個virtual memory address被使用, 它會先被轉成physical memory. 而這個轉換機制會透過page table來完成. Page table將virtual address分成幾個片段, 每個片段透過index指向一個table, 而table會指到另一個table或是physical page.
        • Linux中, page table分為三層. 最上面那層又叫PGD (page global directory), 由一個型態是pgd_t的array構成. 第二層是PMD (page middle directory), 由pmd_t的array構成. 第三層被簡稱page table, 由型態為pte_t的page table entry構成.
        • Page table跟架構有關, 被定義在asm/page.h
      • TLB
        • 幾乎每次操作virtual address都要透過page table來轉換, 因此效能是一個關鍵. 大部份的processor都使用TLB (translation lookaside buffer) 來加速這個過程, 它的作法即是將virtual-to-physical mapping 暫存起來. 當一個virtual address被存取, processor會先到TLB裡看有沒有hit, 若有則立即回傳physical address, 若沒有再透過page table來查physical address.

2011年9月29日 星期四

使用debug工具-CGDB

CGDB入門

GDB是一套好用的debug工具,而CGDB則是基於GDB的功能,在介面上分成了指令區和程式碼顯示區兩塊,所以CGDB和GDB的用法是一樣的,更貼切地說,CGDB只是加了程式碼顯示區域的GDB。

這份文件會以”對在qemu上跑的Linux kernel做debug的動作”為範例做說明。

1. 安裝,使用CGDB

安裝使用apt-get即可:

#apt-get install CGDB

因為我們所要debug的對象是屬於arm架構下,所以必須使用toolchain所提供的CGDB:

#cgdb –d arm-none-linux-gnueabi-gdb

2. QEMU開啟GDB mode

在QEMU開機的script中,要加上-S -s的參數,這個參數會讓QEMU的控制交給GDB,下了這個參數會讓QEMU開機過程卡住,等到GDB有執行的指令才會開始動。

3. GDB連線到QEMU

要debug就必須要先將GDB所在的terminal連線到QEMU所在的terminal,GDB預設的port是1234,在GDB裡面打入:

#target remote :1234

4. GDB載入symbol file

GDB是依據所載入的symbol file來顯示debug的相關資訊,包含程式執行到哪一行、所有register所儲存的東西、變數內容等。在我們編kernel的時候,在kernel source code所在的跟目錄下會伴隨產生vmlinux這個檔案,file是用來載入symbol file的指令,後面接著symbol file所在的路徑和檔案名稱,例如:

#file linux/vmlinux

5. 使用GDB script

先編輯一個簡單的script叫做gdbscript內容為:

target remote :1234

file linux/vmlinux

cgdb沒有提供讀script檔案的參數,但是GDB有,在CGDB指令的最後放上雙減號"--",這代表CGDB指令的結束,所以接在雙減號之後就是給GDB看的參數,我們在加上-x參數導入script:

#cgdb –d arm-none-linux-gnueabi-gdb -- -x gdbscript

這樣打開CGDB之後就會自動把port接起來並且載入symbol file了。

6. GDB指令

(1) continue

讓程式繼續跑下去,直到停在breakpoint。

(2) Breakpoint

程式執行到這裡就會停下來,並且會顯示目前執行到code的哪裡

a. Add breakpoint

Breakpoint可以設定在function、程式的某一行、記憶體位址

#breakpoint schedule

#breakpoint kernel/sched.c:5430

b. Enable breakpoint

設定的breakpoint不一定就一定要生效,預設是enable的狀態,如果要把一個breakpoint enable,指令打:

#enable

是breakpoint的流水號

c. Disable breakpoint

#disable

d. Clear breakpoint

清除breakpoint,後面接著行號或是function名稱

#clear test.c:1234

#clear schedule

e. Delete breakpoint

刪除breakpoint,後面接著breakpoint流水號

#delete

f. Conditional breakpoint

可以加上條件來決定某個中斷點在執行的時候要不要停下來

#condition (var > 3)

(3) next

執行下一行程式碼,但是遇到function不會進去function裡面。

(4) step

執行下一行程式碼,遇到function會進去逐行跑,所以這個指令是會把整任何程式碼徹底地跑過。

(5) list

顯示目前停下來的點是被誰呼叫的。

(6) print

印出變數內的值,可以加入/x以十六進位顯示,或是/d以十進位顯示。

#print /x var

(7) info

顯示很多有用的資訊,包含register、memory位置都可以。

#info r

(8) backtrace

可以看出使用GDB時在那些地方停下來過。

(9) disassemble

呈現出目前的組合語言程式碼

7. 指令縮寫:

指令

縮寫

backtrace

bt

breakpoint

b

condtion

cond

continue

c

diable

dis

disassemble

disas

enable

en

Info

i

list

l

next

n

print

p

step

s

GDB的縮寫原則是,只要指令的前幾個字元可以辨別出是哪個指令,就可以當縮寫,舉例:list的縮寫可以是l, li, lis, list。

8. CGDB上下區域的切換

在CGDB按下Esc會focus到上面的顯示code區域,這個時可以按下+或-來調整顯示code區域的大小,也可以Page Up和Page Down來移動,按下i會回到下面才可以打指令,