<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 三、 來源:[JOS學習筆記(三)](http://blog.csdn.net/roger__wong/article/details/8596116) 過了個年,好久沒碰專業內的東西了,之前做的JOS相關的東西都快忘了,還是看了前面兩篇日志才想起來。 當進入內核后基本都是比較簡單的代碼了,我也并沒有全部分析,根據講義要求只分析了一下printf函數和堆棧的backtrace,所以這篇日志也就寫這兩個方面吧。 ## 1、printf函數。 進入kernel后從i386_init函數開始,首先做一些初始化工作,包括部分內存的清零,初始化顯示器串口等(無非是判斷一下地址使光標閃動正確的位置等),然后調用了cprintf,嘗試講一個10進制的數字用8進制來表示,而這個函數是需要我們完成的。 進入cprintf函數(printf.c文件)后,首先是使用va_前綴的函數(也許是宏)來取出參數,這是標準的c語言可變長度參數的實現形式。這個函數其實是一些_buildin前綴的函數的別名,而_buildin函數則是gcc內置的函數,并不在任何的JOS代碼中有定義,當GCC進行編譯的時候會自動將這些函數名與相應的函數體連接。之后則調用vcprintf函數。 vcprintf函數定義在同一個文件里,直接調用vprintfmt函數,值得注意的是傳入一個函數指針,指向了本文件的putch函數,這個函數之后再講。 進入vprintfmt(printfmt.c文件)函數,發現這里實現的控制打印格式的邏輯,然后調用傳進來的函數指針,進行具體的打印工作。找到含有case 'o'的代碼,仿照case 'd'的代碼完成8進制數字顯示即可,也就是原封不動的復制過來,將base改成8,如下: ![](https://box.kancloud.cn/2015-12-24_567b6e746e676.jpg) 之前的putch函數會調用console.c里的cons_putc函數,而cons_putc函數又調用了cga_putc /serial_putc/lpt_putc,分別對應寫顯示器,寫串口和并口,之所以我們不僅在qemu里面看到了kernel打印的文字,還在我們的控制臺里看到了打印文字,就是因為其寫了串口或者并口的原因,再由qemu將串口或并口輸出信息打印到控制臺(具體哪個口我沒有深究)。 一言以蔽之,console.c完成“如何打印”的邏輯,而printfmt.c完成“打印什么”的邏輯,它們的鏈接紐帶就是printf.c。 ## 2、堆棧的backtrace JOS中所有函數的調用公用一個堆棧,在之前已經分析過了堆棧是如何初始化以及位于內存中的位置,現在的任務是要從堆棧中找到函數的嵌套調用的關系,并顯示出來。 首先想一想,函數調用是如何使用堆棧的。拋開JOS不談,一個函數在調用時,肯定要壓入參數給函數體傳值(當然有時候也使用寄存器傳值,比如windows c里面的fast call,這里暫且不論),然后要壓入函數結束后的下一條指令的地址,以便函數可以正確的返回,其次因為公用一個堆棧所以要壓入BP也就是基址寄存器的值,和在函數體中使用到的寄存器的值,以便返回時可以恢復現場。但是這些值壓入的順序和規則目前還是不知道的,需要一些額外的資料。 通過讀kernel.asm的代碼或者看講義文件,我們可以知道JOS中的函數調用后堆棧的結構是這樣的: ![](https://box.kancloud.cn/2015-12-24_567b6e7480dc5.jpg) esp的含義是“這個地址以下的空間是未被使用的堆棧控件”,ebp的含義是“這個地址以下至esp的空間是屬于目前所執行函數的堆棧空間”,所以圖中saved%ebp和 ret%eip就是屬于調用此函數的函數的ebp和eip。 通過閱讀匯編代碼我們可以發現,一個函數在調用之前,其調用者會將參數壓棧(順序沒深究,和編譯器有關),也就是壓入arg2 和arg1,然后調用call,call的動作會把ret%eip壓棧,同時轉到函數體執行,在函數體執行的開頭有一段預處理代碼,也就是圖中的prologue,會將ebp寄存器(call指令不改變ebp的值,此時的ebp還是上一個函數的)內容壓棧,然后將當前esp賦值給ebp,隨后進行現場保存的工作,存儲在local variables空間里,值得注意的是,在預處理時會一下申請足夠的空間,包括保存現場所需空間,局部變量所需空間(這大概也就是標準C的變量聲明需要放在函數開頭的原因吧,為了方便編譯器),調用其它函數所壓入變量的空間,換句話說圖中arg1,arg2是屬于上一個函數的local variables空間,這也就是backtrace不能準確的判斷出函數所傳參數個數而統一要求打印出5個參數的原因。 因此,通過ebp不斷尋找上層的ebp,直到回溯所有的函數,在entry.S中可以看到,在調用i386_init之前,將ebp置0了,因此當ebp為0的時候就是函數返回的時候,按這個邏輯代碼如下: ``` int mon_backtrace(int argc, char **argv, struct Trapframe *tf) { uint32_t eip=read_eip(); uint32_t ebp=read_ebp(); cprintf("Stack backtrace:"); uint32_t esp=ebp; int j=0; while(ebp!=0) { cprintf("ebp %x eip %x ",ebp,eip); ebp=*(uint32_t *)(esp); esp+=4; eip=*(uint32_t *)(esp); esp+=4; cprintf("args "); for(j=0;j<=4;j++) { cprintf("%x ",*(uint32_t *)(esp)); esp+=4; } cprintf("\r\n"); esp=ebp; } return 0; } ``` 需要注意的是,因為是32位環境,所以esp指針的變化每次+4(32位是4字節)。 執行結果如下: ![](https://box.kancloud.cn/2015-12-24_567b6e748fc71.jpg) bingo! 可以看到,第一個函數參數為3個0,后面的函數參數依次是0 1 2 3 4 5,和代碼邏輯相同,說明implement應該沒什么大問題。 最后說一下read_eip()函數的技巧。 為什么這里read_eip()是個普通函數而不是一個inline函數,眾所周知,inline函數會在編譯的時候直接嵌入其調用者的代碼里,這樣嘗試讀取eip寄存器值這個動作本身就會改變eip寄存器的值,使read_eip()方法變的毫無意義。 因此作為一個普通函數,在調用的時候會把其調用者的eip入棧,然后在從棧中找到這個eip返回,棧中確切的位置之前分析過是ebp的上4個字節處,所以使用嵌入匯編高效的完成這個功能。 ![](https://box.kancloud.cn/2015-12-24_567b6e74a8dc6.jpg)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看