磨刀不誤砍柴工——實驗工具準備

        寫在最前面的話:

   後面的一系列博客主要圍繞MIT-OS的學習課程來創建;

   爲什麼會學習mit-os?
   1)對於複雜的事物,一種常見的思維就是從簡單到複雜的思維。根據這種思維去學習原始的os,透過它去熟悉os的基本概念與核心構造,從而以此作爲基礎去學習現代os的各項技術,能夠去更新創造與優化它們。
   2)一種典型的西方邏輯思維——原子思想,所有系統都是可以分解爲原子結構,然後有序的組合;根據這種思維,很容易將os分割成若干流程或者若干結構的有序組合;而且在學習過程中,發現這種課程是很好的學習模板,它通過代碼的方式去引導學習去實現相關的流程與結構,這樣更有利於掌握與學習學習內容。
   3)從一個更完整的角度去學習os,因爲os是最貼近硬件,所以需要接觸硬件的相關知識,特別是處理器的軟件參考手冊,從一個狹窄的角度會發現,os是處理器的軟件驅動;對於os,個人認爲一定需要自己嘗試去實現相關部分流程或者模塊,同時去調試os,才能更與os貼近,從而得到更深刻的理解。

   具體課程可以到如下網址參考:http://pdos.csail.mit.edu/6.828/2012/schedule.html

      俗話說得好磨刀不誤砍柴工,有了好刀才能勢如破竹的砍柴;也只有當把工具與環境有更好,更深的認識,才能將系統的知識有效的模擬與調試,才能將書本上的知識帶入到實際工程中來;以工程的態度去對待所學到的知識,將理論與實踐相結合,從而對概念有更深入的理解。

     從某種角度來說,對於代碼或者軟件來說,更多的是經驗的累計與總結;而且它很實在,它可以實現自己的思想,同時也能夠被直觀的看到與仿真,說白了,就是任何代碼或者最好是能夠運行它,然後調試它,從實際運行過程中去體會它的意義,從而能夠更好的理解,然後被直接使用。目前最常用的代碼分析方法,就是看源代碼,這樣靜態的去分析與理解,這種方式很直觀而且有效,但是它只是停留在邏輯層面,忽略了實實在在運行環境,這樣很容易只是理解思想,很難對學習者有更好的啓發與提升;所以我的建議是,任何代碼都請去調試它,或者自己去實現它,在模擬環境中調試之。

        對於操作系統的調試,目前有現成的解決方案就是用虛擬機去運行之,透過虛擬機平臺去調試它;所以對於MIT—OS的實驗課程所需要的工具與環境就是虛擬機。當然,以此作爲平臺,我們也可以去調試linux的內核。還有需要了解的一種工具就是鏈接源代碼與系統的工具——編譯器。所以我們會從如下2個方面去準備我們的工具與實驗環境,1個實驗環節來展示它們的力量。

       前面兩個方面可以參考如下的部分:http://pdos.csail.mit.edu/6.828/2012/tools.html

一)虛擬機——qemu

        爲什麼要選擇它呢?開源,而且功能強大——不僅能完全模擬pc環境,效率高,更重要的是它能與gdb調試工具直接進行交互,進行系統調試。其他更多的瞭解可以到官網http://wiki.qemu.org/Main_Page。當然它有很多的用處,但對於目前我們來說,更多是熟悉它的使用,能夠運行我們的os,然後去調試之。

       1.下載源碼:從官網http://wiki.qemu.org/Download,解壓縮它。

       2.編譯它,爲了防止仿真的系統直接將我們的終端給控制了,所以我建議編譯一個帶簡單窗口的版本,這樣更方便我們調試系統——所以我用的是SDL的圖形界面的——因爲它足夠簡單。

      a)解決依賴關係如下:

     qemu -->zlib

              -->glib-->libffi

             -->sdl

      最新的qemu依賴於zlibglibsdl,同時glib依賴libffi。所以爲了成功編譯qemu,需要提前編譯安裝如上4個包,編譯方法就是linux的源碼安裝3步曲——configuremakemake install

      b)配置編譯參數:

 ./configure --target-list=i386-softmmu,x86_64-softmmu --enable-sdl --with-sdlabi=2.0 --enable-debug –-prefix=$YROOT

      因爲我們主要模擬x86環境中的實現,所以我們只需要支持的虛擬機器構架爲 --target-list=i386-softmmu,x86_64-softmmu,當然如果需要支持其他構架的實現,也可以添加其他的;支持sdl圖形窗口:--enable-sdl --with-sdlabi=2.0 ;將qemu建議安裝到我們的實驗環境——由環境變量決定YROOT

     c)編譯與安裝:

     make && make install

     d)生成使用文檔——html版本:qemu-doc.html

     make html

     因爲生成時,有依賴關係:makeinfo-->textinfo.

     3.常用的使用命令,彙總如下:如需要更詳細的參考可以用qemu-doc.html或者從官網獲取http://wiki.qemu.org/Manual。如下的描述很多從qemu-doc.html摘取,而且以i386平臺的工具使用,同時我們也主要使用它。

     a)基本使用方式:

      qemu-system-i386 [options] [disk_image]

      options爲控制參數。

      disk_image爲仿真使用的磁盤鏡像,就像我們電腦使用的硬盤。

     b)處理器與內存的參數:

     -smp X :處理器核數,比如雙核:-smp 2

     -m Y:內存大小,比如1G:-m 1G

     c)硬盤添加:

     -hdX(a-d) file:添加磁盤鏡像文件,比如:-hdb basic.img

     d)不是用圖形窗口:

      -nographic:默認qemu是會使用圖形窗口,如果不是用則禁用之,它會在調試linux用到,qemu保存log到文件的功能沒有用。

     e)保存debug系統信息到文件:

     -d xxx -D file:調試系統的xxx信息到文件file,比如:查看系統的反彙編——qemu-system-i386 -d in_asm -D test-d

