Aros/開發者/文件/庫/Exec
此庫管理所有其他庫、裝置、資源、中斷、應用程式和計算機的記憶體。要檢視關係。關於此主題的另一種觀點。
作業系統的核心。它提供搶佔式多工處理、內建的連結串列基元、訊息傳遞基元、IO 基元等,並在整個系統中被大量重用。如果您理解 exec,您將理解系統其餘部分的底層內部機制。
實際上,AmigaOS 的大部分以及 AROS 都是基於廣泛使用訊息傳遞構建的,訊息傳遞使用訊號來指示何時有新訊息可用,並使用訊號量來安全訪問某些資料結構。
這就是 Amiga 如何處理可移植程式碼問題的方法 - 所有程式碼都是使用指向庫“基礎”的指標的偏移量編寫的,除了地址 4 之外,沒有固定地址。每個庫都有一個呼叫表,每個條目 6 個位元組(一個跳轉指令),然後跳轉到正確的入口點。編寫自己的庫很容易,前提是您擁有“標頭”的正確格式。裝置驅動程式“借用”了庫的結構,並使用相同的概念來提供預設的 I/O 處理程式。
結構的打包。對於 OS3.x 庫的 API 定義的結構,MOS 和 OS4 讓這些結構以 m68k 相容的方式打包(例如,大多數情況下在兩個位元組邊界),而其他結構以 PPC 原生方式打包(4 位元組用於 4 位元組長的資料等)。我們甚至有一個解決方案透過使用 #include 來實現它,以便在 AROS 包含檔案中啟用此打包
#include <aros/compat_packing_on.h> /* Definition of structs with compat packing */ #include <aros/compat_packing_off.h>
這些 aros/compat_packing_on.h 和 aros/compat_packing_off.h 的內容可以取決於架構和編譯器。
另一個要修復的是 LVO 表的設定方式以及用於偽造庫函式的 m68k 條目的存根程式碼。
總而言之,ppc 架構會對所有結構使用原生打包,併為 LVO 使用指標表,而無需特殊的存根程式碼。ppcmos 會對需要特殊打包的結構應用特殊打包,併為共享庫提供 MOS LVO 表約定。
將 SysBase 移出 4L - 這將另外提供一個完整的除錯資訊,用於在崩潰回溯中顯示 KS 函式名稱。
為了使 AROS 的 kickstart 的部分可移植,因為我們的 KS 是模組化的。似乎有三個部分
- BSP - 板級支援包。核心、exec、硬體驅動程式。
- Strap - 這部分只在託管和原生之間有所不同。它包含 bootloader.resource 和 dosboot.resource。在原生上,它還包括駐留檔案系統(AFS、FFS、CDVSFS)。
- Base. 核心模組(圖形、實用程式、直覺、dos 等)。這部分被認為是完全機器無關的。
目前,所有託管 kickstart 和 x86-64 原生 kickstart 都遵循此方案。它提供更舒適的程式碼維護。如果您想新增一些新的機器無關模組(上次是 filesystem.resource 例如),您只需在一個地方新增它,所有埠都會收到它。當您在一個埠上測試某樣東西時,您可以 100% 確定任何其他埠的行為方式都相同。
其他埠(i386、m68k 和 ppc 原生)將遵循。目前,m68k-amiga 違反了此規範,因為它在 graphics.library 中包含特定於硬體的程式碼。
- 有關更多資訊,請參見此處
當應用程式請求記憶體時,它可以自己分配一小部分記憶體,或者要求 Exec 找到滿足其要求的合適記憶體區域。
#include <proto/exec.h> APTR AllocMem( ULONG size, ULONG flags ); void FreeMem( APTR memory, ULONG size ); /* return no status */ APTR AllocVec( ULONG size, ULONG flags ); /* remembers memory set better */ void FreeVec( APTR memory ); /* return no status */
flags MEMF_TOTAL - All available memory . MEMF_CLEAR - The allocated memory area is initialized with zeros. MEMF_LOCAL - Get memory that will not be flushed, if the computer is reset. MEMF_CHIP - Get memory that is accessible by graphics and sound chip. Required for some functions. MEMF_FAST - Get memory that is not accessible by graphics and sound chips. You should normally not set. MEMF_PUBLIC - This flag must be set, if the memory you allocate is to be accessible by other tasks. MEMF_REVERSE - If this flag is set, the order of the search for empty memory blocks is reversed. MEMF_NO_EXPUNGE - If not enough free memory is found, AROS tries to free unused memory.
Exec 提供 AllocEntry() 和 FreeEntry() 例程,以透過 MemList 在一次呼叫中分配多個記憶體塊。
struct MemList {
struct Node ml_Node;
UWORD ml_NumEntries; /* number of entries in this struct */
struct MemEntry ml_ME[1]; /* the first entry */
};
struct MemEntry {
union {
ULONG meu_Reqs; /* the AllocMem requirements */
APTR meu_Addr; /* the address of this memory region */
} me_Un;
ULONG me_Length; /* the length of this memory region */
};
使用這些函式分配的記憶體必須在使用後釋放。
KickMemPtr/KickTagPtr/KickCheckSum 東西(復位證明駐留程式)現已實現並提交。忘了提,目前它會立即 InitResident() 它們。它們應該被注入 ResModules 列表(也按正確的順序)中,這並非易事,因為 KickTags 只能在自動配置裝置配置後才能處理(SysBase 或 KickMemTags 可以位於自動配置快速 RAM 中),而 ResModules 列表是一個非常煩人和愚蠢的列表。
實際上,沒有 Exec 函式應該填寫 Dos/IoErr()。OpenLibrary()(當被 LdDaemon 覆蓋時)是一個明顯的例外。AllocMem() 不會被 DOS 打補丁,也不會設定任何 DOS 錯誤程式碼。只有那些被 DOS 打補丁的 exec 函式可能會更改 IoErr(),但這只是一個副作用,這不是獲取錯誤程式碼的受支援方法。
新的記憶體系統似乎將 struct MemHeader 中的 mh_Upper 欄位視為記憶體區域內最頂層的有效地址,而舊的實現和 AOS 將其視為最後一個有效地址的高一個位元組。
我們針對 AllocMem() 的 AutoDoc 指出返回的記憶體與 sizeof(struct MemChunk) 對齊,但 AmigaOS 3.x AutoDoc 只保證“返回的記憶體塊是長字對齊的”。AmigaOS 3.x 確實返回 MemChunk 對齊的塊,但我們是否在不經意間改變了 API?MemChunk 對齊是 LONG 對齊的超集(所有 MemChunk 對齊的塊也是 LONG 對齊的),因此這不是問題。只有當 MemChunk 對齊不是長字對齊時才會有問題。MemChunk 是 IPTR 的倍數。以前有一組奇怪的宏,但它們現在已被簡化。因此,它是 AROS_WORSTALIGN 或 sizeof(struct MemChunk)(取決於哪個更大)。struct MemChunk 故意是一個 APTR 和一個 IPTR,所以它也是 sizeof(IPTR)。請參閱 exec\memory.h 中的 MEMCHUNK_TOTAL 定義。
如果有人想將 AROS 軟體移植到其他作業系統,而該作業系統只有 LONG 對齊的記憶體塊,那麼如果被移植的軟體依賴於 MemChunk 對齊(應用程式程式設計師不應該需要知道 MemChunk 是什麼),則可能會有問題。程式設計師有責任使其程式碼與它想要執行的作業系統相容。我們可以將以下類似的短語新增到 AllocMem() 的自動文件中:'為了與其他類似 Amiga 的作業系統相容,程式設計師應該只依賴於 LONG 對齊的記憶體塊,如原始 AmigaOS 3.x 中所述。'
struct MemChunk {
struct MemChunk *mc_Next; /* pointer to next chunk */
ULONG mc_Bytes; /* chunk byte size */
};
struct MemHeader {
struct Node mh_Node;
UWORD mh_Attributes; /* characteristics of this region */
struct MemChunk *mh_First; /* first free region */
APTR mh_Lower; /* lower memory bound */
APTR mh_Upper; /* upper memory bound+1 */
ULONG mh_Free; /* total number of free bytes */
};
MEMF_CHIP 指的是 (amiga) CHIP 集所需的記憶體。我認為使用它來表示圖形卡晶片集所需的記憶體沒有區別。圖形記憶體根本不是系統記憶體,例如,在大多數情況下,你無法從那裡執行程式碼。
許多程式在為一些平面圖形資料分配緩衝區時會提供 MEMF_CHIP 標誌。如果我們談論的是系統友好的軟體,那麼這些資料就會被饋送到圖形/直覺。甚至我們的 graphics.library 在 AllocBitMap() 內部也使用 CHIP 分配,以保持與 Amiga 硬體的相容性。因此,如果我們不宣告任何 RAM 為 CHIP(因此將 CHIP 作為 VRAM 指示器),那麼我們就無法進行這些分配。一個替代方案是:我們將所有 RAM 宣告為 CHIP,在這種情況下,我們沒有問題與 AllocMem()s 的平面圖形。在這種情況下,CHIP 標誌純粹是為了向後相容。是的,需要以其他方式查詢 GFX 卡記憶體。
在分配記憶體時,exec 函式不會檢查所請求的位元組大小是否已繞回 (ULONG)。如果有人出於某種未知原因嘗試分配太多記憶體,並且位元組大小在內部增長到超過 2^32,就會發生這種情況。分配不會失敗,使用者可能會完成分配,但位元組大小會混亂。不過,這可能不是一個現實世界中的場景,也不是一個真正的問題......
我還想知道是否有人正在擴充套件記憶體分配例程。需要一些可以分配對齊的記憶體並將記憶體與一些邊界相匹配的東西。聽起來你需要一個 slab 分配器 - 可以輕鬆地將其編寫為圍繞 AllocMem() 的包裝器。
a slab is a large chunk of memory, split into equal size blocks, and memory usage is mapped by a bitmap.
AmigaOS 4.x 有一個用於此的介面 - 我們可能想要實現一個 API 相容的版本。
slab 分配器的主要優點是速度,但它們也會增加相當多的額外記憶體開銷。因此,如果你認為你將解決記憶體不足問題,我懷疑你會感到驚訝。
不僅如此。slab 分配器如果用於系統物件,則效果很好,但不能真正用於任何其他用途。根據定義,它們旨在簡化和加快相同大小物件的分配。
Linux(核心)中使用的夥伴分配器非常快 - 但也浪費了很多記憶體(我認為它甚至比 slab 分配器更糟糕)。這並不重要,因為核心沒有使用大量的記憶體,並且它沒有用作通用分配器。
嗯,夥伴分配器只是用來獲取原始記憶體(MMU 頁面),然後將其進一步分配到地址空間。一個很大的好處是,夥伴分配器總是給你與分配大小邊界對齊的記憶體。缺點是,大小是二的冪(從 MMU 頁面 - 4K 開始,是分配的最小數量)。
1. Scales nicely from few MB of managed memory up to huge amounts - I've successfully tested it with 12GB AFAIR. 2. The allocation time, including all locking, takes about 250 cpu cycles - less than 0.1 microsecond on 2.5 GHz cpu. 3. Freeing memory is slightly faster - 0.09 microsecond 4. Memory overhead for buddy itself: 0.4%
夥伴分配器本身並不需要浪費太多記憶體,但它相當慢。最後一個在 4GB 機器上浪費了 16MB 的 RAM......然而,減少它會使整個夥伴分配器非常慢。
以下是分配及其對齊方式和邊界的排序列表,為此我需要更好的東西
Max Size Boundary Alignment
Device Context Base Address Array 2048 PAGESIZE 64
Device Context 2048 PAGESIZE 64
Input Control Context 64 PAGESIZE 64
Slot Context 64 PAGESIZE 32
Endpoint Context 64 PAGESIZE 32
Stream Context 16 PAGESIZE 16
Stream Array (Linear) 1M None 16
Stream Array (Pri/Sec) 4Kb PAGESIZE 16
Transfer Ring segments 64K 64KB 16
Command Ring segments 64K 64KB 64
Event Ring segments 64K 64KB 64
Event Ring Segment Table 512K None 64
Scratchpad Buffer Array 248 PAGESIZE 64
Scratchpad Buffers PAGESIZE PAGESIZE Page
我們所有的驅動程式都需要自己對齊記憶體(如果需要),這可能會導致混亂。頁面大小可以由使用者定義,不一定與 cpu/系統相同,還是在某個地方可用?
驅動程式自行對齊記憶體會導致比需要更多的記憶體使用量。如果我要在 64kb 邊界上分配 64kb 的記憶體,並且對齊方式為 16 位元組,那麼我實際上必須分配至少 128kb。
記憶體碎片是 AROS 的一個真正問題嗎?如果不是,我很樂意使用類似 AllocVecAligned(bytesize, alignment, boundary) 的東西(從 PCI 裝置可訪問的記憶體中分配),而不是作業系統竊取它可能永遠不會使用的記憶體。對齊的記憶體分配主要用於驅動程式程式碼,這些程式碼傾向於保留它們的記憶體並且從未釋放它們(直到軟啟動),但我不確定......
記憶體碎片在 AROS 中是一個非常大的問題 - 在記憶體有限的系統上。它只是被大多數 AROS 埠都在具有大量記憶體的系統上執行的事實所掩蓋。
http://www.morphos-team.net/tlsf (但以前也據我所知在 Aminet 或其他地方可用)
void *allocaligned(ULONG size, ULONG flags, ULONG align)
{
void *ptr = AllocMem(size + align - 1, flags & ~MEMF_CLEAR);
if (ptr)
{
ULONG alignptr = ((ULONG) ptr + align - 1) & (ULONG) -align;
Forbid();
FreeMem(ptr, size + align - 1);
ptr = AllocAbs(size, (APTR) alignptr);
Permit();
if (ptr && (flags & MEMF_CLEAR))
memset(ptr, 0, size);
}
return ptr;
}
恕我直言,64kb 邊界具有明確的 16 位元組對齊......邊界只意味著分配的記憶體不能跨越某個區域,並不意味著記憶體需要與邊界的尺寸對齊。
硬體使用這些東西,因為它們不需要更新完整的記憶體地址暫存器,但在 64Kb 邊界的情況下,它只需要 16 位,因此它偏移的記憶體不能跨越該邊界。
在我的情況下,有一個“事件環段表”,其中包含 64 位地址(位 0-5 保留且未使用 = 64 位元組對齊),指向“事件環段”,現在這些段不能跨越 64Kb 邊界,儘管給出了地址的較低 16 位(減去位 0-5)。邊界不是對齊。
不要部分地釋放記憶體,因為這會“破壞/使無用”圍繞原始分配的 mungwall,即使它不會,它仍然是作業系統的工作來提供合理的操作來乾淨地分配東西。
更願意讓所有這些都透過一個將標籤作為輸入的 allocmem 來處理......(已經有了 MEMF_HWALIGNED)......嗯?為什麼?!為什麼不選擇 AllocMemTags 呢?我不太喜歡這種迴圈依賴關係,即 exec.library 依賴於 utility.library,而 utility.library 依賴於 exec.library......
#define MEMF_TAGGED (1L << 21) /* Additional allocation tags */ APTR AllocMem(IPTR size, flags, ....)
buff = AllocMem(680, MEMF_CLEAR | MEMF_ANY);
IPTR hw_addr;
hw_buff = AllocMem(1024*4, MEMF_CLEAR | MEMF_TAGGED,
MEMT_Alignment, 1024,
MEMT_BusDevice, pcidev,
MEMT_BusDMAAddress, &hw_addr,
TAG_END);
它不需要在邊界上分配也不需要瀏覽記憶體列表。“prefix”是整個分配過程中必須相同的位,等同於邊界。“valid_bits”組合了地址大小和對齊方式。
APTR AllocMemAligned(IPTR size, ULONG flags, UQUAD prefix, UQUAD valid_bits)
{
APTR mem;
/* Check if a full 64-bit pointer is allowed */
if((valid_bits & 0x8000000000000000LL) == 0)
{
flags |= MEMF_31BIT;
valid_bits &= 0x7fffffffLL;
}
/* Allocate a block large enough to definitely include a suitable
sub-block */
mem = AllocMem((size << 1) + ~valid_bits, flags);
/* Shift starting position of sub-block to fit alignment etc.;
allocate sub-block and give back the rest */
if(mem != NULL)
{
Forbid();
FreeVec(mem);
if((mem & ~valid_bits) != 0)
mem = (mem & valid_bits | ~valid_bits) + 1;
if((mem & prefix) != (mem + size & prefix))
mem = mem + size & prefix;
mem = AllocAbs(size, mem);
Permit();
}
return mem;
}
某種私有的 AllocVecAligned 函式已經編寫,但恕我直言,驅動程式程式碼自己去瀏覽記憶體列表是一件壞事,但在目前這是唯一可行的選擇(嘗試在邊界上分配會導致所有適合邊界的記憶體區域可能會在記憶體非常有限的系統上用盡)?使用 #?Vec#? 函式而不是 #?Mem#? 的問題在於,必須儲存大小可能會破壞對齊方式,從而浪費空間。就像 AllocVec() 一樣,但恕我直言,#?Mem#? 函式對於分配 DMA 塊等並不麻煩,這些塊通常是固定大小的。
Nouveau 驅動程式將新增 gfx 卡記憶體到記憶體列表中,並且該 (gfx 卡記憶體) 區域在我的 PCI(-e) 裝置不可訪問,必須跳過,只使用真正的系統記憶體。
alignOffset 的原因是什麼?它用於你需要在記憶體中儲存對齊的東西,但還需要在對齊區域開始之前儲存附加資料的情況。
APTR AllocVecAligned(ULONG bytesize, ULONG requirements, ULONG alignment, ULONG boundary) {
D(bug("AllocVecAligned(%ld, %lx, %lx, %lx)\n", bytesize, requirements, alignment, boundary));
UBYTE *ret;
ULONG bytesize_adjusted;
struct MemHeader *mh;
struct MemChunk *mc=NULL, *p1, *p2;
if( ((!bytesize) || (bytesize>boundary)) )
return NULL;
/* Add room for stored size */
bytesize_adjusted = bytesize + AROS_ALIGN(sizeof(ULONG));
/* Round to a multiple of MEMCHUNK_TOTAL */
bytesize_adjusted = AROS_ROUNDUP2(bytesize_adjusted, MEMCHUNK_TOTAL);
/* Be really anal about the size */
if(bytesize_adjusted<bytesize)
return NULL;
Forbid();
我的對齊首選擬合分配將瀏覽滿足要求的記憶體列表,然後它將檢查塊是否包含足夠的空間來存放 bytesize_adjusted。如果是,則它將向 ptr 新增 sizeof(ULONG) 並將 ptr 對齊到對齊值,並檢查原始位元組大小是否不會跨越邊界。
為了使這工作,需要對 Bytesize_adjusted 進行一些調整。據我所知,所有分配都在 64 位元組邊界上,這意味著為了使新的分配不破壞對齊方式,分配必須在該對齊方式上開始和結束。然後開銷將是 128 位元組,這仍然比不使用 AllocAbs() 手動對齊要好。
對於首選擬合分配方案,對齊分配很慢。
改變分配方案是我的能力範圍之外,而且無論如何,如果真的要發生,它需要某種共識。TLSF 型別的分配方案的主要好處(如果我正確理解這個概念)是不需要搜尋具有足夠空間的塊,因為記憶體列表對不同的空閒空間塊有不同的列表,因此它返回最適合分配的塊:這會導致分配後返回非常小的空閒塊(如果分配的大小小於塊大小),而不是首選擬合,它從第一個合適的塊中獲取記憶體,剩餘部分將被釋放。在我的情況下,第一個塊是系統記憶體的剩餘部分,因為我的驅動程式非常早載入,並且沒有釋放記憶體,它的尺寸幾乎是我的 Aros 盒子記憶體 (MEMF_FAST) 的完整 2Gb。
分配的速度有點用處,為什麼有人會在做一些需要速度的事情時分配記憶體?可以使用記憶體池來降低碎片級別,但它們不適合對齊的分配。分配速度確實非常重要。想想系統啟動和其他所有情況,在這些情況下,會執行數千甚至數百萬次記憶體分配。速度在那兒確實很重要。
MorphOS alignOffset 在我看來仍然是有點無用的,為什麼不在最後或其他地方分配額外的記憶體?因為你可能希望硬體結構是軟體結構中的一個欄位。例如,在 Poseidon 的 OHCI 實現中,軟體 ED 和 TD 結構以僅軟體的欄位開頭,後面跟著硬體結構。只有硬體結構需要對齊。在我正在開發的分離式 OHCI 驅動程式中,這些軟體結構現在以 struct MinNodes 開頭,因此它們必須位於硬體結構之前,以便在 struct Lists 中輕鬆放置(除非使用笨拙的解決方法)。
MorphOS 對齊分配也缺乏邊界要求,而這對於硬體驅動程式程式碼來說是必須的。但是,這個限制可能很容易解決。例如,如果對齊方式是 128 位元組,邊界是 1024 位元組,你可以將請求的大小向上舍入到大於或等於對齊方式的下一個二的冪,並將其用作請求的對齊方式。因此,如果所需的大小向上舍入到 512,則請求 512 位元組的對齊方式,並且不會跨越邊界。
需要對齊分配的 AROS 驅動程式包括:HDAudio、USB 驅動程式、AHCI 等等。它們都需要對齊分配,一些乙太網驅動程式也可能需要,現在對齊功能已在它們的程式碼中實現。一個對齊分配函式很有用。
據我所知,所有圖形記憶體管理目前都是使用 Allocate() 在自己的私有 MemHeader 上實現的,儲存在 VRAM 中。好吧,Allocate() 使用的是慢速演算法。此外,如果 Nouveau 依賴於自己的分配器呢?在這種情況下 - 如果我們引入一個實現記憶體池處理程式的能力,例如作為類呢?一個這樣的類將是 exec 的池管理器。在這種情況下,AllocPooled()/FreePooled() 可以用來在 VRAM 中執行分配。例如,如果 Nouveau 使用自己的分配器,它會將其公開為一個類加上物件指標(池本身)。這樣池函式就可以使用它。
庫由四個部分組成:庫節點、函式(向量)表、函式集和與庫相關的全域性資料。
struct Library {
struct Node lib_Node;
UBYTE lib_Flags;
UBYTE lib_pad;
UWORD lib_NegSize;
UWORD lib_PosSize;
UWORD lib_Version;
UWORD lib_Revision;
APTR lib_IdString;
ULONG lib_Sum;
UWORD lib_OpenCnt;
};
如果沒有正確的 AROS_LH 樣式暫存器引數,當前庫函式將假定基於堆疊的變數。新增 .conf 和 AROS_LH()。一些庫使用 SDI 包含以實現可移植性。
談論庫基礎中的私有 AROS 結構。(沒有人關心一些額外的 LVO,它只有 6 個位元組/LVO)。如果它們是 AROS 私有的,那麼我們沒有理由不能根據需要動態分配它們。但是,如果它們被初始化為某個值,就像大多數 OOP 陣列一樣,您需要將初始化值儲存在某個地方。為它們分配足夠的空間來儲存它嗎?一種(可能很醜陋)的方法是分配儲存空間 + 陣列,儲存初始化值,將指向陣列本身的指標記錄在所需結構中 - 然後透過宏訪問初始化值?
當原始程序退出時,aroscbase 被設定為 NULL(我猜它也被釋放了)。當然,這仍然被分離的程序使用.. 該應用程式不使用 detach.o?它應該避免這種問題。
問題不在於 arosc.library 本身,而在於它的自動開啟。當然,當程式退出時,符號集會被遍歷以關閉所有自動開啟的庫,這會導致 arosc.library 也被關閉。
SetFunction( struct Library *lib, LONG funcOffset, APTR funcEntry) /* 更改函式查詢位置 */
Exec 根據不同任務的優先順序和需求安排時間切片(多工處理)。
struct Task {
struct Node tc_Node;
UBYTE tc_Flags;
UBYTE tc_State;
BYTE tc_IDNestCnt; /* intr disabled nesting */
BYTE tc_TDNestCnt; /* task disabled nesting */
ULONG tc_SigAlloc; /* sigs allocated */
ULONG tc_SigWait; /* sigs we are waiting for */
ULONG tc_SigRecvd; /* sigs we have received */
ULONG tc_SigExcept; /* sigs we will take excepts for */
UWORD tc_TrapAlloc; /* traps allocated */
UWORD tc_TrapAble; /* traps enabled */
APTR tc_ExceptData; /* points to except data */
APTR tc_ExceptCode; /* points to except code */
APTR tc_TrapData; /* points to trap code */
APTR tc_TrapCode; /* points to trap data */
APTR tc_SPReg; /* stack pointer */
APTR tc_SPLower; /* stack lower bound */
APTR tc_SPUpper; /* stack upper bound + 2*/
VOID (*tc_Switch)(); /* task losing CPU */
VOID (*tc_Launch)(); /* task getting CPU */
struct List tc_MemEntry; /* allocated memory */
APTR tc_UserData; /* per task data */
};
CHILD_NOTNEW = 1 CHILD_NOTFOUND = 2 CHILD_EXITED = 3 CHILD_ACTIVE = 4
任務有訊息埠,可以等待訊號和/或訊息傳遞到埠。這會導致 Dos 庫中的程序。
每個任務都有自己的一組 32 個訊號,其中 16 個留作系統使用。當一個任務向第二個任務發出訊號時,它要求作業系統設定第二個任務訊號的 32 位長字中特定的位。
使用 Wait() 使任務休眠
mysignals = Wait(1L<<17 | 1L<<19); /* waiting on signal 17 or 19 to wake up */
SetSignal() 可以更改任務的訊號位,也可以監控它們。
解決此問題的一個簡單方法是,任務使用 timer.device 或圖形函式 WaitTOF() 在其輪詢迴圈中短暫休眠。如果它是一個程序,則使用 DOS 庫 Delay() 或 WaitForChar() 函式。
- 閱讀更多 此處
訊號量鎖定方法,所有任務在訪問共享資料結構之前都同意鎖定約定。
struct SignalSemaphore {
struct Node ss_Link;
WORD ss_NestCount;
struct MinList ss_WaitQueue;
struct SemaphoreRequest ss_MultipleLink;
struct Task *ss_Owner;
WORD ss_QueueCount;
};
我的建議是儘可能地消除 Forbid()/Permit,並將 Forbid 鎖替換為 AROS 中的訊號量。保留 Forbid()/Permit 的當前狀態,但要麼抱怨(#warning)要麼懲罰程式碼(使用除錯日誌或其他方式)。如果您打算使用 forbid(obj) - 您不妨直接使用訊號量,因為一切歸結為鎖定特定資源訪問。
如果使用 C 或其他低階語言程式設計,程式設計師應該掌握以下內容。
struct SignalSemaphore mymemory_lock;
...
int init_all(void)
{
...
InitSemaphore(&mymemory_lock);
...
}
int myfunc(...)
{
---
ObtainSemaphore(&mymemory_lock);
/* Critical section */
ReleaseSemaphore(&mymemory_lock);
...
}
您使用 ObtainSemaphore 鎖定物件(訊號量),使用 ReleaseSemaphore 釋放它。
當您想要在關鍵部分中排除 Forbid() 時,它將在函式中變成類似這樣的東西。
int myfunc(...)
{
---
ObtainSemaphoreTags(&mymemory_lock, SEM_DONTFORBID, TRUE, TAG_END);
/* Critical section */
ReleaseSemaphore(&mymemory_lock);
...
}
"Amiga 類" 隱藏訊號量 -> 它們通常被隱藏,因為它們不需要被外部程式碼訪問/作為例程的一部分進行處理。
即使在“正常路徑”中,ObtainSemaphore 也會使用 Forbid,這意味著訊號量可用。訊號量函式只在很短的時間內禁止。
效能改進,其背後的想法是記憶體分配器鎖定整個操作。即它在分配開始之前呼叫 Forbid(),只在完成時呼叫 Permit()。因此,決定嘗試使用訊號量而不是禁止,這應該允許其他任務在分配期間工作。
改進的訊號量驗證。現在,如果在主管模式下呼叫訊號量 API,將釋出警報。將記憶體管理切換為使用全域性訊號量而不是 Forbid()/Permit() 對。應該會提高多工處理能力。最好將其設定為執行時(引導選項)選項(預設值為 == 使用 forbid/permit),因為周圍有程式碼不希望 FreeMem() 會破壞 Forbid 狀態。
許多程式需要原始行為,例如許多程式執行以下操作
Forbid() Create new task or process. Set some task variables and do other stuff. Permit()
至少 m68k 必須始終使用原始行為。
Morphos 有一個關於此主題的 很好的介紹。
struct Node {
struct Node *ln_Succ;
struct Node *ln_Pred;
UBYTE ln_Type;
BYTE ln_Pri;
char *ln_Name;
};
ln_Name is currently in the wrong position compared to above, will be fixed by AROS ABIv1.
這個基本節點結構是 AmigaOS (TM) 和 AROS 等列表使用的許多結構的起點。AROS 和其他 Amiga 類作業系統中的許多資訊都儲存在列表中。
在將節點新增到列表之前,必須將其 ln_Type、ln_Pri 和 ln_Name 欄位初始化為相應的值。
struct List {
struct Node *lh_Head; /*first node in list*/
struct Node *lh_Tail; /*always */
struct Node *lh_TailPred; /*last node in list*/
UBYTE lh_Type; /*type of node*/
UBYTE lh_Pad; /*byte not used*/
};
1. Set the lh_Head field to the address of the lh_Tail. 2. Clear the lh_Tail field. 3. Set the lh_TailPred field to the address of lh_Head. 4. Set lh_Type to the same data type as the nodes to be kept the list.
來自 此處
#include <proto/exec.h> void AddHead( struct List *list, struct Node *node ); void AddTail( struct List *list, struct Node *node ); void Enqueue( struct List *list, struct Node *node ); void Insert( struct List *list, struct Node *node, struct Node *pred );
struct Message {
struct Node mn_Node;
struct MsgPort *mn_ReplyPort;
UWORD mn_Length;
};
這會導致 Intuition 中的 IntuiMessage。
CreateMsgPort 即 CreateMessagePort() 完成所有操作
allocate a signal create a message port set the message ports sigbit and task to your allocated signal and self
WaitPort()
listen for that signal in wait respond when messages signal your task
要與裝置通訊,您必須從程式中開啟一個埠。然後您必須初始化一段記憶體,它將作為您的程式和裝置之間的“管道”。在此塊中,您放置與要與其通話的裝置相關的所有必要資訊。作為回報,這段記憶體將填充您請求的資訊。最後一步是開啟裝置。
完成後,您必須按順序關閉,以免丟失使用的記憶體塊。注意,您必須按相反的順序釋放任何分配的記憶體塊。
要建立通訊埠,您需要命令 CreateMsgPort(),它將透過設定以下結構在您的程式中開啟一個“埠訊息”(MsgPort)
struct MsgPort (
struct Node mp_Node;
UBYTE mp_Flags;
UBYTE mp_SigBit;
void * mp_SigTask;
struct List mp_MsgList;
);
透過 PutMsg() 傳送,如果需要回復,則先設定 mn_ReplyPort。要等待訊息,請使用地址初始化 mp_SigTask 並使用訊號編號設定 mp_SigBit。從佇列中移除頂層訊息,使用 GetMsg(),如果佇列為空則返回 0。
每個 SendIO 必須與一個相應的 WaitIO 配對。該程式重用了 timerequest,儘管之前的請求尚未完成。這是不允許的。您不應該在 IORequest 回覆埠上使用 GetMsg。收到訊號後,呼叫 WaitIO 將執行所有必要的請求處理,包括隱式 GetMsg。如果請求已得到回覆,WaitIO 不會再次等待。手動 GetMsg 是不合適的。
DoIO() 和 WaitIO() 以及兩者(DoIO() 實際上跳轉到 WaitIO())將位元組大小的 io_Error 程式碼擴充套件為 LONG,就像 OpenDevice() 一樣。
這是低階 IPC 的樣子
ssize_t uade_ipc_read(void *f, const struct uade_msg *um, const void *buf)
{
struct MsgPort *msgport = (struct MsgPort *) f;
struct UADEMessage *msg;
/*Wait(1 << msgport->mp_SigBit);
WaitPort(msgport);
msg = (struct UADEMessage *) GetMsg(msgport);*/
/* ugly busy loop, because WaitPort isn't working... */
while (!(msg = (struct UADEMessage *) GetMsg(msgport)))
{
Delay(1);
}
CopyMemQuick(&msg->um, um, sizeof(struct uade_msg));
CopyMem(msg->data, buf, ntohl(um->size));
ReplyMsg(msg);
return 1;
}
ssize_t uade_ipc_write(void *f, const struct uade_msg *um, const void *buf)
{
struct MsgPort *msgport = (struct MsgPort *) f;
struct MsgPort *replyport;
struct UADEMessage msg;
if ((replyport = CreateMsgPort()))
{
msg.message.mn_Node.ln_Type = NT_MESSAGE;
msg.message.mn_Length = sizeof(struct UADEMessage);
msg.message.mn_ReplyPort = replyport;
CopyMemQuick(um, &msg.um, sizeof(struct uade_msg));
CopyMem(buf, msg.data, ntohl(um->size));
PutMsg(msgport, (struct Message *) &msg);
WaitPort(replyport);
DeleteMsgPort(replyport);
return 1;
}
return 0;
}
uade_ipc_write 總是建立一個新的回覆訊息埠,這非常低效,但在重複使用同一個埠進行不同的寫入操作時遇到了一些問題。可能更大的問題是 uade_ipc_write,因為它無法使用 WaitPort 或 Wait。它們使用第一個訊息,但之後 uade_ipc_write 將始終在 WaitPort 上阻塞,無論有多少訊息放入埠。不幸的是,Wait 也不好,所以不得不使用那個難看的 while(!GetMsg) 迴圈,延遲為 1/50 秒。
您的 msgport 是否在不同的任務/程序中分配?如果是,您需要先更新 msgport 中的 mp_SigTask 欄位,然後才能使用它。
msgport->mp_SigTask = FindTask(NULL);
還要注意,mp_SigBit 將在錯誤的任務中分配,因此您可能想分配一個新的,以確保安全。
msgport->mp_SigBit = AllocSignal(-1);
您可以建立沒有訊號的埠,如下所示
struct MsgPort *CreatePortNoSignal (void) {
struct MsgPort *port;
port = AllocVec(sizeof(*port), MEMF_CLEAR);
if (port) {
port->mp_Node.ln_Type = NT_MSGPORT;
port->mp_Flags = PA_IGNORE;
port->mp_MsgList.lh_Head = (struct Node *)&port->mp_MsgList.lh_Tail;
port->mp_MsgList.lh_Tail = NULL;
port->mp_MsgList.lh_TailPred = (struct Node *)&port->mp_MsgList.lh_Head;
}
return port;
}
void DeletePortNoSignal (struct MsgPort *port) {
FreeVec(port);
}
只需記住在使用它之前將 mp_SigTask、mp_SigBit 和 mp_Flags 設定為正確的值(mp_Flags 應為 PA_SIGNAL)。
- 需要獲取一個訊息埠
- 為一個稱為 I/O 請求的專用訊息包分配記憶體
- 在 I/O 請求中設定指向訊息埠的指標
- 透過開啟裝置來設定與裝置本身的連結
有很多方法可以建立 I/O 請求
- 將其宣告為結構體。所需記憶體將在編譯時分配。
- 將其宣告為指標並呼叫 AllocMem() 函式。呼叫 FreeMem() 函式釋放記憶體
- 將其宣告為指標並呼叫 CreateIORequest() 函式。
返回值 = OpenDevice(device_name,unit_number, struct IORequest,flags)
DoIO() 和 SendIO() 最常使用。
CloseDevice(IORequest)
struct IORequest (
struct message io_Message;
struct Device * io_Device / * Pointer to the device * /
struct io_Unit Unit * / * unit * /
UWORD io_Command / * Command to perform * /
UBYTE io_Flags;
BYTE io_Error; / * error * /
);
struct IOStdReq {
struct Message io_Message;
struct Device *io_Device; /* device node pointer */
struct Unit *io_Unit; /* unit (driver private)*/
UWORD io_Command; /* device command */
UBYTE io_Flags;
BYTE io_Error; /* error or warning num */
ULONG io_Actual; /* actual number of bytes transferred */
ULONG io_Length; /* requested number bytes transferred*/
APTR io_Data; /* points to data area */
ULONG io_Offset; /* offset for block structured devices */
};
/* library vector offsets for device reserved vectors */
#define DEV_BEGINIO (-30)
#define DEV_ABORTIO (-36)
/* io_Flags defined bits */
#define IOB_QUICK 0
#define IOF_QUICK (1<<0)
#define CMD_INVALID 0
#define CMD_RESET 1
#define CMD_READ 2
#define CMD_WRITE 3
#define CMD_UPDATE 4
#define CMD_CLEAR 5
#define CMD_STOP 6
#define CMD_START 7
#define CMD_FLUSH 8
#define CMD_NONSTD 9
struct unit {
struct MsgPort unit_MsgPort;
UBYTE unit_Flags;
UBYTE unit_pad;
UWORD unit_OPENCNT;
};
您可以在“Snoopy”中找到使用 SetFunction() 進行修補的示例。在退出應用程式之前,您必須重置修補程式(透過使用舊函式的地址呼叫 SetFunction())。但是,您無法捕獲另一個應用程式修補了相同函式的情況。
您可以透過檢查函式地址是否位於所討論資源佔用的記憶體中來確定函式是否已被修補。也許這是 AROS 可以嘗試改進的一個領域,即讓 setfunction 程式碼位於更靈活的底層實現之上,該實現確實為感知應用程式提供了查詢其他修補程式碼的方法——可能有一些方法可以仲裁訪問許可權。例如,應用的修補程式程式碼可以宣告它只想要讀取生成的資料(因此導致實際操作資料的修補程式優先執行)。
處理 I/O 請求和其他訊號的正確方法是
OpenDevice();
// put first command in iorequest
SendIO (iorequest)
while (running)
{
received = Wait (othersignal | iosignal);
if (received & iosignal)
{
WaitIO (iorequest);
// handle data in iorequest if needed
// put next command in iorequest
SendIO (iorequest);
}
if (received & othersignal)
{
// handle other signal
}
}
if (CheckIO (iorequest) == NULL)
AbortIO (iorequest);
WaitIO (iorequest);
CloseDevice();
/*
* This is an example of using the serial device.
* First, we will attempt to create a message port with CreateMsgPort()
* Next, we will attempt to create the I/O request with CreateIORequest()
* Then, we will attempt to open the serial device with OpenDevice()
* If successful, we will send the SDCMD_QUERY command to it and reverse our steps.
* If we encounter an error at any time, we will gracefully exit.
*
* Run from CLI only
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/serial.h>
#include <exec/exec_protos.h>
#include <stdio.h>
void main(void)
{
struct MsgPort *SerialMP; /* pointer to our message port */
struct IOExtSer *SerialIO; /* pointer to our I/O request */
/* Create the message port */
if (SerialMP=CreateMsgPort())
{
/* Create the I/O request */
if (SerialIO = CreateIORequest(SerialMP,sizeof(struct IOExtSer)))
{
/* Open the serial device */
if (OpenDevice(SERIALNAME,0,(struct IORequest *)SerialIO,0L))
/* Inform user that it could not be opened */
printf("Error: %s did not open\n",SERIALNAME);
else
{
/* device opened, send query command to it */
SerialIO->IOSer.io_Command = SDCMD_QUERY;
if (DoIO((struct IORequest *)SerialIO))
/* Inform user that query failed */
printf("Query failed. Error - %d\n",SerialIO->IOSer.io_Error);
else
/* Print serial device status - see include file for meaning */
printf("\n\tSerial device status: %x\n\n",SerialIO->io_Status);
/* Close the serial device */
CloseDevice((struct IORequest *)SerialIO);
}
/* Delete the I/O request */
DeleteIORequest(SerialIO);
}
else
/* Inform user that the I/O request could be created */
printf("Error: Could create I/O request\n");
/* Delete the message port */
DeleteMsgPort(SerialMP);
}
else
/* Inform user that the message port could not be created */
printf("Error: Could not create message port\n");
}
struct Interrupt (
struct Node is_Node;
APTR is_Data;
VOID (*is_code)();
};
Amiga 裝置與 Amiga 庫非常相似,只是裝置通常控制某種 I/O 硬體,並且通常包含一組有限的標準函式,這些函式接收用於控制 I/O 的命令。
struct DeviceList {
BPTR dl_Next; /* bptr to next device list */
LONG dl_Type; /* see DLT below */
struct MsgPort * dl_Task; /* ptr to handler task */
BPTR dl_Lock; /* not for volumes */
struct DateStamp dl_VolumeDate; /* creation date */
BPTR dl_LockList; /* outstanding locks */
LONG dl_DiskType; /* 'DOS', etc */
LONG dl_unused;
BSTR dl_Name; /* bptr to bcpl name */
};
在 AmigaOS 檔案系統(也稱為“處理程式”)中,檔案系統被實現為單個任務,等待接收來自應用程式(或來自 DOS,透過 Open() 等呼叫)的“資料包”。每個資料包都包含一個針對檔案系統任務的命令(例如“開啟檔案”或“建立資料夾”)以及命令所需的任何資料(例如檔名)。
因此,當檔案系統任務接收到資料包時,它無法執行任何操作,直到傳送資料包的任務被中斷。然後它將處理資料包。同時,傳送任務(應用程式)可以繼續執行其他工作。檔案系統任務在命令完成時向傳送任務發出訊號。
使用 CreateIORequest() 函式。完成此操作後,使用 OpenDevice() 開啟裝置。
Error = OpenDevice (Name, Unit, RequestIO Flags)
名稱:裝置的名稱。單元:預設值為 0。錯誤 =? (能夠始終開啟一個錯誤返回 NULL)
首先關閉最後開啟的內容。實際上,如果我們關閉第一個通訊埠 (MsgPort),然後關閉查詢結構 (IORequest),預計會丟失記憶體或更糟的是導致系統崩潰。
CloseDevice ()
使用 IORequest DeleteIORequest() 釋放結構,最後執行 DeleteMsgPort()。
現在我們知道了用於通訊(同步和非同步)的命令(DoIO、SendIO、AbortIO 和 CheckIO),我們必須知道在 IOStdReq 結構中填寫什麼。
struct IOStdReq (
...
struct Device * io_Device / * Pointer to the device * /
struct Unit * io_Unit / * Unit * /
UWORD io_Command / * Command * /
UBYTE io_Flags / * IOF_QUICK or not * /
BYTE io_Error / * error * /
ULONG io_Actual / * Number of bytes transferre * /
ULONG io_Length / * Number of bytes to transfer * /
TRPA io_Data / * Pointer to memory area * /
...
);
它應該用 io_Command 欄位填寫要執行的命令,用 io_Length 欄位填寫要傳輸的總位元組數,用 io_Data 欄位填寫記憶體區域或資料的起始位置。
AROS console.device 和 console-handler,兩者都旨在將其升級到 3.x 版本,並新增額外的功能(有效地重新實現 KingCON 的許多重要功能,更清晰地分層在 console.device/console-handler 上,而不是將其全部塞入替換的 console-handler 中)。
console.device/handler 具有私有 API 用於與 ConClip 通訊。AROS ConClip 使用直覺編輯掛鉤從字串小部件獲取剪下/貼上事件,並將它們轉換為傳送到 ConClip 任務的訊息;我將讓 console.device/handler 使用相同的格式。
開啟控制檯裝置需要四個步驟
- 使用 CreatePort() 建立一個訊息埠。
- 建立一個型別為 IOStdReq 的 I/O 請求結構。
- 開啟一個直覺視窗,並在 IOStdReq 的 io_Data 欄位中設定指向它的指標,並在 io_Length 欄位中設定視窗的大小。該視窗必須為 SIMPLE_REFRESH,才能與 CONU_CHARMAP 和 CONU_SNIPMAP 單元一起使用。
- 透過呼叫 OpenDevice() 開啟控制檯裝置
控制檯裝置單元
CONU_LIBRARY - return device pointer, no console is opened
CONU_STANDARD - synchronous communication to open a standard console
CONU_CHARMAP - asynchronous mode to open a console with a character map
CONU_SNIPMAP - open a console with a character map and copy-and-paste support
控制檯裝置標誌
CONFLAG_DEFAULT - redraw the window when it is resized CONFLAG_NODRAW_ON_NEWSIZE - will not redraw the window when it is resized
字元對映單元 CONU_CHARMAP 和 CONU_SNIPMAP 是唯一使用 flags 引數來設定字元對映使用方式的單元。CONU_STANDARD 單元忽略 flags 引數。
呼叫 setbuf() 來關閉 stdout 的緩衝。在退出時,流保持為無緩衝的(在除錯開啟的情況下,我看到所有後續命令都執行而沒有緩衝——不好)。
console.device 確實可以在沒有它的情況下正常工作——它只是回退到其內部複製/貼上緩衝區。但是 ConClip 在經典系統上實現的是將 console.device/handler 與讀取/寫入 clipboard.device 的路徑分離。對於那些始終在 RAM 中擁有剪貼簿的人來說,這幾乎沒有意義。如果您執行大型複製/貼上操作並將剪貼簿儲存在速度較慢的磁碟上,則它並不那麼沒有意義。
已經存在指定文字或背景的筆(調色盤條目)編號的 ESC 序列。就像在原始 AmigaOS 上所做的那樣。但是有一個小問題——在深度螢幕(>3bpp)上,只有前四個筆被定義。以及最後四個筆(這是為了讓畫素反轉可以工作)。它們之間的筆未定義。它們可以是未初始化的(設定為黑色)或由其他應用程式動態分配並設定為它想要的值。
高階 shell(如 VinCED 和 MUICon)只分配它們想要的筆,並將顏色編號對映到分配的筆編號。這是在公共螢幕上使用調色盤的標準方法。在直接顏色螢幕上,調色盤仍然有效,這是由 graphics.library 處理的。因此,當您需要使用少量固定數量的定義顏色時(例如在您的情況下),使用調色盤是可以的。
shell 不會像您想象的那樣簡單地從命令列解析引數,然後將緩衝區傳遞給呼叫的程式。相反,它將資料推送到用作“字串流”的檔案控制代碼中,從中可以從記憶體緩衝區讀取資料。此類技巧沒有記錄。實際上,shell 的行為沒有記錄,它是一堆令人難過的 hack。
它與 rastport 唯一的互動應該是 ScrollRaster、RectFill()、Text()、SetAPen()、SetABPenDrMd() 和 SetSoftStyles()。
更相容的 KS 替代方案,用於模擬。不喜歡預先分配過多記憶體(甚至更糟的是,使用靜態陣列)而不是在真正需要記憶體時才分配記憶體的 rom 程式碼。但我不會進行會使程式碼更難讀且更混亂的更改,僅僅因為這樣做會將記憶體使用量減少 100 位元組或類似的值 :)。
擴充套件 RawDoFmt() 是一個糟糕的主意。RawDoFmt() 應該只做原始自動文件所說的,所有 AROS 程式應該使用 VNewRawDoFmt()。這些常量來自 MorphOS,並且 NULL 指標 (RAWFMTFUNC_STRING) 也受 AmigaOS4 支援。這些系統上的 m68k 程式碼可以利用它們。
locale.library 修補了 VNewRawDoFmt,因此實際上使用了 LocVNewRawDoFmt。當使用原始 VNewRawDoFmt 時,不會發生崩潰。我做了一些程式碼比較並得出了一些結論
首先,資料流在記憶體中的設定方式如下
i : 4 位元組處理器 : 從第 5 個位元組開始
即使 i 被降級((UWORD)i),它仍然佔用 4 個位元組,這就是為什麼此更改沒有幫助的原因。
接下來,這兩個函式都認為一個裸 '%d' 應該為 2 個位元組/sizeof(UWORD) 長,而 "%ld" 被認為是 4 個位元組/sizeof(ULONG)。然而,不同之處在於這兩個函式如何從 va 流讀取資料。
VNewRawDoFmt 將任何小於或等於 int 的資料讀取為 int,而 LocVNewRawDoFmt 將 UWORD 讀取為 2 個位元組。
另一個顯著差異是 VNewRawDoFmt 具有兩個獲取宏(fetch_va_arg 和 fetch_mem_arg),而 LocVNewRawDoFmt 對這兩種情況(va 流和記憶體流)使用相同的宏。
- 哪種行為是正確的(fetch_va_arg vs stream_addr)?
最初的原因是 CON: 是編譯器/autoinit/stdiowin.c,它始終將輸入和輸出流設定為“CON://///AUTO/CLOSE”。這將強制在程式從 Input()/Output() 進行任何讀寫操作時開啟控制檯視窗。
對原始 AOS 進行了一些測試,發現當從 WB 啟動時呼叫 ReadArgs 時,沒有 CON 視窗。
- 在 stdiowin 中執行類似於讀取 oldin[檢查拼寫] 並重新注入到新的輸入中。然後 ReadArgs 應該獲取最初由 createnewproc 或 runcommand 注入的 EOF。
- 將 __stdiowin 設定為 NIL:作為預設值,這似乎與我剛測試的結果一樣有效。
在 UNIX 主機上引入了 kernel.resource,現在正在逐步將其功能遷移到該資源中。首先是統一 InternalLoadSeg()。現在 dos.library 在 kernel.resource 中註冊已載入的可執行檔案,而不是使用自己的私有結構。kernel.resource 匯出一個靜態變數,以便讓 gdb 訪問此列表。
使用 --enable-debug=modules,symbols 重新配置 AROS。我猜想 "symbols" 應該向核心可執行檔案新增完整的除錯資訊。但在這樣做之後,gdb 只打印了錯誤,類似於 "Dwarf error: failed to resolve reference no 28"。並且它再次無法讀取型別資訊。
順便說一句,新程式碼不再需要 SET_START_ADDR(),這意味著 gdb 除錯現在將在所有架構上工作。
我們已經擁有了可工作的 tc_TrapCode 欄位。如果我們將其指向某些程式碼,當 CPU 遇到陷阱時,該程式碼將在監督模式下執行。至少在 i386 原生和 Windows 主機上是如此。但是,當前程式碼只接收警報號,這還不夠。為了能夠進行任何高階診斷,我們需要訪問 CPU 上下文。我們也應該能夠修改它。在經典 AmigaOS 中,此處理程式使用 C 約定(引數在堆疊上)呼叫。堆疊佈局如下:
0(sp).l = trap# 4(sp) Processor dependent exception frame
AROS 不會直接在堆疊中儲存 CPU 上下文。相反,它使用 ETask 結構的私有部分中的獨立儲存。這裡最簡單的方法是將 TrapCode 入口點宣告為以下內容:
void TrapCode(ULONG trapNum, struct ExceptionContext *ctx);
這樣我們就可以在堆疊上擁有兩個指標。如果我們進一步發展這個想法,我們會發現,如果我們將其宣告為以下內容,就可以輕鬆地提供與 m68k AROS 埠的二進位制和原始碼相容性:
void TrapCode(ULONG trapNum, APTR ContextHandle);
並在程式碼內部,我們使用宏來訪問實際的 CPU 上下文:
struct ExceptionContext *ctx = GET_CONTEXT(ContextHandle);
其中,GET_CONTEXT 在 m68k 上擴充套件為以下內容:
#define GET_CONTEXT(x) ((struct ExceptionContext *)&x)
在其他系統上擴充套件為以下內容:
#define GET_CONTEXT(x) ((struct ExceptionContext *)x)
現在我們有了上下文指標。但是,為了能夠對它做些什麼,我們需要宣告上下文的實際結構。我建議在 aros/cpu.h 中進行宣告。對於同一個 CPU,所有版本(主機和原生)的上下文應該保持一致。
我選擇了 ExceptionContext 作為結構的名稱,以便與 AmigaOS4 相容(也許將來也能實現二進位制相容)。MorphOS 缺少陷阱處理功能(不是真的,但機制完全不同,透過訊息傳遞工作),所以沒有東西可以借鑑。
在 AmigaOS4 中,ExceptionContext 具有以下定義:
struct ExceptionContext
{
uint32 Flags; /* Flags, describing the context (READ-ONLY)*/
uint32 Traptype; /* Type of trap (READ-ONLY) */
uint32 msr; /* Machine state */
uint32 ip; /* Return instruction pointer */
uint32 gpr[32]; /* r0 - r31 */
uint32 cr; /* Condition code register */
uint32 xer; /* Extended exception register */
uint32 ctr; /* Count register */
uint32 lr; /* Link register */
uint32 dsisr; /* DSI status register. Only set when valid */
uint32 dar; /* Data address register. Only set when valid */
float64 fpr[32]; /* Floating point registers */
uint64 fpscr; /* Floating point control and status register */
/* The following are only used on AltiVec */
uint8 vscr[16]; /* AltiVec vector status and control register */
uint8 vr[512]; /* AltiVec vector register storage */
uint32 vrsave; /* AltiVec VRSAVE register */
};
Flags 是一個特殊欄位,它告訴哪些欄位實際存在於此結構中。這樣就提供了與可能未來 CPU 中更多暫存器的向後相容性。
enum enECFlags
{
ECF_FULL_GPRS = 1<<0, /* Set if all register have been saved */
/* If this flag is not set, gpr[14] through */
/* gpr[31] are invalid */
ECF_FPU = 1<<1, /* Set if the FPU registers have been saved */
ECF_FULL_FPU = 1<<2, /* Set if all FPU registers have been saved */
ECF_VECTOR = 1<<3, /* Set if vector registers have been saved */
ECF_VRSAVE = 1<<4 /* Set if VRSAVE reflects state of vector */
/* registers saved */
};
我建議將相同的結構用於 PPC AROS。我已經查看了任務切換器程式碼,可以重新排列暫存器以匹配此佈局。無論如何,此佈局比當前使用的佈局更好,因為它支援 AltiVec。
至於 i386,我看到了當前的 AROS 上下文(定義不完善)和 Windows 上下文。這裡有趣的是,AROS 儲存 8087 FPU 暫存器集或 SSE2 暫存器集。這真的是互斥的嗎?SSE 不能與 8087 FPU 同時使用嗎?這就是我們在 Windows 中所擁有的:
#define MAXIMUM_SUPPORTED_EXTENSION 512
typedef struct _FLOATING_SAVE_AREA {
IPTR ControlWord;
IPTR StatusWord;
IPTR TagWord;
IPTR ErrorOffset;
IPTR ErrorSelector;
IPTR DataOffset;
IPTR DataSelector;
UBYTE RegisterArea[80];
IPTR Cr0NpxState;
} FLOATING_SAVE_AREA;
struct AROSCPUContext {
IPTR ContextFlags;
IPTR Dr0;
IPTR Dr1;
IPTR Dr2;
IPTR Dr3;
IPTR Dr6;
IPTR Dr7;
FLOATING_SAVE_AREA FloatSave;
IPTR SegGs;
IPTR SegFs;
IPTR SegEs;
IPTR SegDs;
IPTR Edi;
IPTR Esi;
IPTR Ebx;
IPTR Edx;
IPTR Ecx;
IPTR Eax;
IPTR Ebp;
IPTR Eip;
IPTR SegCs;
IPTR EFlags;
IPTR Esp;
IPTR SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
};
ContextFlags 是:
#define CONTEXT_i386 0x10000 #define CONTEXT_i486 0x10000 #define CONTEXT_CONTROL (CONTEXT_i386|0x00000001L) #define CONTEXT_INTEGER (CONTEXT_i386|0x00000002L) #define CONTEXT_SEGMENTS (CONTEXT_i386|0x00000004L) #define CONTEXT_FLOATING_POINT (CONTEXT_i386|0x00000008L) #define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386|0x00000010L) #define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386|0x00000020L) #define CONTEXT_FULL (CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS)
如您所見,SSE 和 8087 有兩個獨立的區域。我們應該為 i386 AROS 採用相同的結構,還是模稜兩可?無論如何,i386 AROS 上下文也應該可擴充套件,並且應該包含一些標誌。我沒有檢視 x86-64 埠,但它應該以類似的方式實現。我也沒有檢視 UNIX 主機埠。它們的上下文是一個黑暗森林,但想法是類似的,至少它可以轉換為我們選擇的通用形式。
我突然想到了一個替代想法。上下文結構可以保持為私有 blob,我們可以在 kernel.resource 中新增一些函式來解析它。比如 KrnGetContextReg() 和 KrnSetContextReg()。這樣更靈活,但可能更慢,更復雜。
- 啟動時間。
- 工作時間雙重故障或監督模式故障。
如果您閱讀並研究 Alert() 和底層程式碼,您會看到一個相當複雜的涉及 ETask 的狀態機。我付出了很多努力,才讓它變得像現在這樣健壯。基本思想是,每個任務都可以有一個 "Crashed" 狀態,並具有一些上下文。在最近的實現中,上下文有幾種型別。它可以是 CPU 上下文或 mungwall 上下文。更多上下文即將到來(例如記憶體管理器上下文)。每當任務崩潰時,崩潰上下文都會記錄在 ETask 中。然後控制權傳遞給 Alert()(或更準確地說是 Exec_ExtAlert())。Exec_ExtAlert() 首先檢查當前的 CPU 許可權級別。如果是使用者級別,那麼我們很可能正在執行通常的任務上下文(要麼是直接的 Alert() 呼叫,要麼是 exec 的處理程式利用的 CPU 異常)。在這種情況下,會呼叫 Exec_UserAlert()。它嘗試透過 intuition.library 啟動一個請求器。這個過程以幾種方式結束:
- 成功完成,使用者響應 "繼續" 以恢復警報。崩潰狀態清除,Alert() 返回。
- 雙重警報。Alert() 再次進入。在這種情況下,新的崩潰上下文不會被記住。Exec_UserAlert()(如果進入)
再次)會發現任務已經處於崩潰狀態,並且將執行權交給 Exec_SystemAlert(),將警報級別提升到 DEADEND。注意,"缺少顯示驅動程式"(AN_SysScrn)也算在內。由於崩潰上下文沒有被覆蓋,所以我們仍然會代表問題的最初原因。當然,這是對任務的死衚衕,而不是對整個系統的死衚衕。您會看到 Suspend/Reboot 選項,而不是 Continue 按鈕,僅此而已。
有三個子系統特定的 ShutdownA() 實現,放置在不同的元件中:
- 原始 IBM AT 硬體的預設實現。透過 AT 鍵盤控制器執行冷重啟。
- ACPI 實現。目前只支援透過 ACPI 重置暫存器執行冷重啟(不需要 AML 直譯器)。
- EFI 實現,使用 EFI 執行時介面。
這些元件修補 ShutdownA() 向量,以安裝自己的例程(如果可用)。EFI 具有最高優先順序(如果存在 efi.resource,ACPI 不會安裝自己的修補程式)。
一開始,它是沒有修補程式完成的。exec.library 程式碼看起來像這樣:
if (EFIBase)
{
... efi shutdown...
}
if (ACPIBase)
{
.... acpi shutdown...
}
... kbc shutdown ...
真的不喜歡這種模式。第四種方法會增加一個 'if',等等。這段程式碼不受任何合併的影響。這就是為什麼我認為沒有用於關閉機制的抽象層的原因。
如果 Alert() 在監督級別被呼叫,我們建議我們在中斷處理程式中崩潰。我們簡單地呼叫 Exec_SystemAlert()。現在,Exec_SystemAlert() 必須盡一切可能通知使用者問題。警報在這裡 99% 是死衚衕,系統處於不可用狀態(它無法啟動 Intuition 請求器)。我們可以在這裡做任何事情。覆蓋螢幕、VRAM 等。然後會重啟。
我的實驗程式碼透過 libbootconsole 在 VESA 幀緩衝區上實現了 Exec_SystemAlert()。
我相信有人談論過保護 kickstart 程式碼區域免遭寫入。應用程式試圖寫入此區域會導致 GURU 還是可恢復的軟體故障(我實際上希望是後者)。死衚衕,但軟體故障。在任何主機或 x64-64-原生上嘗試 tests/exec/traptest illegal <address>。
您所說的 "設定幀緩衝區" 是什麼意思?您指的是 "強制從原生模式切換到 VGA 模式" 嗎?無論驅動程式的編寫者想要什麼。
在 "強制幀緩衝區" 上顯示某些內容的程式碼在哪裡。這段程式碼如何知道 "幀緩衝區" 的組織方式(寬度/高度/bpp/對齊)。回撥可以填寫一些結構,類似於引導載入程式提供的結構。核心會將指向此結構的指標提供給此回撥。
每個驅動程式是否都需要提供一個預定義組織的幀緩衝區?我目前的建議是將其留給驅動程式決定。它可以提供任何東西,只要 libbootconsole 支援即可。目前,此庫支援 EGA/VGA 硬體文字模式或塊狀幀緩衝區(LUT 和直接顏色)。
所以驅動程式本身會接收包含要顯示的文字的結構?驅動程式會接收一個結構,它會將幀緩衝區引數(型別、解析度、地址)放入該結構中。libbootconsole 然後會使用它。
您如何設想驅動程式註冊此回撥?是否會在 exec 中新增一個新函式來執行此操作?在核心中。類似於 KrnAddVideoReserCallback()。它是特定於影片的。
還有一個替代想法 - 使用機器的 BIOS。有人知道,是否可以在 AROS 內部恢復功能性 BIOS 狀態?再說一次,不用擔心記憶體覆蓋等。只要初始化一些影片模式,顯示文字並停止即可。除了跳轉到 16 位真實模式,還需要什麼?我是否必須恢復中斷向量等?EFI 似乎更禮貌。至少它的執行時服務提供了一些控制檯。
這樣的定義是錯誤的 - 軟體應該在執行時確定有多少記憶體可用,並進行相應的設定。順便說一句,這些雜湊東西都是多餘的。我們可以使用 exec 的 AVL 例程來進行關聯。graphics.library 和 oop.library 也適用。當庫基地址具有 "巨大" 的靜態陣列時,很難動態更改核心記憶體使用情況。
execbase AlertBuffer 也相對較大。(從 A500 的角度來看)。它真的需要這麼大嗎?實際上,它只需要儲存完整的警報文字(包括回溯)。列印文字後,系統會重啟。因此,理論上,只需要一個指標即可。在這裡破壞記憶體是安全的(好吧,幾乎,我們不應該破壞重置生存程式碼)。順便說一句,在原生埠上,零頁可以用於此。據我所知,它本來就是保留的。
AROS 的使用範圍從在 Amiga 500 克隆上執行來自軟盤的一兩個程式,到在價值 2000 美元的 PC 上進行繁重的多工桌面使用。
由於 dosboot.resource 現在不再包含特定機器的程式碼,因此將從規範中刪除 strap。這兩個模組將被放入 base。檔案系統將保留在原生上的 'FS' 包中。
AROS 沒有 PThread 庫。但是,C 執行時庫的一部分在共享庫中,這些庫本質上是可重入的程式碼。只有靜態連結的 C 執行時庫的一部分可能是執行緒不安全的。
當前的 C 庫為每個任務(例如執行緒)建立了一個 C 上下文,以便 malloc/free 能正確執行。我認為,如果你開始線上程之間共享檔案控制代碼,你會遇到問題。我認為你可以配置 C 庫使其與子程序共享上下文;例如,所有執行緒將使用同一個上下文,但這樣我認為 malloc/free 就不會再是多執行緒安全的。對於 ABI V1 中 C 庫的當前實現,malloc/free 不是執行緒安全的。
目前,malloc/free 基於訊號量保護 (MEMF_SEM_PROTECTED) 的 Exec 記憶體池,因此它是執行緒安全的。因此,這意味著 ABI V1 對 mem 函式的實現也是執行緒安全的。
但是,正如你幾周前已經指出的,當前的 C 庫在使用 FILE* 控制代碼的 fopen()/fread()/fwrite() 等函式方面不是執行緒安全的。這實際上是 AROS 對最新的 YAM 夜間構建的支援目前失效的原因,因為在 YAM 2.7 中,許多東西現在被放在了單獨的執行緒中,這需要所有這些函式都能夠執行緒安全地工作。在其他平臺(OS4/newlib、OS3/clib2)上,這種情況已經存在。
void Disable( void ); 為當前任務停用中斷。中斷不應被停用太長時間。Wait() 會從停用狀態退出,但當任務恢復執行時,中斷將再次被停用。每次呼叫 Disable() 都會增加中斷停用計數。
void Enable( void ); 會減少中斷停用計數。當計數達到零時,將啟用中斷。後續呼叫 Enable() 不會有任何作用,停用巢狀計數將保持為零。
void Forbid( void ); 會增加任務切換停用計數。Wait() 會從禁止狀態退出,但當任務恢復執行時,任務切換將再次被停用。基本上,Forbid() 停用了任務切換。只有在無法使用訊號量時才使用它!
void Permit( void ); 會減少任務切換停用計數。當計數達到零時,切換將再次被啟用。後續呼叫 Permit() 不會有任何作用。
void AddIntServer( long vector (D0), struct Interrupt *server (A0) ); 會將一個全域性中斷伺服器新增到給定的異常向量中。鏈中所有中斷伺服器將按順序被呼叫。中斷伺服器的呼叫方式為
is_Code( ULONG is_Data (D0), UWORD *StackFrame (A1) );
引數既在暫存器中,也在 C 語言處理程式的堆疊中。StackFrame 包含一個長字,其中包含異常號,然後是“正常”的 68000 堆疊幀。
可以使用 tc_ExceptData 和 tc_ExceptCode 安裝私有(特定於任務的)異常處理程式。如果未為異常安裝全域性處理程式,則將呼叫特定於任務的處理程式。如果也沒有特定於任務的處理程式(tc_ExceptCode = NULL),則該任務將被掛起。
void RemIntServer( long vector (D0), struct Interrupt *server (A0) ); 會從中斷伺服器鏈中刪除指定的中斷伺服器。請記住,為處理程式生成中斷的源必須首先被停用。
void Cause( long vector (D0) ); 在監督模式下呼叫中斷伺服器。它的行為就像一個真實的中斷。如果未為該異常安裝全域性處理程式,也未安裝特定於任務的處理程式,則該任務將被掛起。
Cause() 由訊息傳遞系統使用,以實現每當訊息到達時都會建立軟中斷的訊息埠。
void *AllocMem( ULONG size (D0), ULONG flags (D1) ); 分配一塊請求大小的記憶體。返回指向記憶體塊的指標,該指標至少與長字對齊。flags 可以設定為 MEMF_CLEAR 以將分配的記憶體初始化為全零。
使用的分配方法是“首次匹配”。返回第一個足夠大的空閒記憶體列表塊。
void FreeMem( void *block (A0), ULONG size (D0) ); 會釋放一塊記憶體。Size 必須是對應分配中使用的 size,block 應是分配返回的值。
如果被釋放的塊與其他空閒記憶體塊相鄰,則它們將被合併。
ULONG AvailMem( ULONG flags (D0) ); 會返回剩餘空閒記憶體的大小。由於空閒記憶體碎片,可能無法分配所有剩餘記憶體。可以使用標誌 MEMF_LARGEST 來找出最大連續空閒記憶體塊的大小。
void AllocRemember( struct List *memlist (A0), ULONG size (D0), ULONG flags(D1) ); 分配一塊至少為請求大小的記憶體塊,並將記賬資訊新增到指定的列表 (memlist) 中,以便可以同時釋放該列表中的所有分配的塊。AllocRemember() 返回指向記憶體塊的指標,該指標至少與長字對齊。Size 和 flags 與 AllocMem() 相同。
NULL memlist 會將分配新增到任務的分配列表中,如果這些分配未在之前被釋放,則在任務退出時會自動釋放它們。
由於從 shell 執行的程式在同一個任務/程序上下文環境中同步執行,因此通常不建議使用任務的記憶體列表。使用自己的列表,在使用前使用 NewList(memlist),並在最後使用 FreeRememberAll(memlist)。
void FreeRemember( void *block (A0) ); 釋放之前透過 AllocRemember() 分配的記憶體。記憶體將從分配列表中刪除並返回給系統。
不要對不是透過 AllocRemember() 分配的記憶體塊使用 FreeRemember()!
void FreeRememberAll( struct List *memlist (A0) ); 釋放與 memlist 關聯的所有分配。在 FreeRememberAll() 之後,該列表將為空,可以在不使用 NewList() 的情況下重複使用。
在向列表新增任何內容之前,必須初始化列表頭 (NewList())。
一個節點一次只能存在於一個列表中。在將節點新增到另一個列表之前,必須從當前列表中刪除該節點。此外,如果節點當前不在列表中,則絕對不能刪除該節點。void Insert( struct List *list (A0), struct Node *node (A1), struct Node *pred (A2) ); 將節點新增到列表中 pred 節點之後。如果 pred 為 NULL,則新增到列表的頭部。
void AddHead( struct List *list (A0), struct Node *node (A1)); 將節點新增到列表的頭部。
void AddTail( struct List *list (A0), struct Node *node (A1) ); 將節點新增到列表的尾部。
void Remove( struct Node *node (A1) ); 從該節點所在的列表中刪除該節點。如果節點不在列表中,則不要使用它,否則會導致記憶體損壞!
struct Node *RemHead( struct List *list (A0) ); 從列表中刪除第一個節點。如果列表為空,則返回 NULL。
struct Node *RemTail( struct List *list (A0) ); 從列表中刪除最後一個節點。如果列表為空,則返回 NULL。
void Enqueue( struct List *list (A0), struct Node *node (A1) ); 根據 ln_Pri 欄位將節點新增到列表中。數字越小表示優先順序越低。優先順序較高的節點最終會進入列表的頭部,但會在所有優先順序相同的節點之後。這也意味著 RemHead()/ Enqueue() 會迴圈遍歷優先順序最高的節點。
struct Node *FindName( struct List *list (A0), UBYTE *name (A1) ); 在列表中按名稱查詢節點。如果未找到指定名稱的節點,則返回 NULL。如果 ln_Name 欄位未初始化,則不要使用!!
請記住,節點名稱可能不唯一。由於 FindName() 的操作方式,您可以將節點結構傳遞給它。您可以使用以下構造來查詢列表中所有具有特定名稱的條目。
struct List *mylist;
struct Node *node;
node = (struct Node *)mylist;
while((node = FindName((struct List *)node, name)))
{
/* Do something with the node. */
}
void NewList( struct List *list (A0) ); 將列表頭初始化為空列表狀態。
struct Node *HeadNode( struct List *list (A0) ); 返回列表中的第一個節點,如果列表為空,則返回 NULL。
struct Node *NextNode( struct Node *list (A0) ); 返回下一個節點,如果節點是最後一個節點,則返回 NULL。
struct Node *TailNode( struct List *list (A0) ); 返回列表中的最後一個節點,如果列表為空,則返回 NULL。
struct Node *PrevNode( struct Node *list (A0) ); 返回下一個節點,如果節點是第一個節點,則返回 NULL。
void AddTask( struct Task *task (A1), long InitialPC (D0), long FinalPC (D1) ); 將任務新增到系統。執行將從 InitialPC 開始,當任務退出時,執行將跳轉到 FinalPC。如果 FinalPC 為 NULL,則使用預設的 Expunge 例程(推薦)。任務結構應包含已初始化的 tc_StackSize 和 tc_Stack。結構的其他成員由 AddTask() 初始化。新任務將獲得其父任務的 Task 指標作為引數。
struct Task *FindTask( char *name (A1) ); 將按名稱查詢任務。如果 name 為 NULL,則將返回當前任務的任務結構指標。如果未找到與名稱匹配的任務,則返回 NULL。
請記住,任務名稱不唯一,任務列表中可能存在許多具有相同名稱的任務。
WORD SetTaskPri( struct Task *task (A0), WORD priority (D1) ); 設定任務的優先順序。值越高,優先順序越高。此呼叫將返回舊的優先順序。實際上,優先順序只是一個 BYTE 值。-128 的優先順序最低,127 的優先順序最高。
觀察:目前,新的優先順序將在任務下次獲得 CPU 時生效。這可以被認為是一個錯誤特徵,但由於對其他任務的優先順序更改並不經常進行,因此這種可預測的延遲被認為是可以接受的。
ULONG SetSignal( ULONG newsigs (D0), ULONG sigmask (D1) ); 將設定和重置訊號。僅更改 sigmask 中設定的訊號位。Newsigs 包含 sigmask 中定義的訊號的新訊號狀態。將返回訊號的舊狀態。
SetSignal(0, 0) 可用於讀取當前訊號狀態,而不影響訊號。
ULONG Wait( ULONG mask (D0) ); 等待 mask 中定義的任何訊號。如果 mask 中沒有一個訊號處於活動狀態,則掛起任務,直到收到 mask 中的任何訊號。此呼叫將返回導致喚醒的訊號。如果 mask 中定義的訊號已設定,則不會進行等待。在返回之前,將清除 mask 中定義的訊號。
void Signal( struct Task *task (A0), ULONG sigmask (D0) ); 向指定的任務傳送一個或多個訊號。如果任務正在等待訊號,則將其喚醒。
Signal() 可以從中斷中呼叫。
ULONG AllocSignal( void ); 為任務分配一個訊號位。將返回位的編號。要獲得訊號掩碼,您必須執行 (1L<<bitnumber)。
VOID FreeSignal( ULONG sigbitnum (D0) ); 釋放先前分配的訊號位。使用位的編號。引數應該是 AllocSignal() 呼叫返回的位編號。
void AddProgram( struct Program *program (A1) ); 將程式新增到系統。程式結構包含程式重定位所需的所有資訊。當程式被新增到系統中時,可以啟動多個程式副本。為 struct Program 分配的記憶體將成為系統所有,因此必須使用 AllocMem() 分配。
使用 AddProgram() 新增的程式可以透過 dos.library 呼叫 ROMLoadSeg() 載入和重新定位。
struct Program *RemProgram( char *name (A1) ); 從系統中刪除程式。此呼叫不會釋放為程式分配的記憶體。使用該呼叫的任務成為資料的擁有者,應在退出之前釋放記憶體。
void AddPort( struct MsgPort *port (A1) ); 將公共埠新增到系統。埠應正確初始化,並且不能是已經存在的公共埠。請參閱 CreatePort()。
void RemPort( struct MsgPort *port (A1) ); 從系統中刪除公共埠。僅當埠是公共埠時才使用此呼叫。請參閱 DeletePort()。
觀察:刪除公共埠是不安全的,因為沒有記錄持有對埠引用的任務。一個半安全的解決方案是 1) 從公共埠列表中刪除埠 2) 等待一段時間以接收任何訊息,並且只有在沒有訊息到達時,才 3) 刪除埠。這假設如果一個任務獲取到對公共埠的引用,它很可能會在不久的將來使用該引用將訊息傳送到該埠。
如果您需要可以安全刪除的公共埠,請使用公共訊號量,因為它們具有引用計數。
struct MsgPort *CreatePort( char *name (A1) ); 分配並初始化訊息埠。如果 name 不為 NULL,則將埠新增到系統公共埠列表。如果 name 為 NULL,則埠將成為私有埠,如果需要,可以稍後將其新增到公共埠列表。(在這種情況下,mp_Node.ln_Name 必須在呼叫 AddPort() 之前初始化。)
void DeletePort( struct MsgPort *port (A1) ); 釋放 CreatePort() 建立的訊息埠。如果埠是公共埠,它也將從系統列表中刪除。
請記住,刪除公共埠並不完全安全。請參閱 RemPort() 下的說明。
struct MsgPort *FindPort( char *name (A1) ); 在埠列表中搜索指定埠名稱。如果未找到埠,則返回 NULL。
請記住,埠名稱不唯一。
void WaitPort( struct MsgPort *port (A0) ); 等待埠中有訊息。不從埠中刪除訊息。使用 GetMsg() 獲取訊息。如果已經有訊息在等待,則不會進行等待。
void PutMsg( struct MsgPort *port (A0), struct Message *message (A1) ); 將訊息放到指定的埠。在此呼叫之後,訊息將成為接收者的財產,在透過 replyport 收回之前,不應對其進行修改。mn_Node.ln_Type 將變為 NT_MESSAGE。
單向通訊也是可能的。在這種情況下,接收者不會回覆訊息,並且必須釋放或重新使用接收到的訊息。這隻有在像這樣編寫的任務之間才有可能。
struct Message *GetMsg( struct MsgPort *port (A0) ); 從埠獲取訊息。如果沒有訊息在等待,則返回 NULL。
請記住,可能有多條訊息已到達訊息埠,而您只獲得了一條訊號。使用 while() 來處理所有訊息,然後再等待。如果您使用 WaitPort(),它會為您處理這個問題(在等待之前檢查埠)。此外,必須特別注意確保在退出之前訊息埠為空。
void ReplyMsg( struct Message *message (A1) ); 將訊息返回其回覆埠。mn_Node.ln_Type 將變為 NT_REPLYMSG。
限制:ReplyPort 永遠不能是軟中斷埠。(但是您無法使用 CreatePort() 建立一個:-))。
void SendTimerReq( struct TimerReq *request (A1) ); 將計時器請求傳送到系統。
void AddDevice( struct Device *device (A1) ); 將裝置新增到系統。裝置結構應初始化並準備接受 Open() 呼叫。
struct Device *RemDevice( struct Device *device (A1) ); 從系統中刪除裝置。如果裝置被任何人開啟,則該呼叫返回 NULL。
LONG OpenDevice( char *name (A1), ULONG unit (D0), struct StdIOReq *req (A0), ULONG flags (D1)); 開啟指定的裝置。struct StdIOReq 由 OpenDevice() 呼叫和內部裝置 Open() 呼叫填充。裝置本身維護 lib_OpenCnt。如果開啟成功,則返回 0。
unit、req 和 flags 被推入堆疊,用於 C 語言 Open() 處理程式。
void CloseDevice( struct StdIOReq *req (A1) ); 關閉由 OpenDevice() 開啟的裝置(對裝置的 Close() 入口進行內部呼叫)。裝置本身維護 lib_OpenCnt。
req 被推入堆疊,用於 C 語言 Close() 處理程式。
LONG DoIO( struct StdIOReq *req (A1) ); 啟動並等待 IO 請求完成。(內部呼叫 SendIO()/ WaitIO())。
LONG SendIO( struct StdIOReq *req (A1) ); 向裝置傳送請求,但不等待回覆。內部呼叫裝置的 BeginIO() 入口。
req 被推入堆疊,用於 C 語言 BeginIO() 處理程式。
struct StdIOReq *CheckIO( struct StdIOReq *req (A1) ); 檢查請求是否已返回。僅檢查 mn_Node.ln_Type == NT_REPLYMSG。
LONG WaitIO( struct StdIOReq *req (A1) ); 等待請求完成並將其從回覆埠中刪除。不要在未傳送的請求上呼叫 WaitIO()!
void AbortIO( struct StdIOReq *req (A1) ); 嘗試透過內部呼叫裝置的 AbortIO() 入口來中止請求的處理。裝置需要實現此功能。您應該在 AbortIO() 之後呼叫 WaitIO()。
req 被推入堆疊,用於 C 語言 AbortIO() 處理程式。
void AddLibrary( struct Library *library (A1) ); 將一個庫新增到系統中。庫應該正確初始化並準備好接收 Open() 呼叫。
struct Library *RemLibrary( struct Library *library (A1) ); 從系統中移除一個庫。如果庫當前被任何使用者開啟,則呼叫返回 NULL。
void InitLibFuncs( struct Library *lib (A0), ULONG *funcs (A1), ULONG num (D0) ); 將庫呼叫向量初始化為 funcs 陣列中指定的地址。陣列的第一個元素成為偏移量 -6 中跳轉的地址,下一個成為偏移量 -12 中的地址,以此類推。庫結構應該具有至少 6*num 的負大小,以確保此呼叫不會覆蓋任何內容。
struct Library *OpenLibrary( char *name (A1) ); 開啟指定的庫。如果庫不存在或無法開啟,則返回 NULL。否則返回庫的庫基址。在內部,庫的 Open() 入口被呼叫,並檢查返回值以確定開啟是否成功。庫本身維護 lib_OpenCnt。
void CloseLibrary( struct Library *library (A1) ); 關閉之前開啟的庫。在內部,庫的 Close() 入口被呼叫。庫本身維護 lib_OpenCnt。
ULONG SetFunction( struct Library *library (A1), LONG offset (D0), ULONG val (D1) ); 將指定的庫呼叫向量更改為指向新的例程。返回向量的老值。offset 是呼叫庫時使用的負呼叫偏移量。
不要嘗試修補 Disable()/Enable() 或者 Forbid()/Permit()。
BOAR 時鐘執行 UTC (GMT) 時間。shell 變數 TIMEZONE 應該用於進行調整以獲得本地時間。 void DateStamp( struct DateStamp *date (A0) ); 將返回當前系統時間。這是一個原子操作,不需要停用中斷。
void SetDate( struct DateStamp *date (A0) ); 將設定新的系統時間。這是一個原子操作,不需要停用中斷。
訊號量函式使用 C 語言呼叫約定(引數在堆疊中)。 void InitSemaphore( struct SignalSemaphore *sem ); 初始化一個用於使用的私有訊號量。
void AddSemaphore( struct SignalSemaphore *sem, const char *name, BYTE pri ); 將一個訊號量新增到系統的公共訊號量列表中。所有初始化都由 AddSemaphore() 完成。
void RemSemaphore( struct SignalSemaphore *sem ); 從系統的公共訊號量列表中移除一個訊號量。
注意:訊號量上可能仍然存在鎖,並且可能還有待處理的請求。要移除一個公共訊號量,請使用引用計數,如下所示
Forbid();
while(1)
{
if(sem->ss_Public == 0)
{
RemSemaphore(sem);
break;
}
/* Delay() breaks Forbid() state */
Delay(100);
}
Permit();
struct SignalSemaphore *FindSemaphore( const char *name ); 在系統的公共訊號量列表中搜索一個訊號量。如果找不到指定的訊號量,則返回 NULL。使用 FreeSemaphore() 來放棄獲取的訊號量控制代碼。
請記住,系統不保證公共訊號量名稱是唯一的。
void FreeSemaphore( struct SignalSemaphore *reference ); 結束對透過 FindSemaphore() 獲取的公共訊號量的使用。
LONG AttemptSemaphore( struct SignalSemaphore *sem ); 嘗試在不阻塞的情況下獲取訊號量的獨佔鎖。失敗返回零,成功返回非零值。
LONG AttemptSemaphoreShared( struct SignalSemaphore *sem ); 嘗試在不阻塞的情況下獲取訊號量的共享鎖。失敗返回零,成功返回非零值。
void ObtainSemaphore( struct SignalSemaphore *sem ); 獲取訊號量的獨佔(寫)鎖。阻塞(等待)直到獲取鎖。
void ObtainSemaphoreShared( struct SignalSemaphore *sem ); 獲取訊號量的共享(讀)鎖。阻塞(等待)直到獲取鎖。
void ReleaseSemaphore( struct SignalSemaphore *sem ); 釋放獲取的訊號量鎖,無論是共享的還是獨佔的。
我還檢查了 AmigaOS v4 SDK,並找到了以下函式
exec.library/GetCPUInfo exec.library/GetCPUInfo
NAME
GetCPUInfo -- Get information about the current CPU (V50)
SYNOPSIS
void GetCPUInfo(struct TagItem *tagList);
void GetCPUInfoTags(ULONG tag1, ...);
FUNCTION
This function is used to retrieve information about the CPU(s)
installed.
This function replaces the ExecBase attention flag mechanism.
INPUTS
Input to this function is a tag list containing the items to
be queried. Each tag item's data must point to a sufficiently
large storage where the result is copied to. The list of tag
items below lists the size of the required storage in
brackets. For example, GCIT_NumberOfCPUs requires a pointer
to an uint32, GCIT_ProcessorSpeed requires a pointer to a
variable which is of type uint64.
Currently, the following items are available:
GCIT_NumberOfCPUs (uint32 *)
Number of CPUs available in the system. This is likely to
be 1 at the moment.
GCIT_Family (uint32 *)
CPU family as a symbolic constant. Currently, these are
defined:
CPUFAMILY_UNKNOWN - Unknown CPU
CPUFAMILY_60X - All PowerPC 60x, like 603 and 604
CPUFAMILY_7X0 - All G3 PowerPC 7X0, like 740, 750,
750CXe, 750FX
CPUFAMILY_74xx - All G4 PowerPC 74xx, like 7400, 7410,
7441
GCIT_Model (uint32 *)
CPU model as a symbolic constant. Currently, these are
defined:
CPUTYPE_UNKNOWN - Unknown CPU
CPUTYPE_PPC603E - PowerPC 603e
CPUTYPE_PPC604E - PowerPC 604e
CPUTYPE_PPC750CXE - PowerPC 750CXe
CPUTYPE_PPC750FX - PowerPC 750FX
CPUTYPE_PPC750GX - PowerPC 750GX
CPUTYPE_PPC7410 - PowerPC 7410
CPUTYPE_PPC74XX_VGER - PowerPC 7440, 7441, 7450, 7451 (Vger types)
CPUTYPE_PPC74XX_APOLLO - PowerPC 7445, 7447, 7455, 7457 (Apollo 6/7 types)
GCIT_ModelString (CONST_STRPTR *)
CPU model as a read-only string. For example, the 604e would be
returned as "PowerPC 604e".
GCIT_Version (uint32 *)
CPU version and revision. The major and minor numbers are
returned as a number with the lower 16 bit as 0xVV.R, where
VV is the version number, and R is the revision. For
example, on a PPC750FX DD2, the result would be 0x0201,
depicting a PowerPC 750FX V2.1.
Note: If a version is not available, the value returned is
0.
GCIT_VersionString (CONST_STRPTR *)
CPU version and revision as a read-only string, in the form
"major.minor".
GCIT_FrontsideSpeed (uint64 *)
CPU frontside bus speed.
Note: This is actually a 64 bit number.
GCIT_ProcessorSpeed (uint64 *)
CPU internal frequency.
Note: This is actually a 64 bit number.
GCIT_L1CacheSize (uint32 *)
GCIT_L2CacheSize (uint32 *)
GCIT_L3CacheSize (uint32 *)
Size of the appropriate cache, if available, otherwise 0.
GCIT_CacheLineSize (uint32 *)
Size of a cache line. Note that this is also the alignment used
by CacheClearE/CacheClearU.
GCIT_VectorUnit (uint32 *)
CPU's vector unit, as a symbolic constant. Currently
defined are:
VECTORTYPE_NONE - No vector unit
VECTORTYPE_ALTIVEC - Motorola AltiVec (tm) Unit
(VECTORTYPE_VMX - IBM VMX Unit)
GCIT_Extensions (CONST_STRPTR *)
CPU feature string. The result is a read-only string that
describes nonstandard features of the CPU.
GCIT_CPUPageSize (uint32 *)
GCIT_ExecPageSize (uint32 *)
Physical and logical page sizes. CPUPageSize determines the
supported sizes of the CPU, while ExecPageSize determines the
supported Exec (i.e. "virtual" page sizes).The latter is the size
supported by Exec API functions.
In general, these tags return a bit mask. If bit n is set, a page
size of 2^n is supported.
For example, GCIT_CPUPageSize might return 0x1000, i.e. bit 12
is set, hence the CPU supports hardware pages of 4096 bytes.
GCIT_TimeBaseSpeed (uint64 *)
Speed of the CPU timer.
Note: This is actually a 64 bit number.
EXAMPLE
/* Query model and version */
CONST_STRPTR Model;
CONST_STRPTR Version;
IExec->GetCPUInfoTags(
GCIT_ModelString, &Model,
GCIT_VersionString, &Version,
TAG_DONE);
printf("CPU: %s V%s\n", Model, Version);
MorphOS 在 exec.library 中具有此函式
exec.library/NewGetSystemAttrsA
NAME
NewGetSystemAttrsA -- Get Exec Info (V50)
NewGetSystemAttrs -- Varargs Stub for NewGetSystemAttrsA()
SYNOPSIS
Result NewGetSystemAttrsA(Data,DataSize,Type,Tags )
D0 A0 D0 D1 A1
ULONG NewGetSystemAttrsA(void*,ULONG,ULONG,struct TagItem*);
ULONG NewGetSystemAttrs(void*,ULONG,ULONG,...);
FUNCTION
Allows you to get informations about the system like cpu type,
caches and so on.
INPUTS
Data - Ptr to a buffer
DataSize - size of the buffer
Type - Information Type
Tags - Additional argument buffer for the type
o SYSTEMINFOTYPE_SYSTEM
returns the System family.
Tags: none
Data: String[DataSize]
o SYSTEMINFOTYPE_VENDOR
returns the System Vendor.
Tags: none
Data: String[DataSize]
o SYSTEMINFOTYPE_REVISION
returns the System revision *string*. Sorry, this is Openfirmware
heritage.
Tags: none
Data: String[DataSize]
o SYSTEMINFOTYPE_MAGIC1
returns the Magic1 field in ExecBase.
Tags: none
Data: u_int32_t
o SYSTEMINFOTYPE_MAGIC2
returns the Magic2 field in ExecBase.
Tags: none
Data: u_int32_t
o SYSTEMINFOTYPE_MACHINE
returns the CPU family. Currently only PowerPC
Tags: none
Data: u_int32_t
o SYSTEMINFOTYPE_PAGESIZE
returns the mmu page size which is needed for mmu related routines.
Can return 0 if no mmu is there in some embedded systems.
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CPUVERSION
returns the CPU type. Depends on CPU family.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CPUREVISION
returns the CPU revision. Depends on CPU family.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CPUCLOCK
returns the CPU clock.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int64_t
o SYSTEMINFOTYPE_PPC_BUSCLOCK
returns the CPU bus clock.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int64_t
o SYSTEMINFOTYPE_PPC_CACHEL1TYPE
returns the CPU L1 Type.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CACHEL1FLAGS
returns the CPU L1 Flags.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL1SIZE
returns the CPU L1 instruction cache size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL1LINES
returns the CPU L1 instruction cache line count.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL1LINESIZE
returns the CPU L1 instruction cache line size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL1SIZE
returns the CPU L1 data cache size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL1LINES
returns the CPU L1 data cache line count.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL1LINESIZE
returns the CPU L1 data cache line size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CACHEL2TYPE
returns the CPU L2 Type.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CACHEL2FLAGS
returns the CPU L2 Flags.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL2SIZE
returns the CPU L2 instruction cache size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL2LINES
returns the CPU L2 instruction cache line count.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL2LINESIZE
returns the CPU L2 instruction cache line size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL2SIZE
returns the CPU L2 data cache size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL2LINES
returns the CPU L2 data cache line count.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL2LINESIZE
returns the CPU L2 data cache line size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CACHEL3TYPE
returns the CPU L3 Type.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CACHEL3FLAGS
returns the CPU L3 Flags.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL3SIZE
returns the CPU L3 instruction cache size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL3LINES
returns the CPU L3 instruction cache line count.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ICACHEL3LINESIZE
returns the CPU L3 instruction cache line size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL3SIZE
returns the CPU L3 data cache size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL3LINES
returns the CPU L3 data cache line count.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DCACHEL3LINESIZE
returns the CPU L3 data cache line size.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_FPU
returns >0 if the CPU supports FPU instructions.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_ALTIVEC
returns >0 if the CPU supports Altivec instructions.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_PERFMONITOR
returns 1 if the CPU supports the Performance Monitor Unit.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DATASTREAM
returns 1 if the CPU supports datastream instructions.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_RESERVATIONSIZE
returns the alignment size of the reservation instructions like lwarx.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_BUSTICKS
returns the bus ticks the cpu needs to increase the timer.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_CPUTEMP
returns the cpu temperature in 8.24 fixedpoint celcius degrees.
might not be implemented for all cpu types and MorphOS versions.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_PPC_DABR
returns 1 if the CPU supports Data Address Breakpoint Register.
Tags: SYSTEMINFOTAG_CPUINDEX (optional) for the CPU Number
Data: u_int32_t
o SYSTEMINFOTYPE_TBCLOCKFREQUENCY
returns the timebase clock frequency used for system timing.
this is the same value as returned by timer.device/ReadCPUClock
base.
Data: u_int32_t
o SYSTEMINFOTYPE_UPTIMETICKS
returns the total system uptime in timebase ticks.
Data: u_int64_t
o SYSTEMINFOTYPE_LASTSECTICKS
returns number of timebase ticks for 'lastsec' measurement.
To get accurate measurements, you should Forbid(), read this
tag value, read other *LASTSEC* values, Permit(). Then do
the math.
Data: u_int64_t
o SYSTEMINFOTYPE_RECENTTICKS
returns number of timebase ticks for 'recent' measurement.
To get accurate measurements, you should Forbid(), read this
tag value, read other *RECENT* values, Permit(). Then do the
math.
Data: u_int64_t
o SYSTEMINFOTYPE_CPUTIME
returns the total system cpu usage in timebase ticks for
SYSTEMINFOTYPE_UPTIMETICKS time.
Data: u_int64_t
o SYSTEMINFOTYPE_LASTSECCPUTIME
returns the system cpu usage in timebase ticks for
SYSTEMINFOTYPE_LASTSECTICKS time.
Data: u_int64_t
o SYSTEMINFOTYPE_RECENTCPUTIME
returns the decaying average system cpu usage in timebase ticks
for SYSTEMINFOTYPE_RECENTTICKS time.
Data: u_int64_t
o SYSTEMINFOTYPE_VOLUNTARYCSW
returns the total number of voluntary task context switches
(task called Wait(), or RemTask()ed self).
Data: u_int64_t
o SYSTEMINFOTYPE_INVOLUNTARYCSW
returns the total number of involuntary task context switches
(task was busy and ran for Quantum slice and other task at the
same priority was made running, or higher priority task appeared
and was made running).
Data: u_int64_t
o SYSTEMINFOTYPE_LASTSECVOLUNTARYCSW
returns the number of voluntary task context switches for
SYSTEMINFOTYPE_LASTSECTICKS time.
Data: u_int32_t
o SYSTEMINFOTYPE_LASTSECINVOLUNTARYCSW
returns the number of involuntary task context switches for
SYSTEMINFOTYPE_LASTSECTICKS time.
Data: u_int32_t
o SYSTEMINFOTYPE_LOADAVG1
returns the average system load for the last minute.
The returned value is 10.11 fixedpoint value. To get floating
point value use: (load / 2048.0f);
Data: u_int32_t
o SYSTEMINFOTYPE_LOADAVG2
returns the average system load for the last three minutes.
The returned value is 10.11 fixedpoint value. To get floating
point value use: (load / 2048.0f);
Data: u_int32_t
o SYSTEMINFOTYPE_LOADAVG3
returns the average system load for the last fifteen minutes.
The returned value is 10.11 fixedpoint value. To get floating
point value use: (load / 2048.0f);
Data: u_int32_t
o SYSTEMINFOTYPE_TASKSCREATED
returns the total number of tasks created.
Data: u_int64_t
o SYSTEMINFOTYPE_TASKSFINISHED
returns the total number of tasks deleted.
Data: u_int64_t
o SYSTEMINFOTYPE_TASKSRUNNING
returns the total number of running tasks.
Data: u_int32_t
o SYSTEMINFOTYPE_TASKSLEEPING
returns the total number of waiting tasks.
Data: u_int32_t
o SYSTEMINFOTYPE_LAUNCHTIMETICKS
returns the timebase for system (sheduler) startup, starting
from 0.
Data: u_int64_t
o SYSTEMINFOTYPE_LAUNCHTIMETICKS1978
returns the timebase for system (sheduler) startup, starting
from Jan 1st 1978. this is useful for formatting system boottime
with dos/DateToStr.
Data: u_int64_t
o SYSTEMINFOTYPE_EMULHANDLESIZE
returns the emulhandle's size.
Data: u_int32_t
o SYSTEMINFOTYPE_EXCEPTIONMSGPORT
returns the global native exception handler's msgport.
Data: u_int32_t
o SYSTEMINFOTYPE_TASKEXITCODE
returns the global native task's exitcode.
Data: u_int32_t
o SYSTEMINFOTYPE_TASKEXITCODE_M68K
returns the global m68k task's exitcode.
Data: u_int32_t
o SYSTEMINFOTYPE_EMULATION_START
returns the address of the abox emulation area.
Data: u_int32_t
o SYSTEMINFOTYPE_EMULATION_SIZE
returns the size of the abox emulation area.
Data: u_int32_t
o SYSTEMINFOTYPE_MODULE_START
returns the address of the module area.
Data: u_int32_t
o SYSTEMINFOTYPE_MODULE_SIZE
returns the size of the module area.
Data: u_int32_t
o SYSTEMINFOTYPE_EXCEPTIONMSGPORT
returns the native Machine Exception MsgPort
Data: struct MsgPort*
o SYSTEMINFOTYPE_EXCEPTIONMSGPORT_68K
returns the 68K Exception MsgPort. This is the msgport the
default system 68k traphandler sends its msgs. If the system
68k traphandler is replaced msgs to this exception msgport
aren't guranteed. Usually the msgport is controlled by the
ABox Log Server.
Data: struct MsgPort*
o SYSTEMINFOTYPE_ALERTMSGPORT
returns the Alert MsgPort.
Usually the msgport is controlled by the ABox Log Server.
Data: struct MsgPort*
o SYSTEMINFOTYPE_MAXHITCOUNT
returns the maxhit default for tasks.
Data: ULONG
o SYSTEMINFOTYPE_MAXALERTCOUNT
returns the max alerts default for tasks.
Data: ULONG
o SYSTEMINFOTYPE_REGUSER
returns the registered user
Tags: none
Data: String[DataSize]
o SYSTEMINFOTYPE_FREEBLOCKS
returns information about the free memory blocks
Tags: SYSTEMINFOTAG_MEMHEADER (optional) to specify single
memheader, defaults to all system memory
SYSTEMINFOTAG_HOOK (optional) call hook rather than
filling array. See exec/system.h
Data: struct MemEntry []
RESULT
Result is the amount of bytes returned at Data.
如果您有興趣實現它,我會向您提供其餘的資訊(LVO 和定義)。但是請注意,目前除了 PPC 之外的其他 CPU 不存在任何定義。
- 它在 Exec 裝置中使用 DOS 錯誤程式碼。
- 它添加了一個義大利麵條式的 goto 語句。
- 它將 iotd 轉換為它本來就有的型別。
- 其他一些看似無意義的程式碼更改,例如用 iotd 引數替換 eject 引數,在單個項周圍新增括號等等。