日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux-0.00 代码解析(三)

發布時間:2025/3/15 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux-0.00 代码解析(三) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

6. 安裝中斷門和陷阱門

# setup timer & system call interrupt descriptors.movl $0x00080000, %eax movw $timer_interrupt, %axmovw $0x8E00, %dxmovl $0x08, %ecx # The PC default timer int.lea idt(,%ecx,8), %esimovl %eax,(%esi) movl %edx,4(%esi)movw $system_interrupt, %axmovw $0xef00, %dxmovl $0x80, %ecxlea idt(,%ecx,8), %esimovl %eax,(%esi) movl %edx,4(%esi)

中斷門的格式是:

代碼中edx是高32位,eax是低32位

movl $0x00080000, %eax 代碼段的選擇子就緒
movw $timer_interrupt, %ax 偏移15..0就緒
movw $0x8E00, %dx edx的低16位就緒

我的疑問是:edx的高16位呢?

movl $0x08, %ecx
從表格中可以看出BIOS把8253的中斷號設置為8.

lea idt(,%ecx,8), %esi
esi = idt + ecx * 8,計算出第8個中斷門的位置,乘以8是因為一個中斷門描述符占8字節。

movl %eax,(%esi) movl %edx,4(%esi) # 安裝中斷門

此中斷門用于切換任務,每隔10ms切換一次。

中斷門timer_interrupt的代碼是:

timer_interrupt:push %dspushl %eaxmovl $0x10, %eax # 0x10是內核數據段的選擇子mov %ax, %dsmovb $0x20, %aloutb %al, $0x20 # 向8259發送中斷結束(EOI)命令,端口是0x20, 命令字是0x20,不用深究movl $1, %eax # eax=1cmpl %eax, currentje 1f #相等跳轉到1處movl %eax, current # current = 1ljmp $TSS1_SEL, $0 #切換到任務1jmp 2f 1: movl $0, current #切換到任務0ljmp $TSS0_SEL, $0 2: popl %eaxpop %dsiret

ljmp $TSS1_SEL, $0
當處理器執行這條指令時,首先用指令中給出的段選擇子訪問GDT或LDT(這里是GDT),分析它的描述符類型,這里發現是TSS描述符,于是執行任務切換,指令中的偏移量(這里是0)被忽略。

下面是安裝陷阱門。
陷阱門的格式是:

movw $system_interrupt, %ax eax的低16位就緒,高16位在上面已經設置成了代碼段的選擇子
movw $0xef00, %dx DPL=3 的陷阱門

疑問:edx的高16位呢?
movl $0x80, %ecx 系統調用向量號是0x80,這個0x80應該是作者指定的

lea idt(,%ecx,8), %esi #這三句同上,不贅述 movl %eax,(%esi) movl %edx,4(%esi)

這個陷阱門其實是一個系統調用,用AL傳參,把AL代表的字符打印到屏幕上。其內部調用了內核過程write_char

system_interrupt: # 0x80系統調用,把AL中的字符打印到屏幕上push %dspushl %edxpushl %ecxpushl %ebxpushl %eaxmovl $0x10, %edxmov %dx, %ds #以上兩句是否可以不要??call write_charpopl %eaxpopl %ebxpopl %ecxpopl %edxpop %dsiret

7. 開始執行任務0

# Move to user mode (task 0)pushflandl $0xffffbfff, (%esp)popflmovl $TSS0_SEL, %eaxltr %axmovl $LDT0_SEL, %eaxlldt %ax movl $0, currentstipushl $0x17pushl $init_stackpushflpushl $0x0fpushl $task0iret

上面這段代碼要想說清楚,就說來話長了。

pushfl #把EFLAGS入棧andl $0xffffbfff, (%esp) #設NT=0popfl #加載EFLAGS

上面三行是為了使EFLAGS的NT位=0;為什么要這樣做呢?因為后面要用iret指令返回,當返回的時候,如果NT=1,表示返回到前一個任務,而這種情況不是我們想要的。

