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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

kernel 3.10内核源码分析--中断--中断和异常返回流程

發布時間:2025/3/15 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 kernel 3.10内核源码分析--中断--中断和异常返回流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、問題
1、內核調度與中斷/異常/系統調用的關系如何?
2、信號處理與中斷/異常/系統調用的關系如何?
3、內核搶占與中斷/異常/系統調用的關系如何?

4、內核線程的調度有何特別之處?中斷/異常/系統調用返回時,內核線程會發生調度嗎?

?這些問題都需要分析清楚中斷/異常的返回流程,才能解答。

二、中斷/異常的返回流程
1、中斷/異常的返回內核軟件流程
中斷、異常(包括系統調用)和fork返回的處理流程如下:

?

關鍵點:
1)中斷返回和異常返回的流程基本一致,差別主要在于異常返回時,需要先關一次中斷。因為Linux實現中,異常使用的是陷阱門,通過時不會自動關中斷;而中斷使用的是中斷門,通過時會自動關中斷。
2)中斷/異常(包括系統調用)返回時,是進行調度(schedule)的重要時機點,其中,中斷(時鐘中斷)返回時調度依賴的最主要的時機點,時鐘中斷處理函數中不會直接進行調度,只是根據相應的調度算法,決定是否需要調度,以及調度的next task,如果需要調度,則設置NEED_RESCHED標記。調度(schedule)的實際執行是在中斷返回的時候,檢查NEED_RESCHED標記,如果設置則進行調度。
3)信號處理是在當前進程從內核態返回用戶態時進行的,在發生中斷、異常(包括系統調用)、或fork時,都有可能從內核態返回用戶態,都是處理信號的時機。注意:只有current進程的信號才能在此時得到處理。其它非正在運行的進程的信號無法處理。
4)關于內核搶占。中斷/異常發生在內核態時,也就是說中斷/異常返回時,需要返回內核態,走resume_kernel流程,此時,如果內核支持內核搶占,則此時是個關鍵的調度時機點,如果內核不支持搶占,則不會發生調度。也就是說:如果當前進程上下文處于內核態,當不支持內核搶占時,則無論進程的優先級和時間片如何,都是不能發生調度的,只能在返回用戶態時,才能發生調度。從這點可以看出,當不支持內核搶占時,Linux的實時性很差(開啟內核搶占后稍好),當在內核態(中斷、軟中斷、其它內核流程)執行時間或流程太長時,可能導致進程調度饑餓,極端情況下,當在內核態發生死鎖時,會直接導致整個系統因無法調度而死鎖,當然針對這種情況(softlockup),內核提供了專門的watchdog機制來檢測。

5)關于內核線程的調度,跟普通線程相比,從原理和機制上看,沒有特別之處。但關鍵的不同在于:內核線程始終運行在內核態,當沒有開啟內核搶占時,設想當一個內核線程被中斷/異常打斷,此時從中斷/異常返回時會發生調度嗎?答案是不會,因為當前進程的上下文處于內核態,在沒有開啟內核搶占的情況下,是不會發生調度行為的,除非該內核線程主動調用schedule()釋放CPU控制權。也就是說,內核線程觸發主動調用schedule,否則會一直占用CPU。所以在編寫內核線程時,需要在相關任務處理結束后,主動調用schedule,這點需要注意。
?

2、中斷/異常返回時硬件完成的處理流程
中斷或異常返回時,必然會執行iret指令,然后將控制器交回給之前被中斷打斷的進程,硬件自動完成如下操作:
1)從當前棧(內核棧)中彈出cs、eip和eflag,并load到相應的寄存器中寄存器。(如之前有硬件錯誤碼入棧,需要先彈出這個錯誤碼)。?
2)權限檢查。比對ISR的CPL是否等于cs中的低兩位的值。如果是,iret終止返回;否則,轉入下一步。?
3)從當前棧(內核棧)中彈出之前壓入的用戶態堆棧相關的ss和esp,并load到相應寄存器,至此,即完成了從內核棧到用戶棧的切換。?
4)后續處理。主要包括:檢查ds、es、fs及gs段寄存器,如果其中一個寄存器包含的選擇符是一個段描述符,并且其DPL值小于CPL,那么,清相關的段寄存器。目的是為了防止用戶態的程序利用內核以前所用的段寄存器,以防止惡意用戶程序利用其訪問內核地址空間。


三、代碼分析
中斷、異常(包括系統調用)、fork返回時,會分別跳轉到entry_32.S匯編代碼中的ret_from_intr、ret_from_exception、ret_from_fork標號處執行。相應代碼分析如下:
1、中斷返回(ret_from_intr)
1)主流程

