Aros/開發者/文件/資源/核心
kernel.resource 包含 AROS 微核心。它是最低級別的元件,負責處理 CPU 和主機板。對於託管埠,kernel.resource 包含一個虛擬機器。
kernel.resource 目前提供以下功能
- 任務排程
- KrnSetScheduler()
- KrnGetScheduler()
- KrnCause()
- KrnDispatch()
- KrnSwitch()
- KrnSchedule()
- 中斷管理
- KrnAddIRQHandler()
- KrnRemIRQHandler()
- KrnCli()
- KrnSti()
- CPU 管理
- KrnIsSuper()
- KrnAddExceptionHandler()
- KrnRemExceptionHandler()
- KrnCreateContext()
- KrnDeleteContext()
- MMU 管理
- KrnMapGlobal()
- KrnUnmapGlobal()
- KrnVirtualToPhysical()
- KrnAllocPages()
- KrnFreePages()
- 除錯
- KrnBug()
- KrnPutChar()
- KrnMayGetChar()
- KrnRegisterModule()
- KrnUnregisterModule()
- KrnDecodeLocation()
- 系統資訊和控制
- KrnGetBootInfo()
- KrnGetSystemAttr()
- KrnSetSystemAttr()
大多數這些函式旨在用於系統自身的用途,而不是使用者軟體。它們由 AROS 的其他各種元件呼叫,併為其提供硬體抽象層。所有函式都描述在自動文件中。下面將給出更詳細的說明,其中討論了實現細節。
目前,kernel.resource 實現了原始的 AmigaOS(tm) 相容的優先順序迴圈排程器。排程器包含三個在 supervisor 特權級別執行的函式
- core_Schedule() - 檢查就緒任務列表,並通知當前任務是否需要切換
- core_Switch() - 從當前任務切換出去
- core_Dispatch() - 選擇一個新的任務執行
通常,它們在以指定順序處理 CPU 中斷後執行。也可以透過三個相應的 API 入口點顯式呼叫排程器:KrnSchedule()、KrnSwitch() 和 KrnDispatch()。
KrnSchedule() 是排程器的主要手動入口點。當 AROS 任務認為應該將 CPU 時間讓給其他任務時,它會呼叫此函式。該函式會導致 core_Schedule() 在 supervisor 級別執行。如果 core_Schedule() 發現當前任務可以繼續執行,則返回 FALSE。否則,它將任務放入就緒任務列表 (SysBase->TaskReady) 並返回 TRUE。這意味著 core_Switch() 和 core_Dispatch() 應該接下來進行。
KrnSwitch() 和 KrnDispatch() 是進入排程器的中間點入口。KrnSwitch() 在當前任務已放入 exec.library 列表之一,並且需要立即停止時被呼叫。目前,這在 exec.library/Wait() 內部完成,該函式將任務放入 SysBase->TaskWait 列表並將任務設定為等待狀態。KrnDispatch() 由 exec.library/RemTask() 呼叫,以便在刪除當前任務時完成其執行。
core_Switch() 和 core_Dispatch() 實際上執行任務切換。core_Switch() 儲存當前任務的狀態,core_Dispatch() 選擇一個新的任務並恢復其狀態。這些例程實際上是 CPU 相關的,因此它們永遠不會被直接使用。相反,應在 CPU 特定程式碼中實現名為 cpu_Switch() 和 cpu_Dispatch() 的 CPU 特定包裝器。
cpu_Switch() 很簡單。它將任務的上下文(CPU 暫存器)儲存到內部 ETask 結構中,然後跳轉到 core_Switch()。core_Switch() 只是通用部分,它包括儲存 SysBase->IDNestCnt 以及根據需要呼叫任務的 tc_Switch 通知向量。
cpu_Dispatch() 是一個棘手的地方。除了恢復 CPU 上下文外,還應處理 CPU 空閒狀態和任務異常。當 core_Dispatch() 返回 NULL 時,應進入空閒狀態。這意味著沒有剩餘的就緒執行任務。要麼所有現有的任務都在等待某些訊號,要麼根本沒有更多工。在空閒狀態下,唯一要做的就是處理中斷。
以下是一個帶有典型空閒迴圈實現的 cpu_Dispatch() 示例(針對 x86-64 CPU,改編自舊程式碼)
void cpu_Dispatch(regs_t *regs)
{
struct Task *task;
struct AROSCPUContext *ctx;
IPTR sse_ctx;
while (!(task = core_Dispatch()))
{
/*
* We enter here every time when there are no ready tasks.
* All accounting (ExecBase state maintenance and IdleCount increment)
* is already done by core_Dispatch()
*/
/* Explicitly enable interrupts and halt the CPU */
__asm__ __volatile__("sti; hlt; cli");
/*
* At this point interrupt(s) have been already processed, but
* the scheduler was not called since interrupted context was
* a supervisor one (see description of core_ExitInterrupt() below).
* Interrupt handlers could possibly call exec.library/Cause() in
* order to queue some software interrupts. In AmigaOS(tm) (and in AROS)
* software interrupts have the lowest priority and are processed after
* all hardware ones. So, we need to pick up queued software interrupts
* here. Software interrupts are described below in details.
*/
if (SysBase->SysFlags & SFF_SoftInt)
core_Cause(INTB_SOFTINT);
/* Repeat everything over and over again until some interrupt wakes up some task */
}
/* We've got a new task, restore CPU context now */
ctx = GetIntETask(task)->iet_Context;
/*
* TODO: This example misses task exception handling.
* More about task exceptions see below.
*/
/* Restore CPU registers */
bcopy(ctx, regs, sizeof(regs_t));
/* Restore FPU and SSE */
sse_ctx = ((IPTR)ctx + sizeof(regs_t) + 15) & ~15;
asm volatile("fxrstor (%0)"::"D"(sse_ctx));
/*
* Jump to the new context. core_LeaveInterrupt() is assemblef function which
* sets stack pointer to the specified context, pops all registers and returns
* from the interrupt.
*/
core_LeaveInterrupt(regs);
/* core_LeaveInterrupt() does not return here */
}
請注意,我們對暫存器幀使用兩種型別:regs_t 和 struct AROSCPUContext。有什麼區別?regs_t 是在中斷進入時儲存在堆疊上的內容。在本示例中,regs_t 只包含 CPU 暫存器。在中斷中,我們通常不會處理 SSE 或 MMX,因此沒有理由在那裡處理它們。但是,struct AROSCPUContext 更完整,它包括 SSE 緩衝區,因為不同的 AROS 任務可能會使用浮點或向量數學。
託管埠也一樣。在那裡,regs_t 將是主機作業系統提供的異常幀,而 struct AROSCPUContext 是我們在 AROS 中儲存的內容。
將來,struct AROSCPUContext 將根據 CPU 系列進行統一。這將使從外部軟體(如偵錯程式)操作 CPU 上下文成為可能。目前它仍然是一個私有定義。
上面描述的三個 API 入口點(KrnSchedule()、KrnSwitch() 和 KrnDispatch())是內部的。唯一相對安全的呼叫函式是 KrnSchedule(),但是它被 exec.library/Reschedule() 包裝成更友好的 exec 形式。KrnSwitch() 和 KrnDispatch() 純粹是內部的,從應用程式內部呼叫它們不是一個好主意。如果呼叫其中一個,當前任務將簡單地丟失,並且不會獲得控制權。
還要注意,exec.library/Reschedule() 可能無法跨 Amiga(tm) 系列的不同系統移植。最初它作為私有函數出現在 AmigaOS(tm) 中。不能保證它會在 MorphOS 和/或 AmigaOS(tm) v4 中保留下來。
簡而言之:這些函式是內部函式,請勿使用它們。作業系統本身擁有良好的任務排程程式,您無需教它如何處理您的任務。當然,在 AROS 特定應用程式中,如果確實要放棄 CPU 時間,您可以呼叫 Reschedule(FindTask(NULL))。但僅此而已。
所描述的排程序列可以執行
- 從軟體中斷(系統呼叫)中明確執行
- 在中斷處理結束時。
(1) 情況很簡單。以下是一個典型的系統呼叫處理程式實現
switch(num)
{
case SC_SCHEDULE:
/* Call scheduler. Simply return if it doesn't want to change anything */
if (!core_Schedule())
break;
/* core_Schedule() returned TRUE, fallthrough here */
case SC_SWITCH:
/* The task is already in some list. Switch to another one no matter what */
cpu_Switch(regs);
/* Fallthrough again, SysBase->ThisTask is invalid here */
case SC_DISPATCH:
/* Select the new task to run */
cpu_Dispatch(regs);
/* Done, so break here */
break;
case SC_CAUSE:
/*
* A software interrupt is requested by exec via KrnCause().
* Call the scheduler no matter what. Explained below.
*/
if (regs->ds != KERNEL_DS)
core_ExitInterrupt(regs);
break;
/* Other syscalls can be handled here if needed */
}
(2) 稍微複雜一些。首先,中斷可以(通常是)巢狀的(優先順序更高的),您應該只調用一次排程程式。其次,您需要確保任務切換實際上是被 exec.library 允許的。
為此,kernel_intr.c 中有一個實用函式 core_ExitInterrupt()。它執行幾乎所有必要的檢查,並且僅在實際允許時才執行排程序列。此外,它還處理待處理的 exec 軟體中斷(如果有)。
該函式僅缺少與 CPU 相關的中斷巢狀檢查。您應該使用任何方法來檢查從該中斷實際上是否正在返回使用者模式。例如,x86-64 核心可以檢查異常幀的 DS 暫存器,以確定它將返回什麼模式。
if (regs->ds != KERNEL_DS)
core_ExitInterrupt(regs);
core_LeaveInterrupt(regs);
exec.library 中的軟體中斷處理由 SysBase 中的 INTB_SOFTINT 向量完成。kernel.resource 負責呼叫它。軟體中斷可能會改變不同任務的狀態,因此在處理完它們之後需要重新排程。這就是為什麼在處理 SC_CAUSE 時我們需要執行完整的排程序列。
exec 軟體中斷與 CPU 軟體中斷(系統呼叫)不同。請明確區分它們,不要被類似的名稱混淆。為了避免以後的混淆,讓我們將這些中斷稱為 SoftInts。排隊一個 SoftInt 意味著只是將一個節點新增到內部列表中。然後在 SysBase->SysFlags 中設定 SFF_SoftInt 位,並呼叫 KrnCause()。
如果在 SoftInt 排隊時中斷被停用,exec.library 可以延遲呼叫 KrnCause()。KrnCause() 僅在中斷啟用時才會被呼叫。
KrnCause() 反過來會在 CPU 上呼叫一個軟體中斷。此中斷應以與實際硬體中斷相同的方式處理。如果合適,需要呼叫 core_ExitInterrupt()。
實際的 SoftInt 處理是在 core_ExitInterrupt() 函式內部完成的,因為 exec 軟體中斷需要在所有實際硬體中斷都處理完之後執行(如上所述,core_ExitInterrupt() 僅在退出所有中斷時呼叫一次)。它檢視 SFF_SoftInt 標誌,如果設定了該標誌,則呼叫 exec 的 SoftInt 向量。其餘部分(包括標誌重置)預計由 exec.library 完成。
請注意,在 cpu_Dispatch() 中的空閒迴圈內,需要對 SFF_SoftInt 標誌進行顯式檢查,並在中斷到達後進行檢查。這是因為空閒迴圈在超級使用者模式下執行,因此在中斷處理完後不會呼叫 core_ExitInterrupt()(根據上面的要求)。
exec.library 中的任務異常提供了一種非同步響應某些訊號的方法。您無需顯式等待異常。當訊號到達時,您的任務將被中斷並跳轉到異常處理程式。處理程式返回後,任務將恢復其正常執行,就像什麼也沒發生一樣。
再次,請不要將任務異常與 CPU 異常混淆。它們是完全不同的東西。exec 中的 CPU 異常稱為陷阱。任務異常在使用者模式下執行,排程和中斷已啟用,就像正常程式碼一樣。
任務異常需要 kernel.resource 端的 cpu_Dispatch() 函式支援。
當 exec 向一個任務丟擲異常時,它會在其 tc_Flags 中設定 TF_EXCEPT 位並導致重新排程。排程程式應該注意到這一點,並將任務定向到一個特殊的例程。當前的排程程式實現將任務設定為 READY 狀態,與其剩餘的時間片無關,並在 cpu_Dispatch() 中執行其餘的處理。
cpu_Dispatch() 應該檢查 TF_EXCEPT 位,如果設定了該位,則執行以下操作
- 將任務的上下文儲存到某處。
- 調整任務的上下文,使其指向異常處理程式例程。
- 跳轉到已調整的上下文。
在此之後,異常處理程式開始起作用。通常它需要呼叫 exec.library/Exception()。此函式會自行完成所有處理。Exception() 返回後,處理程式應該獲取在步驟 (1) 中儲存的原始任務上下文,並跳轉到該上下文。
目前只有 Windows 主機埠正確地實現了任務異常。它的程式碼比較複雜,使用了某些技巧(因為它是在主機上執行的),因此不能作為很好的示例。
kernel.resource 中的排程程式被設計為面向未來且可擴充套件的。為此提供了兩個函式:KrnGetScheduler() 和 KrnSetScheduler()。它們的目的是為不同的任務排程演算法提供支援。這些函式可以從使用者應用程式中安全呼叫。但是請記住,它們會影響整個系統,因此網頁瀏覽器(例如)不是使用它們的合適工具。它們更適合於某些系統維護實用程式(如 AmigaOS(tm) 的 Executive)。
KrnSetScheduler() 改變當前的排程演算法。KrnGetScheduler() 通知當前選擇了什麼演算法。目前這些函式是保留的。KrnSetScheduler() 實際上什麼也不做,而 KrnGetScheduler() 始終返回 SCHED_RR,它是唯一可用的排程程式。
為了實現新的排程演算法,應該修改以下部分
- core_Schedule() - 它是排程程式程式碼的 99%。該函式實際上決定任務將執行多長時間以及何時再次執行。它可以使用任何它想要的東西來做出決定,包括以下資訊通常可從 exec.library 獲取
- SFF_QuantumOver - 當任務的時間片(時間片)結束時,此標誌在 SysBase->SysFlags 中被設定。時間片計數由 exec.library 作為 VBlank 中斷處理的一部分完成。
- TF_EXCEPT - 當任務有待處理的異常時,此標誌由 exec 在任務的 tc_Flags 中被設定。通常的做法是對該標誌設定任務為 READY 狀態,並將實際的異常處理留給 cpu_Dispatch()。但是理論上它可以透過任何其他方式完成。
- 任務在 tc_Node.ln_Pri 中的優先順序。
- core_Dispatch() - 該例程負責從 TaskReady 列表中選取一個新任務,併為它分配時間片。時間片是透過設定 SysBase->Elapsed 的值來分配的。此值實際上是 VBlank 滴答次數,它決定了如果沒有外部事件打擾任務,它將執行多長時間。原始的 RoundRobin 排程程式在其中放置了一個在 SysBase->Quantum 中指定的不變的預設值。事實上,此值是使用者的首選項。
exec.library 不是為了在 m68k 基於的 Amiga(tm) 以外的其他系統上執行而設計的。在保持統一的處理方式的同時,將其適應不同的硬體非常困難。為了克服這些問題,kernel.resource 提供了自己的 API 來處理和控制機器的硬體中斷。
請注意,這裡的“中斷”指的是真正的中斷,CPU 從實際硬體中接收這些中斷。不要將它們與上一章中描述的 exec SoftInts 混淆。為了與以後的明確區分,我們將使用廣泛採用的 IRQ(中斷請求)縮寫。還要明確區分 IRQ 和 CPU 異常。它們在 kernel.resource 中不是一回事,儘管 IRQ 實際上是在 CPU 異常之上實現的,並且與之密切相關。事實上,IRQ 透過中斷控制器對映到一個(或多個)CPU 異常。
託管的 AROS 埠也使用 IRQ 來表示各種外部事件。例如,在 UNIX 主機 AROS 中,IRQ 表示 UNIX 訊號,在 Windows 主機 AROS 中,IRQ 是用於與主機 I/O 執行緒通訊的模擬中斷。
IRQ 由數字指定,從 0 到 255。其分配是特定於機器的。除非您確切知道是什麼硬體生成了它,否則您無法確定特定的 IRQ 的含義。由於這種 API 在此處描述的通常僅對硬體驅動程式有用。
為了利用 IRQ,驅動程式需要兩個 kernel.resource 函式
- KrnAddIRQHandler()
- 此函式安裝 IRQ 處理程式。它採用 IRQ 編號、指向處理程式函式的指標以及兩個任意值,這些值將被傳遞到
- 處理程式。它返回一些不透明的值,實際上是指向描述處理程式的內部結構的指標。不要嘗試戳它
- 以某種方式!在 kernel.resource 的某些實現中,內部資料可能駐留在受保護的記憶體中,因此您只會得到垃圾或崩潰。
- KrnRemIRQHandler()
- 此函式刪除之前安裝的處理程式。它採用 KrnRemIRQHandler() 之前返回的指標。
請注意,每個 IRQ 都可以安裝任意數量的處理程式。安裝新的處理程式不會刪除和/或停用之前的處理程式。處理程式之間沒有任何影響,當 IRQ 到達時,它們會依次呼叫。處理程式沒有優先順序。
在某些情況下,軟體需要控制中斷。為此目的,存在兩個 exec.library 函式:Disable() 和 Enable()。在 AROS 中,這些函式在 kernel.resource 中有它們的低階姐妹:KrnCli() 和 KrnSti()。它們執行相同的操作,除了核心函式直接對 CPU 操作。它們立即執行其操作,並且不會以某種方式修改 exec.library 狀態。因此,它們沒有巢狀計數。
這兩個函式是為了提供 exec.library 的抽象層。不要從使用者應用程式中呼叫它們,它們不會比使用 exec.library 函式有任何優勢。由於它們沒有任何狀態跟蹤,因此它們會很容易地擾亂系統。
中斷處理既依賴於 CPU 又依賴於硬體,並且在不同的機器上有所不同。基本 kernel.resource 程式碼只提供一個函式 - krnRunIRQHandlers()。它接受 IRQ 編號的單個引數,並執行所有已安裝的處理程式。其餘程式碼是特定於機器的,並且為每臺受支援的機器單獨編寫。
根據特定 IRQ 是否使用,您可能希望啟用或停用單個 IRQ(如果硬體提供此功能)。基本程式碼期望您為此實現兩個函式
- ictl_enable_irq(n)
- 啟用 IRQ 編號 n
- ictl_disable_irq(n)
- 停用 IRQ 編號 n
為了實現這兩個函式,您需要在您的 kernel_arch.h 中宣告它們的原型(有關詳細資訊,請參閱 移植)。預設情況下,它們被 #define 為空程式碼,因此它們實際上被省略了。
基本程式碼不期望這些函式有任何返回值。
AROS 需要至少一個週期性計時器中斷才能執行。它用於定期執行任務排程程式並測量系統時間。從 AROS 方面來說,對計時器的頻率沒有任何實際要求。但是許多 Amiga(tm) 應用程式期望 exec.library 以 50 或 60 Hz 的頻率提供 VBlank(顯示垂直消隱)中斷。它們用它來延遲。因此,實現 exec VBlank 中斷是絕對必要的(與其他特定於經典 Amiga(tm) 硬體的中斷不同)。另外,exec.library 在內部使用 VBlank 來測量任務時間片(SysBase->Elapsed 在 VBlank 處理程式中遞減)。
AROS 執行在各種不同的硬體上,並非所有硬體(截至 2010 年 9 月 30 日,實際上沒有)能夠以給定的頻率生成顯示垂直消隱中斷。由於這種 exec VBlank 中斷在 AROS 中被模擬。由於實際的 VBlank 可能來自各種來源,因此預計它將透過呼叫 exec 的 INTB_VBLANK 向量來從外部驅動。kernel.resource 可以使用 core_Cause(INTB_VBLANK) 呼叫來執行此操作。
還有第二個 AROS 元件也需要硬體計時器,它是 timer.device。但是某些機器只有一個硬體計時器,需要在這兩個元件之間共享。這意味著它們中只有一個可以控制計時器。有兩種可能的情況
- 計時器由 kernel.resource 控制。在這種情況下,kernel.resource 將硬體計時器設定為固定頻率,並在每次計時器中斷時呼叫 exec VBlank 向量。計時器頻率由 exec.library 在 SysBase->VBlankFrequency 中指定,預設為 50 Hz,但可以透過核心命令列“vblank=XX”引數覆蓋。Amiga(tm) 軟體預期的兩個常見值是 50(對於 PAL Amiga)和 60(對於 NTSC Amiga)Hz,但實驗使用者可以在這裡嘗試任何其他值。然後,timer.device 可以將自身掛鉤到 VBlank 中斷並使用它來進行時間核算。這種情況通常在開發新埠時實現,此時 timer.device 尚未移植到硬體上。
- 計時器由 timer.device 控制。在這種情況下,它很可能被重新程式設計,並且 kernel.resource 停止對它的頻率有正確的瞭解。在這種情況下,timer.device 負責自行驅動 VBlank 中斷。為了告訴 kernel.resource 計時器已被接管,它應該使用 KrnSetSystemAttr(KATTR_VBlankEnable, FALSE) 呼叫。這將關閉核心的內建 VBlank 模擬。另外,timer.device 應該負責在 SysBase->ex_EClockFrequency 中設定正確的值;這很可能反映硬體的主時鐘頻率。
在某些硬體(如經典的 Amiga(tm) 或 MiniMig 等克隆)上,可以使用真實的影片消隱中斷。在這種情況下,kernel.resource 可能根本不實現 VBlank 模擬,而只是初始化相應的硬體。當然,它仍然應該在相應的 IRQ 到達時負責呼叫 exec VBlank 中斷向量。請記住,exec 仍然執行在 kernel.resource 之上,我們仍然需要驅動它所有的中斷向量!
在 VBlank 中斷之上執行的 timer.device 可能存在低精度。有時中斷被停用,如果它們被停用足夠長的時間,計時器中斷就會丟失。這會導致系統時間下降和變慢。為了減輕這種影響,kernel.resource 可以提供一個高頻週期性計時器。此計時器預計以 VBlank 的整數倍頻率執行。這對系統時間的粒度有積極影響,但對時間的精度可能會有不同的影響(它在很大程度上取決於實際的系統)。
為了提供高頻計時器,核心必須使用正確的 IRQ 編號提供 KATTR_TimerIRQ 屬性。另外,kernel.resource 預計將實際計時器頻率放入 SysBase->ex_EClockFrequency。如果 timer.device 不使用實際的硬體,它將使用此頻率作為主時鐘。
基本 kernel.resource 程式碼在 core_TimerTick() 函式中包含對高頻計時器的支援。您應該在每次計時器 IRQ 時呼叫它。它將負責計數滴答並以正確的週期自行呼叫 exec VBlank 向量。
然後,使用者可以透過“eclock=XX”命令列引數指定所需的 EClock 頻率。此引數在 exec.library/PrepareExecBase() 中處理,並在核心的初始化程式碼中進行微調。
請注意,高頻計時器是純粹的內部事物!它僅用於支援通用的 timer.device 實現。不要在任何第三方元件和/或應用程式中依賴它。在 timer.device 接管並重新程式設計計時器後,無法知道計時器的頻率。也無法確定 timer.device 使用此計時器還是其他計時器。沒有人承諾你 SysBase->ex_EClockFrequency 將包含此計時器的頻率,因為 timer.device 可能會更改此欄位。你只能透過查詢 KATTR_VBlankEnable 屬性的值來查詢核心自身 VBlank 模擬的狀態,但僅此而已。因此,最好遠離它。
kernel.resource 中的 CPU 支援可以被認為是不完整且實驗性的。
目前,kernel.resource 只提供一個函式:KrnIsSuper()。它告訴呼叫者是否以超級使用者模式執行。返回值應該被視為布林值,並與零進行比較。非零表示您當前處於超級使用者模式。
此函式被 exec.library 廣泛用於延遲任務排程程式呼叫。
一些 kernel.resource 實現可能在受保護的記憶體中分配內部資料。目前這包括 IRQ 處理程式節點、異常處理程式節點和 CPU 上下文資料。為了使這能夠正常工作,需要實現以下內部函式
- goSuper() - 將當前 CPU 特權級別設定為超級使用者,並返回一些描述原始特權級別的東西。原始級別儲存在
cpumode_t 型別的變數中。
- goUser() - 將當前 CPU 特權級別設定為使用者
- goBack(mode) - 將當前 CPU 特權級別設定為 goSuper() 返回的值
另外(儘管與之沒有直接關係),需要重新定義 krnAllocMem() 和 krnFreeMem() 以從受保護區域分配記憶體。預設情況下,它們是宏,擴充套件到 exec 的 AllocMem() 和 FreeMem()(請參閱 kernel_memory.h)。這也是在 移植 章節中討論的。
exec.library 的 CPU 異常處理僅針對 m68k CPU 家族。雖然 AROS 仍然支援 exec 陷阱,但可能需要為特定 CPU 異常安裝單獨的處理程式。在 API 端,CPU 異常支援由兩個函式表示
- KrnAddExceptionHandler()
- KrnRemExceptionHandler()
這些函式的工作方式類似於 KrnAddIRQHandler() 和 KrnRemIRQHandler(),但它們對原始 CPU 異常進行操作。異常處理程式會將指向已儲存 CPU 上下文的指標作為第三個引數,並且預計會返回一個值。如果某個處理程式返回非零值,kernel.resource 不會呼叫 exec 陷阱處理程式(預計會停止任務並調出軟體故障請求程式)。任務的執行將繼續。
這些函式對於實現 AROS 偵錯程式非常有用。但是,kernel.resource 的這部分可以被認為是不完整且正在開發中。這些函式的規範可能會隨時更改。此外,CPU 上下文結構應在每個 CPU 家族中統一併公開,以便使這些函式真正有用。
在實現方面,基礎程式碼提供了單個 krnRunExceptionHandlers() 函式。類似於 krnRunIRQHandlers(),它將為指定的異常執行已安裝的處理程式,但將返回一個值。如果所有處理程式都返回零,則它將為零。
Exec 陷阱實際上是模擬的。特定於 CPU 的程式碼負責將本機 CPU 異常號轉換為 exec 陷阱號,並呼叫 SysBase->ThisTask->tc_TrapCode。相同的規則適用於託管 AROS,唯一的例外是 CPU 異常實際上也是模擬的,它們是從主機作業系統提供的資訊中重建的。這會導致一個副作用,即並非所有 CPU 異常都可以被捕獲並正確處理。由於 kernel.resource 中異常處理的不完整性,託管 AROS 埠上的異常處理程式碼是最具實驗性和未完成的部分之一。
CPU 上下文的實際格式因使用的 CPU 而異,即使是同一家族的 CPU 也是如此。例如,32 位 x86 CPU 可能會使用不同的上下文格式,具體取決於可用的擴充套件暫存器集(FPU、SSE 等)。託管埠也可能會將主機特定資訊儲存為上下文的一部分(例如主機作業系統的 errno 值)。
為了幫助 exec.library 處理這些差異,kernel.resource 提供了兩個 API 函式
- KrnCreateContext()
- 分配並初始化 CPU 上下文儲存區
- KrnDeleteContext()
- 釋放 CPU 上下文儲存區
這兩個函式由 exec.library 在建立和刪除任務時使用。通常不需要從使用者應用程式呼叫它們。
如果使用記憶體保護,這些函式將在受保護區域分配記憶體。只有在具有超級使用者許可權的情況下才允許訪問此區域。請將上下文儲存區視為私有,特別是考慮到上下文結構目前也是私有的。當此問題解決後,文件將更新。
總而言之,kernel.resource 中的 CPU 管理 API 可以被認為是不完整的。以下是可以被認為是缺失的功能列表
- CPU 上下文修改
- 可能的解決方案:在各種 CPU 家族中統一 CPU 上下文格式。可能引入 KrnGetTaskContext() 和 KrnSetTaskContext() 等函式,以便從使用者應用程式(如偵錯程式)訪問 CPU 上下文。
- CPU 異常處理
- 審查異常處理程式機制。可能處理程式應該被優先順序化。可能它們應該以某種方式瞭解彼此。
- CPU 許可權級別切換
- exec.library 為此目的提供了三個呼叫:Supervisor()、SuperState() 和 UserState()。也許它們應該以某種方式在 kernel.resource 公開 API 的基礎上執行。當前核心的實現基於原始 PPC 本機程式碼,可能不適合其他 CPU。
- CPU 快取操作
- 清除 CPU 快取的方式取決於使用的 CPU 模型。在 DMA 前後需要採取的措施取決於 CPU 和主機板硬體。也許 kernel.resource 應該在 exec.library 呼叫應該執行的一些快取控制 API 的基礎上。
此列表不代表任何最終決定,甚至不代表任何最終意見。這些主題是開放討論的。
kernel.resource 的這部分正在積極開發中。文件將隨後提供。
這部分代表了用於低階系統除錯的各種支援函式。相關函式可以分為兩個子組
KrnPutChar() 和 KrnMayGetChar() 與 exec.library 中的對應函式 RawPutChar() 和 RawMayGetChar() 執行相同的操作。它們只是為了提供 exec.library 的硬體抽象。第三個函式 KrnBug() 主要是在滿足核心自身需求時新增的。它接受的格式字串符合 C 標準(不符合 RawDoFmt() 標準)。KrnBug() 有一個特殊之處:它應該能夠與 NULL KernelBase 一起工作。這是因為它可以從核心啟動程式碼中呼叫,而此時還沒有設定任何 KernelBase。
除錯輸出的預設實現最終會進入 krnPutC(),它在基礎程式碼中是空的。在您的核心實現中,您需要用某個低階函式替換它,該函式將給定字元輸出到某個可靠的位置,例如序列埠。此函式應該儘可能健壯:它不應依賴任何庫,它應該能夠在中斷中工作,並且如前所述,它應該即使在沒有 KernelBase 的情況下也能工作。這是 AROS 程式碼中為數不多的允許使用忙等待迴圈的地方之一,因為您沒有其他功能機制。即使在系統崩潰狀態下,此函式也會被呼叫,以輸出警報資訊。
KrnMayGetChar() 也有類似的要求,只是它不需要使用 NULL KernelBase。
託管 AROS 可能會選擇將 KrnBug() 重新路由到主機 vprintf(),以簡化操作。
這是 AROS 最有趣和最具創新性的部分。這個 API 允許應用程式獲取有關程式在記憶體中的佈局資訊。
- KrnRegisterModule()
- 當新的可執行檔案被載入到記憶體中時,必須呼叫此函式。通常,它由 dos.library/InternalLoadSeg() 呼叫,因此您不必擔心它。但是,如果您正在編寫將程式碼直接儲存到記憶體中的編譯器,您可能希望為剛剛構建的模組呼叫此函式。
- 目前,只有 ELF 檔案被 AROS 完全支援,因此只支援 DEBUG_ELF 型別的資訊。但是,預計在某個時候,將實現對在 AmigaDOS hunk 檔案中找到的除錯資訊的 supports。.
- KrnUnregisterModule()
- 必須在可執行模組從記憶體中解除安裝之前呼叫此函式。同樣,這通常由 dos.library/UnLoadSeg() 完成。
- KrnDecodeLocation()
- 這是最主要的函式。它允許呼叫者在已註冊模組列表中查詢地址。目前,提供以下資訊
- 模組名稱
- 段號、名稱、起始地址和結束地址
- 符號(函式)名稱、起始地址和結束地址
- 此列表不是最終的。將來可能會支援更多屬性(例如,可能能夠查詢原始檔名和行號)。
- KrnDecodeLocation() 目前由內建的 exec.library 崩潰處理程式用於顯示崩潰位置資料。
這是最後一組公共核心函式,目前包含三個條目
- KrnGetBootInfo()
- 此函式返回指向載入程式提供給 kickstart 的標記列表的指標。此列表的標記在 aros/kernel.h 中定義
- KRN_KernelBase
- 待記錄
- KRN_KernelLowest
- 記憶體中載入的 kickstart 映像的起始地址
- KRN_KernelHighest
- 記憶體中載入的 kickstart 映像的結束地址
- KRN_KernelBss
- 指向 BSS 段描述符陣列的指標(struct KernelBSS)。該陣列以 addr 欄位值為 NULL 的條目結束。kickstart 執行熱重啟時,需要清除所有記錄的 BSS 段。__clear_bss() 實用函式可以用於此目的。
- KRN_GDT
- 待記錄
- KRN_IDT
- 待記錄
- KRN_PL4
- 待記錄
- KRN_VBEModeInfo
- 指向 VESA 影片模式資訊結構的指標(在 aros/multiboot.h 中描述的 struct vbe_mode),描述當前影片模式。VESA 顯示驅動程式需要此資訊。
- KRN_VBEControllerInfo
- 指向 VESA 影片顯示控制器資訊結構的指標(struct vbe_controller)。此標記與 KRN_VBEModeInfo 配對。
- KRN_MMAPAddress
- 指向機器記憶體對映的指標,採用 Multiboot 格式(struct mb_mmap)。
- KRN_MMAPLength
- 提供的 multiboot 記憶體對映的長度。
- KRN_CmdLine
- 指向核心命令列引數的以零結尾的字串的指標。
- KRN_ProtAreaStart
- 待記錄
- KRN_ProtAreaEnd
- 待記錄
- KRN_VBEMode
- 如果載入程式執行了模式切換,則指定實際的 VESA 影片模式號。如果指定了此標記,則其值將覆蓋由 KRN_VBEControllerInfo 提供的 struct vbe_controller 中的 vbe_mode 欄位。
- 此標記可能是多餘的,並且可能會被刪除。如果您正在編寫具有 VESA 支援的自己的載入程式,請考慮設定 vbe_mode 為有效值,而不是使用此標記。
- KRN_VBEPaletteWidth
- VESA 調色盤的寬度(以位為單位)(6 或 8)。對於直接顏色影片模式(24 位和 32 位),可能不存在。
- KRN_MEMLower
- 待記錄
- KRN_MEMUpper
- 待記錄
- KRN_OpenFirmwareTree
- 指向扁平化的 OpenFirmware 裝置樹的指標。資料格式文件需要新增。
- KRN_HostInterface
- 指向託管 AROS 的主機作業系統介面結構體的指標。此結構體指定了 AROS 與主機作業系統通訊所需的回撥函式(例如,連結到其共享庫)。該結構體本身被認為是私有的且特定於埠的,它僅在載入程式、核心和 hostlib.resource 之間共享。
- KRN_DebugInfo
- 指向單鏈表的指標,該連結串列包含啟動程式除錯資料結構(struct debug_seginfo 或 dbg_seg_t)。這些結構體描述了啟動程式模組在記憶體中的佈局,並由 KrnDecodeLocation() 函式使用以提供資料。它們被認為是持久且只讀的,並且在熱重啟期間保持不變。
- 啟動程式除錯資訊是可選的,省略它可以節省記憶體,但在這種情況下,KrnDecodeLocation() 將無法提供有關啟動程式內部發生崩潰的詳細資訊。
- KRN_BootLoader
- 指向描述主載入程式本身的 ASCII 字串的指標。用於提供資訊。
- 允許讀取和檢查此標籤列表的內容,但是其中一些資訊可以透過 bootloader.resource 以更抽象和更合適的形式訪問。建議儘可能使用 bootloader.resource。
- 另外請注意,在某些架構(如經典 Amiga(tm))上,核心可以直接啟動,無需外部載入程式的幫助。在這種情況下,KrnGetBootInfo() 可能會返回 NULL。
- KrnGetSystemAttr()
- 此函式提供有關 AROS 執行的系統的一些資訊。此資訊大多是內部的,之前已經討論過。但是,有一個 KATTR_Architecture 屬性在各種地方可能很有用。它的值是形式為“系統-cpu”的 ASCII 字串,例如“pc-x86_64”或“mingw32-i386”。它在各種系統資訊實用程式中以及在依賴於特定架構的硬體驅動程式中可能很有用(尤其是託管驅動程式,它們高度依賴於它們為其編譯的主機,儘管它們是基於磁碟的)。例如,為 Linux 編譯的 tap.device 與為 FreeBSD 編譯的 tap.device 在二進位制上不相容(因為底層系統在二進位制上不相容),建議在這樣的元件中檢查系統的架構以避免混淆的崩潰。
- KrnSetSystemAttr()
- 此函式提供 KrnGetSystemAttr() 的反向功能,允許更改某些屬性的值。目前,只有 KATTR_VBlankEnable 可設定,它已在 系統定時器處理和 VBlank 模擬 章中詳細討論。
內部實用程式函式
[edit | edit source]這些函式沒有在 kernel.resource API 中體現。它們是基礎程式碼提供的內部函式,用於提供程式碼重用並幫助將 AROS 移植到其他架構。
記憶體管理
[edit | edit source]kernel.resource 程式碼提供兩個用於分配和釋放記憶體的函式,這些函式只能由 kernel.resource 使用
- krnAllocMem(length)
- krnFreeMem(address)
這些函式在可替換的 kernel_memory.h 標頭檔案中描述。預設情況下,它們是包裝到 exec AllocMem() 和 FreeMem() 的宏。但是,如果您正在實現記憶體保護,您可以重新實現這些函式以從受保護區域分配記憶體。請記住,CPU 上下文也將由 KrnCreateContext() 函式儲存在那裡。
系統啟動支援
[edit | edit source]這些函式在系統早期啟動期間提供有用的幫助。
- krnRomTagScanner()(在 kernel_romtags.h 中宣告)
- 掃描給定的地址範圍以查詢有效的 ROMTags 並構建已發現的 ROMTags 列表。返回值需要放置在 SysBase->ResModules 中。請注意,此函式本身依賴於有效的 AllocMem() 和 FreeMem(),這意味著它需要在 PrepareExecBase() 之後呼叫。
- __clear_bss()(在 kernel_base.h 中宣告)
- 清除提供的描述符陣列中列出的 BSS 段。實際上,這應該是您啟動核心時要做的第一件事。請記住,全域性變數可能儲存在 BSS 部分本身,因此只有在您呼叫此函式之後才能儲存它們的值!通常,您會在初始載入程式標籤列表解析期間呼叫此函式。
- PrepareExecBase()
- 實際上,這是 exec.library 函式,但它與 kernel.resource 關係密切,因此非常適合放在這裡。此函式獲取第一個 MemHeader 的地址,並在其中構建 SysBase。但是,您需要設定 MemHeader 本身。完全由您來發現可用記憶體。
- 此函式是目前從 exec.library 靜態連結的唯一函式。將來這可能會改變,允許 kernel.resource 和 exec.library 完全分離,在這種情況下,規範將更新。
- 此函式需要手動宣告
extern struct ExecBase *PrepareExecBase(struct MemHeader *, char *, struct HostInterface *);
- 第二個和第三個引數是指向核心命令列(透過 KRN_CmdLine 標籤傳遞)和 HostInterface 結構體的指標。第三個引數是可選的,它是否需要由 exec.library 中的特定於架構的程式碼確定。在某些埠(如 Windows 託管)上,exec.library 在此早期階段需要訪問主機作業系統。