movl $TSS0_SEL, %eax #TSS0_SEL是任務0的TSS選擇子ltr %ax

以上兩行是把任務0的TSS選擇子裝入任務寄存器TR;
LTR指令的格式是

ltr r/m16

操作數是16位的通用寄存器或者是指向16位單元的內存地址。TSS選擇子是16位的,所以沒有用eax,而用ax; LLDT指令用法類似。

movl $LDT0_SEL, %eaxlldt %ax #把任務0的LDT段選擇子裝入LDTR(局部描述符表寄存器)movl $0, current #表示當前運行的是任務0sti #開中斷

7.1 中斷處理過程

咱們先復習一下異常或中斷的處理過程。
當目標代碼段描述符的DPL(可以用門描述符中的段選擇子,從GDT或LDT中找到)在數值上<=CPL(當前特權級)時,才允許將控制轉移到中斷或異常處理程序。

如果 DPL < CPL,將發生棧切換。棧切換的過程:
(1)根據DPL,從當前任務的TSS中取得對應的SS和ESP,作為中斷或異常處理過程使用的SS和ESP(新棧)。
(2)處理器把舊棧的選擇子和棧指針壓入新棧。
(3)把EFLAGS、CS、EIP壓入新棧。
(4)對于有錯誤碼的異常,還要把錯誤代碼壓入新棧(我們模擬返回的時候沒有錯誤碼)。

有了上面的鋪墊,我們可以繼續看代碼了。

pushl $0x17 #把任務0的局部空間數據段(也是棧段)選擇子入棧pushl $init_stack #把任務0的ESP入棧pushfl #把EFLAGS入棧pushl $0x0f #把任務0的代碼段選擇子入棧pushl $task0 #把任務0的EIP入棧iret #環境已經設置完畢,這里模擬從中斷返回,返回后開始執行任務0,其特權級為3

7.2 中斷返回過程

先不管后面的操作數是怎么來的,總之壓棧該壓什么,順序是什么我們理解了。接下來看看IRET指令。這個指令的含義,我首先看了Intel指令集手冊,感覺說得很復雜,某些地方還有歧義。我不甘心,又搜了很多網上的資料,沒有一個令我滿意。最后,我打算用AMD的解釋,因為很簡明:

IRET, Less Privilege. If an IRET changes privilege levels, the return program must be at a lower privilege than the interrupt handler. The IRET in this case causes a stack switch to occur:

  • The return pointer is popped off of the stack, loading both the CS register and EIP register with the saved values. The return code-segment RPL is read by the processor from the CS value stored on the stack to determine that a lower-privilege control transfer is occurring.
  • The saved EFLAGS image is popped off of the stack and loaded into the EFLAGS register.
  • The return-program stack pointer is popped off of the stack, loading both the SS register and ESP register with the saved values.
  • Control is transferred to the return program at the target CS:EIP.
  • 說到這里,文件head.s的主體就完了。

    8. 任務0的LDT

    ldt0: .quad 0x0000000000000000.quad 0x00c0fa00000003ff # 0x0f.quad 0x00c0f200000003ff # 0x17

    任務0的局部描述符表分析如下:

    索引號描述符類型基地址段界限粒度PDPL備注選擇子
    0空描述符-------
    1代碼段00X3FF4KB13代碼段,非一致性,可讀0x0F
    2數據段00X3FF4KB13數據段,向上擴展,可寫0x17

    為了少耗費點腦細胞,我寫了一個C語言的小程序,專門用來分析各種類型的段描述符,拿走不謝。
    http://blog.csdn.net/longintchar/article/details/78881396
    運行截圖如下:

    9. 任務0的TSS

    tss0: .long 0 /* back link */.long krn_stk0, 0x10 /* esp0, ss0*/.long 0, 0, 0, 0, 0 /* esp1, ss1, esp2, ss2, cr3 */.long 0, 0, 0, 0, 0 /* eip, eflags, eax, ecx, edx */.long 0, 0, 0, 0, 0 /* ebx esp, ebp, esi, edi */.long 0, 0, 0, 0, 0, 0 /* es, cs, ss, ds, fs, gs */.long LDT0_SEL, 0x8000000 /* ldt, trace bitmap */.fill 128,4,0 krn_stk0:

    以上填0的字段就不分析了,我們重點看看非0字段。
    krn_stk0是ESP0,就在代碼的最后一行。
    0x10是內核數據段的選擇子。
    LDT0_SEL是任務0的LDT的選擇子,數值上 = 0x28,在GDT中有定義。

    復習一下,GDT中的描述符。

    索引號選擇子描述符類型基地址段界限粒度PDPL備注
    10x08代碼段00X7FF4KB10內核代碼段,非一致性,可讀
    20x10數據段00X7FF4KB10內核數據段,向上擴展,可寫
    30x18數據段0XB80000X24KB10內核顯存段,向上擴展,可寫
    40x20TSS段tss00X681B13任務0的TSS段,不忙
    50x28LDT段ldt00X401B13任務0的LDT描述符
    60x30TSS段tss10X681B13任務1的TSS段,不忙
    70x38LDT段ldt10X401B13任務1的LDT描述符

    0x8000000是I/O許可位串。如果這個值大于或者等于TSS的段界限(在TSS描述符中),則表明沒有I/O許可位串。因為TSS的段界限是0x68,所以表示沒有許可位串。

    10. 任務1的TSS

    tss1: .long 0 /* back link */.long krn_stk1, 0x10 /* esp0, ss0 */.long 0, 0, 0, 0, 0 /* esp1, ss1, esp2, ss2, cr3 */.long task1, 0x200 /* eip, eflags */.long 0, 0, 0, 0 /* eax, ecx, edx, ebx */.long usr_stk1, 0, 0, 0 /* esp, ebp, esi, edi */.long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */.long LDT1_SEL, 0x8000000 /* ldt, trace bitmap */.fill 128,4,0 krn_stk1:

    和任務0相比,任務1的TSS有幾點不同。
    (1). eip和eflags不是0,而是task1和0x200,task1是任務1代碼的入口點,當任務0首次被時鐘中斷打斷時,將會切換到任務1,這時CPU把tss1作為任務1的EIP初始值。同理,0x200作為任務1的eflags初始值。為什么eflags初始值是0x200呢?根據下圖,可以知道eflags中IF為1,表示允許中斷。必須要允許中斷,因為任務切換靠中斷實現。

    【System Flags in the EFLAGS Register】

    (2). 任務0的esp=0,ss=0,任務1的esp=usr_stk1,ss=0x17。這是因為TSS中的esp和ss對應的特權級別是3,任務0的esp、ss的初始值通過iret指令從棧中獲取,而任務1的要從TSS中獲取。
    (3). 任務0的es,cs,ds,fs,gs都為0,任務1的es=ds=fs=gs=0x17,cs=0x0f,道理同上,任務0的cs初始值從棧中獲得,任務1的所有段寄存器的初始值都從TSS中獲得。

    下圖是32位任務狀態段(TSS)的格式,貼出來方便復習。

    11. 任務0的代碼段

    task0:movl $0x17, %eax #0x17是任務0的數據段的選擇子movw %ax, %ds #因為任務0沒有用到局部數據段,所以這兩句可以不要movb $65, %al # print 'A' int $0x80 # 系統調用movl $0xfff, %ecx 1: loop 1b # 為了延時jmp task0 # 死循環

    任務1的代碼段類似,不再贅述。

    用了3篇博文,基本把代碼說完了。下篇文章打算修改幾個地方并做實驗。

    【未完待續】

    總結

    以上是生活随笔為你收集整理的Linux-0.00 代码解析(三)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。