當然可以查看系統的其他信息: qemu-system-i386 -d help

     f)添加串口:

     -serial xxx:給系統添加調試串口,這在系統調試中很重要,比如:添加當前終端爲串口——-serial stdio。當然也可以查詢其他的串口:qemu-system-i386 -serial [TAB][TAB]

     g)停止處理器:-S 

     h)設置gdb調試:-gdb tcp::port;使用tcp端口進行調試。這實際上就模擬了嵌入式linux系統的gdb遠程調試。詳細步驟可以參考《qemu-doc.html3.12 GDB usage》:當然可以參考之前我的博客關於gdb的使用。快捷gdb調試設置:-s(等價於-gdb tcp::1234)。

     i)監視器的使用:

  -monitor xxx;使用qemu監視器,通過它可以發現系統的狀態,同時動態配置系統,極大的方便了系統調試。但是我們沒有用。

     j)直接啓動linux

qemu-system-i386 -kernel arch/i386/boot/bzImage -hda root-2.4.20.img -append "root=/dev/hda"

     -kernel直接加載內核,-append爲添加給內核的參數。

二)交叉編譯工具

      爲什麼需要交叉編譯工具呢?目前我們有最多的參考資料,最成熟的技術,是建立在x8632位系統中的,但是目前我們電腦已經到了64位,所以我們需要在64位系統中編譯出32位運行系統,這樣儘可能方便調試系統。而對於64位系統或者其他處理器——比如:ARM,可以看作是系統的擴展與移植,當我們熟悉x8632位系統中,就可以將某些概念與技術進行推廣,然後通過差異化去理解相關的概念。

       網上有很多交叉編譯工具的編譯方法,都是我們的參考,但是能不能成功,這就需要自己動手實驗了。

       如下所描述的內容都是參考,只有自己動手去嘗試才能保證其正確性,因爲每個人的實驗環境不一樣。

所以如下爲老生常談的內容,同時交叉編譯工具的製作是很麻煩的,而且需要很多配置的信息,大都需要詳細參考gcc官網的相關編譯註釋,從而我們爲了簡化這個流程,同時保證製作的穩定性,將相關的編譯部分製作成腳本,來自動生成相關工具。在我的博客對應的資源中有腳本的實現,其中有較詳細的註釋,保證能夠編譯成功。

   參考文檔:

   官方文檔:https://gcc.gnu.org/install/

   編譯測試結果:https://gcc.gnu.org/ml/gcc-testresults/2015-05/msg00687.html

   另外一個有效的參考文檔爲lfs(Linux From Scratch)相關編譯步驟,其中也包含了交叉編譯工具的製作——只不過它是在模擬root的方式下實現的:

從如下網址,下載最新的參考文檔:http://www.linuxfromscratch.org/lfs/download.html

編譯步驟如下:

    1.下載源碼:

gcc-->glibc   -->glibc-ports,mpfr,mpc,gmp,islkernel頭文件

      -->binutils

     因爲編譯器gcc依賴glibcbinutils,而glibc又依賴於glibc-ports,mpfr,mpc,gmp,islkernel頭文件。

