歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

linux內核啟動過程追蹤

一、使用自己的Linux系統環境搭建MenuOS的過程

  # 下載內核源代碼編譯內核

  cd ~/LinuxKernel/

  wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz

  xz -d linux-3.18.6.tar.xz

  tar -xvf linux-3.18.6.tar

  cd linux-3.18.6

  make i386_defconfig

  make # 一般要編譯很長時間,少則20分鐘多則數小時

  # 制作根文件系統

  cd ~/LinuxKernel/

  mkdir rootfs

  git clone https://github.com/mengning/menu.git # 如果被牆,可以使用附件menu.zip

  cd menu

  gcc -o init linktable.c menu.c test.c -m32 -static –lpthread

  cd ../rootfs

  cp ../menu/init ./

  find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

  # 啟動MenuOS系統

  cd ~/LinuxKernel/

  qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

    // -initrd file Use file as initial ram disk.

二、重新配置編譯Linux使之攜帶調試信息

在原來配置的基礎上,make menuconfig選中如下選項重新配置Linux,使之攜帶調試信息

  kernel hacking—>[*] compile the kernel with debug info

  make重新編譯(時間較長)



三、使用gdb跟蹤調試內核

  1、qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 關於-s和-S選項的說明:

  // # -S freeze CPU at startup (use ’c’ to start execution)

  //# -s shorthand for -gdb tcp::1234 若不想使用1234端口,則可以使用-gdb tcp:xxxx來取代-s選項

 2、另開一個shell窗口

  gdb

  (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加載符號表

  (gdb)target remote:1234 # 建立gdb和gdbserver之間的連接,按c 讓qemu上的Linux繼續運行

  (gdb)break start_kernel # 斷點的設置可以在target remote之前,也可以在之後

四、過程圖示:

  1、可以看到QEMU 已經運行而且被“凍結”。(有幾個文件沒有,不影響後續操作)

   \

  2、另外打開一個窗口,進入linuxkernel目錄,輸入gdb 回車

     \

  3、輸入:(gdb)file linux-3.18.6/vmlinux

     (gdb)target remote:1234

       (gdb)break start_kernel

   \

  4、輸入c 回車,可以看到內核繼續啟動,最後停在了start_kernel處:

\

更多gdb指令:

顯示和查找程序源代碼

(1)list :顯示10行代碼,但是我為什麼沒有顯示成功呢?

(2)list 5,10:顯示源文件第五行到第十行的代碼

(3)list t4.c:5,10:顯示源文件中第五行到第十行的代碼,在跳是含有多個源文件的次序時使用;

(4)list get_sum:顯示get_sum函數周圍的代碼//什麼叫周圍的代碼呢?

(5)list t4.c :get_sum:顯示源文件t4.c中第五行到第十行的代碼,在跳是含有多個源文件的次序時使用;
(6)如果在調試中運行linux命令,則可以在gdb的提示符下輸入shell命令. (gdb)shell ls
(7)search forward用來從當前行向前查找第一個匹配的字符串;
search get_sum forward get_sum
(8)reverse_search 用來從當前行想前查找第一個匹配的字符串: Example: reverse_search main

設置和管理斷點:
(1)以行號設置斷點:(gdb)break 7
(2)以函數名設置斷點:(gdb)break get_sum
(3)以條件表達式設置斷點:方法一:break 行號或者函數名 if 條件. Example: (gdb)break 7 if i==99
方法二:watch 條件表達式,下面是具體的舉例:
方法三:awatch;用來給表達式設置斷點,在表達式的值發生改變或者表達式的值杯讀取的時候,程序暫時停止;
(4).查看當前設置的斷點:info breakpoints

(5)使用“disable 斷點編號”命令可以是某個斷點失效,程序運行到該段點時不會停下來而是繼續運行。

(6)使用“enable 斷點編號”命令可以是某個斷點恢復有效。

徹底的刪除某個斷點,可以使用clear或者delete命令。
(1)clear:刪除程序中所有的斷點;
(2)clear 行號:刪除此行中的斷點
(3)clear 函數名:刪除該函數的斷點
(4)delete 斷點編號:刪除指定編號的斷點。如果一次要刪除多個斷點,各個斷點編號以空格隔開。

控制程序的執行:
(1)continue命令:讓程序繼續運行,直到下一個斷點或者運行完為止。格式:continue
(2)kill命令:用於結束當前程序的調試

(3)next和step命令
   區別:如果遇到函數,next會把函數調用當作一條語句來執行,再次輸入next會執行函數調用後的語句;

   而step則會跟蹤進入函數,一次一條的執行函數內的代碼,直到函數內的代碼執行完,在進行函數調用後的語句;
(4)nexti和stepi命令:用來單步執行一條機器指令,注意不是單步執行一條魚據。單步執行一條語句使用next和step命令。通常一條語句有多條機器指令構成的。
   注意的是:gdb的一些命令可以簡寫,比如list可以用li來代替,continue命令可以用cont來代替。

  

  5、繼續輸入 list start_kernel 查看start_kernel函數為中心上下10行的代碼

    然後接著輸入list 查看後續的代碼(每輸入一次list,向後顯示10行)

   \

我們繼續輸入list跟蹤start_kernel以後的代碼,看看後面的執行過程:

\

\

\

\

可以看到,在start_kernel之後是一大堆的初始化操作,初始化外設、時鐘、寄存器等。後面還有很多操作,這裡不作過多的截圖展示,

  6、後面進入rest_init函數:

\

四、總結:

  在start_kernel中調用了一系列的初始化函數,已完成內核本身的設置:設置與體系結構相關的環境、進程調度器初始化、控制台初始化、系統IRQ初始化、內存初始化等。在Start_kernel函數的最後調用了rest_init()函數,在rest_init中建立了init線程,並在最後調用cpu_idle()函數。

  可以這樣理解:start_kernel最後clone出一個新的進程,也就是init進程,然後原來的進程就去執行cpu_idle()函數了,也就變成了idle進程,當發生一次進程調度後,init進程被調度運行。

  核心進程init()主要進行一些外設初始化的工作包括SMP(Symmetric Multi-Processing 對稱多處理)的初始化,以及調用do_basic_setup()完成外設及其驅動程序的加載和初始化,當do_basic_setup()函數返回init() ,init() 又打開了、dev/console設備,重定向輸出文件到控制台,最後通過kernel——execve加載執行init程序。

  追蹤到init後可以修改文件系統,比如把顯示的MENUOS修改為MYOS,修改quit指令,讓其退出文件系統等:

\

\

\
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img


    // -initrd file Use file as initial ram disk.

  這裡將-initrd 後面的參數rootfs.img加載到內存中運行,所以,這裡的rootfs.img 可以隨便改名字,因為內核代碼中不會直接用這個名字而是通過initrd來傳遞的。再者rootfs.img是由tootfs文件夾打包得來的,而rootfs文件夾中只有一個init可執行文件,而init可執行文件來自於menu文件夾,menu文件夾中執行make指令會發現生成了一個test可執行文件,運行menu文件夾中的test和init後會發現效果是一模一樣的,所以顯然init是由test通過(cp test init) 拷貝、重命名得來的,所以最終修改文件系統可以歸結為修改menu文件夾中的main.c、test.c文件。

  在init/initramfs.c文件中:

\


Copyright © Linux教程網 All Rights Reserved