实验三 Linux的启动与关闭,实验三:跟踪分析Linux内核的启动过程
Ubuntu?16.04下搭建MenuOS的過程:
1、下載內核源代碼編譯內核
1 # 下載內核源代碼編譯內核
2 cd ~/LinuxKernel/
3 wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
4 xz -d linux-3.18.6.tar.xz
5 tar -xvf linux-3.18.6.tar
6 cd linux-3.18.6
7 make i386_defconfig
8 make # 一般要編譯很長時間,少則20分鐘多則數小時
9
10 # 制作根文件系統
11 cd ~/LinuxKernel/
12 mkdir rootfs
13 git clone https://github.com/mengning/menu.git # 如果被墻,可以使用附件menu.zip
14 cd menu
15 gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
16 cd ../rootfs
17 cp ../menu/init ./
18 find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
19
20 # 啟動MenuOS系統
21 cd ~/LinuxKernel/
22 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
2、重新配置編譯Linux使之攜帶調試信息
1 $ sudo apt-get install ncurses-dev
2
3 首先需要安裝圖形化調試工具ncurses,ncurses-dev是一個庫,利用它就可以顯示圖形化的控制頁面了。
4
5 $ make menuconfig
6
7 選擇kernelhacking—>
8
9 選擇Comile-time checks and comp[iler options —>
10
11 按Y選中Compile the kernel with debug info
12
13 保存退出
14
15 重新make
3、使用gdb跟蹤調試內核
1 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 關于-s和-S選項的說明:
2 # -S freeze CPU at startup (use ’c’ to start execution)
3 # -s shorthand for -gdb tcp::1234 若不想使用1234端口,則可以使用-gdb tcp:xxxx來取代-s選項
4
5 另開一個shell窗口
6 gdb
7 (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加載符號表
8 (gdb)target remote:1234 # 建立gdb和gdbserver之間的連接,按c 讓qemu上的Linux繼續運行
9 (gdb)break start_kernel # 斷點的設置可以在target remote之前,也可以在之后
此時可以繼續list接著向下查看,也可以step單步運行,或者利用匯編碼ni(next?instruction)逐指令地運行調試。
一些常用的gdb的命令:
(gdb)?break?n?:在第n行處設置斷點
(gdb)?r:運行程序
(gdb)?n:單步執行
(gdb)?c:繼續運行
(gdb)?p?變量:打印變量的值
(gdb)?bt:查看函數堆棧
(gdb)?set?args?參數:指定運行時的參數
(gdb)?show?args:查看設置好的參數
(gdb)delete?斷點號n:刪除第n個斷點
(gdb)step:單步調試,若有函數調用,則進入函數(與命令n不同,n是不進入調用的函數的)
基礎的工作就是這些,下面我嘗試去分析一下內核從start_kernel到init進程啟動的大體過程
使用list命令看到的start_kernel源碼:
lockdep_init() : Linux有一個死鎖檢測模塊lockdep,看注釋說的是初始化hash表,應該是內核會依賴這個表來做其他的事。
set_task_stack_end_magic(&init_task) : init_task是一個全局變量,即是手工創建的PCB。 它是在具體的代碼是
struct?task_struct?init_task?=?INIT_TASK(init_task); ?那么task_struct應該就是一個類似于PCB結構的結構體,而INIT_TASK便是初始化該結構體的函數。
后面是一些模塊的初始化,水平有限就不一一解釋了,就說說老師提到的函數吧。
trap_init() : 初始化中斷向量,設置一些中斷門。
mm_init() : 內存管理模塊的初始化。
sched_init() : 調度模塊的初始化。
rest_init() : 源碼如下:
static noinline void __init_refok rest_init(void)
394 {
395 int pid;
396
397 rcu_scheduler_starting();
398 /*
399 * We need to spawn init first so that it obtains pid 1, however
400 * the init task will end up wanting to create kthreads, which, if
401 * we schedule it before we create kthreadd, will OOPS.
402 */
403 kernel_thread(kernel_init, NULL, CLONE_FS);
404 numa_default_policy();
405 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
406 rcu_read_lock();
407 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
408 rcu_read_unlock();
409 complete(&kthreadd_done);
410
411 /*
412 * The boot idle thread must execute schedule()
413 * at least once to get things moving:
414 */
415 init_idle_bootup_task(current);
416 schedule_preempt_disabled();
417 /* Call into cpu_idle with preempt disabled */
418 cpu_startup_entry(CPUHP_ONLINE);
419 }從rest_init開始,Linux開始產生進程,因為init_task是靜態制造出來的,pid=0,它試圖將從最早的匯編代碼一直到start_kernel的執行都納入到init_task進程上下文中。
第403行的kernel_init函數中有一個run_init_process函數,它的作用便是建立init_process進程,也就是Linux系統中的1號進程。它是第一個用戶態進程。
而從最后的cpu_startup_entry中可以看到init_task以后便成為了一個idle進程,也就是0號進程,當系統沒有進程運行時,一直在運行這個空閑進程。
總結:
通過本次試驗,我對Liunx內核啟動的過程有了大體的了解。
內核啟動時,會先進行一部分硬件的初始化,這部分是匯編語言完成的,而后會運行start_kernel,也就是內核的C語言部分。start_kernel會繼續進行一些內核的初始化,建立0號進程
,最后通過rest_init()建立1號進程,也就是第一個用戶態進程,而以后0號進程便成為一個空閑(idle)進程,當沒有其他進程運行時,系統便運行0號進程。
總的來說,雖然明白了大體的過程,但還是感覺對于內核的了解只是浮于淺淺的表面,好像懂了點什么,又好像什么也不懂。希望繼續深入的學習能夠解答我的疑惑。同時也以此自勉吧。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的实验三 Linux的启动与关闭,实验三:跟踪分析Linux内核的启动过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大校军衔相当于什么官
- 下一篇: 春节时间莲花山开放吗