所以需要到各自的官網上下載最新的源代碼即可,然後放到目錄tools-src路徑下。

   2.設置與創建編譯,安裝環境:

   ctools_instl_path=`pwd`/tools----工具安裝路徑

   #編譯根目錄

    compile_top=`pwd`/build------編譯的路徑

    ctarget=i486-yiye-linux------目標主機的構架,格式如下:處理器構架-製造商-運行os

    glibc_instl_path=$ctools_instl_path/$ctarget--glibc安裝路徑

    sysroot_path=$glibc_instl_path----系統的模擬根路徑

    根據如上配置我們編譯出來的交叉工具爲:i486-yiye-linux-gcc,然後根據如上目錄創建對應的目錄。不能使用i386來標記處理器構架,因爲glibc已經認爲i386構架過時了,至少需要i486構架,但是不影響我們描述與使用。

     3.以如下的步驟編譯所有源碼:

     install_kernel_headers-------編譯與安裝內核頭文件

     install_binutils--------編譯與安裝binutils工具

     install_gcc_step1------第一遍編譯與安裝gcc

     install_glibc---------編譯與安裝glibc

     install_gcc_step2-------第二遍編譯與安裝gcc

     如上所述,gcc被編譯了兩次,第一次編譯是爲了編譯glibc,第二次編譯是爲了gcc能夠引用我們編譯出來的gcc;詳細的安裝步驟與解釋請參考我的腳步,主要是很多細節需要去設置,當然有時可能需要去查看相關源代碼,根據官方文檔去猜測其真實用途。唯一一個需要多次調整gcc的使用相關庫的路徑。

     4.腳本的使用:

./auto_compile.sh tools-src

     然後就會生成交叉編譯工具了,如下是我編譯的5.1.0的 i486-yiye-linux-gcc的相關路徑。

tools/

├── qemu

│   ├── glib-2.44.0

│   ├── qemu-2.3.0

│   ├── texinfo-5.2

│   └── zlib-1.2.8

├── sdl

│   └── SDL2-2.0.3

├── sources

│   ├── binutils-2.25.tar.gz

│   ├── gcc-5.1.0.tar.bz2

│   ├── glibc-2.21.tar.xz

│   ├── glibc-ports-2.16.0.tar.xz

│   ├── gmp-6.0.0a.tar.xz

│   ├── isl-0.14.tar.bz2

│   ├── linux-4.0.2.tar.xz

│   ├── mpc-1.0.3.tar.gz

│   └── mpfr-3.1.2.tar.xz

├── test

│   ├── a.out

│   ├── binutils-2.25

│   └── main.c

└── tools

    ├── bin

    ├── i486-yiye-linux

    ├── include

    ├── lib

    ├── lib64

    ├── libexec

    └── share

     編譯出來的版本測試如下:

[yiye@yiye pc_prj]$ i486-yiye-linux-gcc -v

Using built-in specs.

COLLECT_GCC=i486-yiye-linux-gcc

COLLECT_LTO_WRAPPER=/home/yiye/study/pc_prj/tools/tools/libexec/gcc/i486-yiye-linux/5.1.0/lto-wrapper

Target: i486-yiye-linux

Configured with: ../gcc-5.1.0/configure --prefix=/home/yiye/study/pc_prj/tools/tools --target=i486-yiye-linux --with-sysroot=/home/yiye/study/pc_prj/tools/tools/i486-yiye-linux --with-build-sysroot=/home/yiye/study/pc_prj/tools/tools/i486-yiye-linux --with-local-prefix=/home/yiye/study/pc_prj/tools/tools/i486-yiye-linux --with-native-system-header-dir=/include --disable-nls --enable-shared --disable-static --enable-languages=c,c++ --enable-__cxa_atexit --enable-c99 --enable-long-long --enable-threads=posix --disable-multilib --with-system-zlib --enable-checking=release --enable-libstdcxx-time

Thread model: posix

gcc version 5.1.0 (GCC) 


三)單步調試linux內核

      爲什麼要調試linux內核?一個很直接的原因就是梳理內核的啓動流程與調試內核的關鍵流程,能夠對內核進行動態分析,不僅僅停留在靜態分析上,更深入去理解內核。推薦一個查看內核源代碼的工具——LXRhttp://lxr.oss.org.cn/ident

      我們以如下步驟進行調試:

      1.下載內核:最新內核www.kernel.org

      2.配置內核:

make CROSS_COMPILE=i486-yiye-linux- ARCH=i386 menuconfig

      3.編譯內核:

make -j8 && make install

     4.製作根文件系統鏡像,因爲內核啓動過程中,需要一個根文件系統,同時也是爲了製作基於linux的最小系統進行調試。製作根文件系統可以參考《Embedded Linux Primer: A Practical, Real-World ApproachChapter 6. System Initialization。詳情見博客資源。因爲製作根文件系統是一個比較麻煩的過程,所以我也創建了一個腳本自動生成硬盤鏡像;詳情見附件:create-simple-root.sh.一定要以root方式來運行。

     在linux環境下製作根文件鏡像,需要如下工具:

      a)創建鏡像文件

dd if=/dev/zero of=disk.img bs=1m count=512

      b)將鏡像與設備聯繫起來

loseup -f disk.img

loseup /dev/loop0 disk.img

      c)對設備文件進行分區

fdisk /dev/loop0

      d)讀取鏡像文件的分區信息

kpartx -a /dev/loop0

      f)對分區格式化

mkfs /dev/mapper/loop0p1

      g)安裝grub

grub-install --target=XXX --boot-directoty=YYYY /dev/loop0

     h)掛載設備loop

mount -t extX /dev/mapper/loopXpY YYY

     i)卸載

umount YYY

kpartx -d /dev/loop0

loseup -d /dev/loop0

     製作根文件系統的相關內容:

     根文件系統需要一個簡單的工具集,所以busybox能夠幫上忙了。所以需要下載最新的busyboxhttp://www.busybox.net/編譯方法和內核一樣。

     然後以如下步驟創建最小的根文件系統:

     a)創建基本文件系統目錄

mkdir -pv tmp/{dev,proc,sys,run}

     b)創建默認節點

mknod -m 600 tmp/dev/console c 5 1

mknod -m 666 tmp/dev/null c 1 3

      c)創建初始化文件

mkdir -pv tmp/etc/init.d

cat > tmp/etc/init.d/rcS << EOF

#this is simple start script!!!

echo "mount add part " > /dev/console

mount -t proc /proc

mount -t sysfs /sys

mount -t tmpfs /run

umask 002

echo "this is first exe script" > /dev/console

export PATH=/bin:/sbin/:/usr/bin:/usr/sbin

EOF

chmod a+x tmp/etc/init.d/rcS

 

cat > tmp/etc/fstab << EOF

#dev  mount-point  type     options           dump fsck-order

/dev/sda1 /        ext3     defaults            1 1

proc   /proc    proc     nosuid,noexec,nodev 0 0

sysfs     /sys     sysfs    nosuid,noexec,nodev 0 0

tmpfs     /run     tmpfs    defaults            1 1

devpts    /dev/pts devpts   gid=5,mode=620      0 0

devtmpfs  /dev     devtmpfs mode=0755,nosuid    0 0

EOF

 

     d)拷貝busybox依賴的庫

      因爲busybox依賴於ld-linux.so/libc.so/libm.so等庫這些庫在編譯glibc時編譯出來了;拷貝它們到lib中。

     使用自動創建腳本:

./create-simple-root.sh busybox-1.23.2/yiyeout/ i486-yiye-linux-

     5.使用qemu進行調試linux內核

     a)第一步使用qemu加載內核

qemu-system-i386 -S -s -kernel linux-4.0.2/arch/x86/boot/bzImage -hdb basic.img -append "root=/dev/sda1 console=ttyS0" -nographic

     -S -s啓動gdb同時將程序掛起。

     -hdb basic.img將基本根分區,加載爲hdb

     -kernel linux-4.0.2/arch/x86/boot/bzImage -append "root=/dev/sda1 console=ttyS0"加載內核與內核參數。

     -nographic 不是用圖形界面,保證得到完整的打印信息。

     b)啓動新終端,使用gdb調試內核:

gdb linux-4.0.2/vmlinux 

     設置斷點:

b start_kernel

      執行之後:

(gdb) b start_kernel

Breakpoint 1 at 0xc19ca77c: file init/main.c, line 490.

      鏈接到qemu的模擬遠程環境:

target remote localhost:1234

      執行之後

(gdb) target remote localhost:1234

Remote debugging using localhost:1234

      繼續運行調試內核:

c

      之後程序會被停在設置的斷點,linux-c代碼開始的地方。

(gdb) c

Continuing.

 

Breakpoint 1, start_kernel () at init/main.c:490

     後續就是使用gdb的相關命令進行內核單步調試或者程序跟蹤了。


     一葉說:根據如上的步驟已經創建好了一個基本的實驗環境,而且能夠調試內核了。這樣來說,對於系統的理解,就可以很直觀的運行與追蹤了。在如上的過程中,由兩個腳本起到了很重要的作用,其實,這是一種有效的學習與總結方式,將好的思想與方法用代碼的方式總結,這樣不僅能加深思考,同時也方便使用,提高效率。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章