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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

一、問題
1、內(nèi)核調(diào)度與中斷/異常/系統(tǒng)調(diào)用的關(guān)系如何?
2、信號處理與中斷/異常/系統(tǒng)調(diào)用的關(guān)系如何?
3、內(nèi)核搶占與中斷/異常/系統(tǒng)調(diào)用的關(guān)系如何?

4、內(nèi)核線程的調(diào)度有何特別之處?中斷/異常/系統(tǒng)調(diào)用返回時,內(nèi)核線程會發(fā)生調(diào)度嗎?

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

二、中斷/異常的返回流程
1、中斷/異常的返回內(nèi)核軟件流程
中斷、異常(包括系統(tǒng)調(diào)用)和fork返回的處理流程如下:

?

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

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

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


三、代碼分析
中斷、異常(包括系統(tǒng)調(diào)用)、fork返回時,會分別跳轉(zhuǎn)到entry_32.S匯編代碼中的ret_from_intr、ret_from_exception、ret_from_fork標(biāo)號處執(zhí)行。相應(yīng)代碼分析如下:
1、中斷返回(ret_from_intr)
1)主流程

點擊(此處)折疊或打開

  • /*從中斷返回*/
  • ret_from_intr:
  • /*將當(dāng)前進程的thread_info結(jié)構(gòu)體的指針存入%ebp幀寄存器中*/
  • GET_THREAD_INFO(%ebp)
  • #ifdef CONFIG_VM86
  • /* 取中斷之前寄存器EFLAGS的高16位和段寄存器CS的內(nèi)容構(gòu)成的32位長整數(shù)放入eax中,其目的是檢驗:
  • ?* 1.中斷之前CPU是否運行于VM86模式
  • ?* (EFLAGS的高16位中的第二位用來標(biāo)識CPU運行在VM86模式下)
  • ?* 2.中斷之前CPU運行于用戶空間還是系統(tǒng)空間
  • ?*(CS的低兩位代表著中斷發(fā)生時CPU的運行級別CPL。若是CS的低兩位為1,表示中斷發(fā)生于用戶空間,)
  • ????????*/
  • 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
  • // 判斷是否返回用戶態(tài)或者v8086模式,如果不是,則轉(zhuǎn)入resume_kernel,否則進入resume_userspace
  • cmpl $USER_RPL, %eax
  • jb resume_kernel????# not returning to v8086 or userspace

  • 2)返回用戶態(tài)

    點擊(此處)折疊或打開

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

  • 3)調(diào)度和信號處理:

    點擊(此處)折疊或打開

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

  • 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
  • ????# 開中斷(前面關(guān)了),意味著信號處理是開中斷執(zhí)行的,還是中斷優(yōu)先級高
  • ????ENABLE_INTERRUPTS(CLBR_NONE)
  • ????# 再次判斷CS低兩位,當(dāng)為1時,表示中斷/異常之前處于用戶態(tài),否則為內(nèi)核態(tài),據(jù)此再次確認(rèn)是否需要返回內(nèi)核態(tài)
  • ????movb PT_CS(%esp), %bl
  • ????andb $SEGMENT_RPL_MASK, %bl
  • ????cmpb $USER_RPL, %bl
  • ????# 返回內(nèi)核態(tài)
  • ????jb resume_kernel
  • ????# edx清零
  • ????xorl %edx, %edx
  • ????# 調(diào)用C函數(shù),其中進行通知鏈即信號的處理
  • ????call do_notify_resume
  • ????# 信號處理完后,重新跳轉(zhuǎn)到resume_userspace,此時如果沒有新的信號產(chǎn)生,則會在前面就通過restore_all恢復(fù)了,不會再到這里了
  • ????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)返回內(nèi)核態(tài)

    點擊(此處)折疊或打開

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

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

    點擊(此處)折疊或打開

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

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

    點擊(此處)折疊或打開

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

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

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。