點擊(此處)折疊或打開

  • /*從中斷返回*/
  • ret_from_intr:
  • /*將當前進程的thread_info結構體的指針存入%ebp幀寄存器中*/
  • GET_THREAD_INFO(%ebp)
  • #ifdef CONFIG_VM86
  • /* 取中斷之前寄存器EFLAGS的高16位和段寄存器CS的內容構成的32位長整數放入eax中,其目的是檢驗:
  • ?* 1.中斷之前CPU是否運行于VM86模式
  • ?* (EFLAGS的高16位中的第二位用來標識CPU運行在VM86模式下)
  • ?* 2.中斷之前CPU運行于用戶空間還是系統空間
  • ?*(CS的低兩位代表著中斷發生時CPU的運行級別CPL。若是CS的低兩位為1,表示中斷發生于用戶空間,)
  • ????????*/
  • movl PT_EFLAGS(%esp), %eax????# mix EFLAGS and CS
  • movb PT_CS(%esp), %al
  • andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
  • #else
  • /*
  • * We can be coming here from child spawned by kernel_thread().
  • */
  • movl PT_CS(%esp), %eax
  • andl $SEGMENT_RPL_MASK, %eax
  • #endif
  • // 判斷是否返回用戶態或者v8086模式,如果不是,則轉入resume_kernel,否則進入resume_userspace
  • cmpl $USER_RPL, %eax
  • jb resume_kernel????# not returning to v8086 or userspace

  • 2)返回用戶態

    點擊(此處)折疊或打開

  • // 如果是返回用戶態
  • ENTRY(resume_userspace)
  • LOCKDEP_SYS_EXIT
  • /*
  • * 前面已經關了中斷了,這次再關的原因是,還有其它流程會自己跳轉
  • * 到這里,比如system_call
  • */
  • ?????DISABLE_INTERRUPTS(CLBR_ANY)????# make sure we don

  • 3)調度和信號處理:

    點擊(此處)折疊或打開

  • work_pending:
  • ????# 返回用戶態時,只需要判斷need_resched是否置位,不需要判斷preempt_count
  • ????# 如果need_resched置位,則發生調度,否則跳轉到work_notifysig
  • ????testb $_TIF_NEED_RESCHED, %cl
  • ????# 進行信號處理
  • ????jz work_notifysig
  • work_resched:
  • ????# 需要調度,調用schedule函數
  • ????call schedule
  • ????# 調度返回,注意:到這里已經是新的進程上下文了,后面有機會處理信號
  • ????LOCKDEP_SYS_EXIT
  • ????# 關中斷
  • ????DISABLE_INTERRUPTS(CLBR_ANY)????# make sure we don't miss an interrupt
  • ????????????????????# setting need_resched or sigpending
  • ????????????????????# between sampling and the iret
  • ????# 關閉trace irq功能
  • ????TRACE_IRQS_OFF
  • ????movl TI_flags(%ebp), %ecx
  • ????# 再次確認是否還有其它事情處理
  • ????andl $_TIF_WORK_MASK, %ecx????# is there any work to be done other
  • ????????????????????# than syscall tracing?
  • ????# 如果沒有,則恢復上下文
  • ????jz restore_all
  • ????# 如果有,再次檢查是否需要調度,如果需要,則再次跳轉到work_resched進行重新調度
  • ????testb $_TIF_NEED_RESCHED, %cl
  • ????jnz work_resched????
  • ????# 如果不需要調度,則繼續到work_notifysig,進行信號處理了,也就是說如果這里發生調度,也是有機會處理信號的。

  • work_notifysig:????????????????# deal with pending signals and
  • ????????????????????# notify-resume requests
  • #ifdef CONFIG_VM86
  • ????testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
  • ????movl %esp, %eax
  • ????jne work_notifysig_v86????????# returning to kernel-space or
  • ????????????????????# vm86-space
  • 1:
  • #else
  • ????movl %esp, %eax
  • #endif
  • ????# 開trace
  • ????TRACE_IRQS_ON
  • ????# 開中斷(前面關了),意味著信號處理是開中斷執行的,還是中斷優先級高
  • ????ENABLE_INTERRUPTS(CLBR_NONE)
  • ????# 再次判斷CS低兩位,當為1時,表示中斷/異常之前處于用戶態,否則為內核態,據此再次確認是否需要返回內核態
  • ????movb PT_CS(%esp), %bl
  • ????andb $SEGMENT_RPL_MASK, %bl
  • ????cmpb $USER_RPL, %bl
  • ????# 返回內核態
  • ????jb resume_kernel
  • ????# edx清零
  • ????xorl %edx, %edx
  • ????# 調用C函數,其中進行通知鏈即信號的處理
  • ????call do_notify_resume
  • ????# 信號處理完后,重新跳轉到resume_userspace,此時如果沒有新的信號產生,則會在前面就通過restore_all恢復了,不會再到這里了
  • ????jmp resume_userspace

  • #ifdef CONFIG_VM86
  • ????ALIGN
  • work_notifysig_v86:
  • ????pushl_cfi %ecx????????????# save ti_flags for do_notify_resume
  • ????call save_v86_state????????# %eax contains pt_regs pointer
  • ????popl_cfi %ecx
  • ????movl %eax, %esp
  • ????jmp 1b
  • #endif
  • END(work_pending)

  • 4)返回內核態

    點擊(此處)折疊或打開

  • /*如果配置了內核搶占*/
  • #ifdef CONFIG_PREEMPT
  • ENTRY(resume_kernel)
  • /*
  • * 前面已經關了中斷了,這次再關的原因是,還有其它流程會自己跳轉
  • * 到這里,比如system_call
  • */
  • DISABLE_INTERRUPTS(CLBR_ANY)
  • /*判斷是否可以搶占*/
  • cmpl $0,TI_preempt_count(%ebp)????# non-zero preempt_count ?
  • /*搶占計數非0,不能搶占,則不產生調度,直接恢復上下文*/
  • jnz restore_all
  • /*可以搶占,則需要調度*/
  • need_resched:
  • /*判斷need_resched是否被設置*/
  • movl TI_flags(%ebp), %ecx????# need_resched set ?
  • testb $_TIF_NEED_RESCHED, %cl
  • /*沒設置need_resched,則不需要調度,直接恢復上下文*/
  • jz restore_all
  • /*
  • *判斷發生中斷時(因為PT_EFLAGS(%esp)中保存的是進入中斷時的EFLAGS值,這是由CPU硬件自動壓棧的,中斷走中斷門,會自動關中斷,異常走陷阱門,不自動關中斷)是否關中斷了,
  • *如果關了,表示是異常上下文(Fixme:應該是中斷吧),則直接恢復上下文。
  • */
  • testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)????# interrupts off (exception path) ?
  • jz restore_all
  • /*如果沒關中斷,表示為中斷上下文?則調用preempt_schedule_irq,進行調度*/
  • call preempt_schedule_irq
  • jmp need_resched
  • END(resume_kernel)

  • 2、異常返回(ret_from_exception)
    異常返回跟中斷返回流程基本一致,差別主要在于異常返回時,需要先關一次中斷。因為Linux實現中,異常使用的是陷阱門,通過時不會自動關中斷;而中斷使用的是中斷門,通過時會自動關中斷。

    點擊(此處)折疊或打開

  • /*從異常返回*/
  • ret_from_exception:
  • /*
  • * 這里為什么要關中斷?而從中斷返回不需要? 因為異常走的是陷阱門,
  • * 默認是不關中斷執行的,而中斷走的是中斷門,默認是關中斷執行的?
  • *
  • */
  • /*關中斷*/
  • preempt_stop(CLBR_ANY)
  • /*從中斷返回*/
  • ret_from_intr:
  • ...

  • 3、fork返回(ret_from_fork)
    fork返回的后半部分處理跟異常/中斷返回一致,前面一部分有單獨的處理:包括調用schedule_tail和跳轉syscall_exit進行相關處理

    點擊(此處)折疊或打開

  • #fork返回,單獨處理
  • ENTRY(ret_from_fork)
  • CFI_STARTPROC
  • pushl_cfi %eax
  • #進行調度收尾處理,包括回收DEAD(X)狀態的進程
  • call schedule_tail
  • #獲取thread_info放入ebp中
  • GET_THREAD_INFO(%ebp)
  • popl_cfi %eax
  • #重設kernel eflags
  • pushl_cfi $0x0202????# Reset kernel eflags
  • popfl_cfi
  • #跳轉到syscall_exit進行系統調用退出相關的處理。
  • jmp syscall_exit
  • CFI_ENDPROC
  • END(ret_from_fork)

  • 原文地址: http://blog.chinaunix.net/uid-14528823-id-4761421.html

    總結

    以上是生活随笔為你收集整理的kernel 3.10内核源码分析--中断--中断和异常返回流程的全部內容,希望文章能夠幫你解決所遇到的問題。

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