1. 程式人生 > >作業系統課設實驗一(Nachos上下文切換)

作業系統課設實驗一(Nachos上下文切換)

一、準備

1.32位系統的Linux

這裡我使用了32位的Ubuntu 16.04映象。

64位的我嘗試掙扎了一下不過還是跪了,而且我也沒有看到成功在64位Linux下跑成功過的博主。。

如果你是64位Linux,也可以考慮使用虛擬機器VMware workstation,是的,這個Linux也是有相應版本的,然後你就可以看到Linux裡面跑Linux的場景了。。

畫中畫
畫中畫

2.Nachos原始碼

百度應該能找到,不過下載前記得自己檢查符不符合自己的要求,我們老師要求的是3.4的C++版本,本文也是基於此的,如果你是用Java的可以不用往下看了。。

3.Makefile可能遇見的坑

從其他博主那裡看到的坑似乎還不少,不過我只遇見了編譯時的問題:

  • 將code目錄下的Makefile中的gmake改成make
  • 刪除Makefile.common中的-fwritable-strings

然後在code目錄下進行make即可,如果沒有報錯就說明make成功。

make成功

 二、問題重述

1. trace the execution of Nachos and observe the executions of
(a) context switch function SWITCH()
(b) function ThreadRoot()
using gdb and
2. answer the following questions:
(a) What are the addresses of the following functions in your Nachos:
i.InterruptEnable()
ii.SimpleThread()
iii.ThreadFinish()
iv.ThreadRoot()
and describe how did you find them.
(b) What are the addresses of the thread objects for
i. the main thread of the Nachos
ii. the forked thread created by the main thread
and describe how did you find them.
(c) When the main thread executes SWITCH() function for the first time, to what address
the CPU returns when it executes the last instruction ret of SWITCH()? What
location in the program that address is referred to?
(d) When the forked thread executes SWITCH() function for the first time, to what
address the CPU returns when it executes the last instruction ret of SWITCH()?
What location in the program that address is referred to?

原題是英文的,不過基本上比較簡單,後面解決部分會用中文來進行描述,此外還要注意,本文題目除錯的是在threads資料夾下編譯好的的nachos,因為問題都和執行緒的上下文切換有關。

三、問題解答

1.追蹤SWITCH和ThreadRoot函式

想檢視某個函式的執行情況,只需要為該函式打上斷點然後執行程式即可,如:break SWITCH,然後run即可,但是由於SWITCH函式和ThreadRoot函式內部都是彙編語句,因此這裡我們可以顯示彙編程式碼來輔助我們進行分析,使用layout asm命令即可:

顯示SWTICH內部的彙編語句情況

2.解答問題

首先這裡執行的是threads目錄下的nachos,不要進錯了目錄。。

cd threads
gdb nachos

a.找到幾個函式的記憶體地址

gdb自帶命令可以檢視某個函式的地址空間,指令為

  p 函式名

藉此可以檢視題目給出的四個函式在記憶體中的地址空間,如圖:

各函式的記憶體地址空間

b.找到主執行緒和子執行緒的地址空間

要想找到兩個執行緒的地址空間,我們首先應該對原始碼進行分析,然後確定好斷點要打的位置。

i.通過分析,我們發現在位於system.cc中的Initialize函式有這樣的程式碼:

Initialize函式部分程式碼

而根據Initialize函式在main函式中十分靠前的位置,我們可以確定,這裡就是主執行緒的初始處。

main函式中的Initialize函式

因此,我們只需要給Initialize函式打斷點,進入後執行到currentThread設定狀態的部分,然後檢視其記憶體地址即可,效果如下:

主執行緒的位置

如圖所示,其記憶體地址為0x8053a88。

ii.通過分析原始碼,我們知道主執行緒Fork子執行緒的地方在threadtest.cc的ThreadTest1函式中:

ThreadTest1函式中進行Fork

因此我們只需要在ThreadTest1函式打上斷點,並執行到給t指標申請空間的部分,再檢視t的地址即可,如圖:

子執行緒的位置

通過除錯,我們可以知道子執行緒的記憶體地址為0x8053ae8。

事實上,這一問也可以通過列印currentThread裡面的值來獲取到兩個執行緒的地址,但是這樣不太好哪個是主執行緒哪個是子執行緒

c.找到主執行緒進入SWITCH最後一句話執行時CPU的返回值

這一問相對較複雜,首先我們分析swtich.s檔案

switch.s

通過註釋可以得知,在ret指令的倒數第三行提到了返回地址被存入了暫存器eax中 ,接下來我們除錯分析彙編部分的程式碼。

gdb中SWITCH函式的情況

而通過對彙編部分的檢視,可以看到其對應的是SWITCH指令偏移77的位置,所以應該在SWITCH+80處打斷點然後分析eax暫存器的數值。

這裡也是通過查閱資料知道原來gdb可以將斷點打在彙編語句上面,並且還能檢視暫存器的值,真的漲姿勢。。

內部打斷點和檢視暫存器的值的指令如下:

(gdb) b *SWITCH +84 #在SWTICH偏移84的位置打斷點
(gdb) i r eax #檢視eax暫存器的值

效果如圖:

eax暫存器情況

如圖可見,此時eax暫存器內部的值為0x804b1c2,我們可以進一步為這個地址打上斷點然後執行:

0x804b1c2指向的地址空間

可見,此時返回值為ThreadRoot函式的地址空間。 

d.找到子執行緒進入SWITCH最後一句話執行時CPU的返回值

這一問與上一問略有相似,但是不同之處在於我們需要分析的是線上程fork之後執行SWITCH函式最後一句後的CPU返回值,因此要完成這一問,我們首先需要定位到執行緒fork之後執行SWITCH的位置,通過分析原始碼我們知道每一次SWITCH都會完成一次程序切換,因此,我們先進行一次SWITCH,然後檢視當前的currentThread的值:

第一次到達SWITCH

該值為子執行緒的地址空間,可見,此時已經進入了子程序的執行緒空間,我們按下n直至再次執行到SWITCH函式。我們按上一問的方式繼續檢視eax暫存器的值,其值變成了0x80491cf。

再次執行到SWITCH函式

我們再次為該值打上斷點然後執行到那裡: 

0x80491cf指向的地址空間

可以看到,此時的值指向的是scheduler.cc檔案中的116行,該行程式碼為:

scheduler.cc第116行

 

程式完整執行效果:

程式完整執行效果

四、小結

由實驗結果可以看出,程式執行與老師課上所講基本一致,比如其中的一些關鍵點:

  1. 主執行緒初始化的方式和子執行緒不一致,前者在Initialize函式中完成,而後者使用Fork實現。
  2. 主執行緒進入SWITCH最後一句話執行時CPU的返回值指向ThreadRoot函式,子執行緒進入SWITCH最後一句話執行時CPU的返回值指向Run函式中的SWITCH函式語句。

而gdb功能的強大也真的是令我佩服。

此外這篇報告已經修改四次了,感謝小夥伴提出的修改意見emmm。。