mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
exec_os: enrich all stubs to bootcamp-quality reference articles
Complete rewrite of 14 exec_os articles from stubs to comprehensive deep-dive technical references with architecture diagrams, pitfalls, and best practices. New: multitasking.md (scheduler, IPC, memory safety, real-world scenarios) Enriched: exec_base, tasks_processes, library_system, library_vectors, interrupts, exceptions_traps, memory_management, message_ports, signals, semaphores, io_requests, lists_nodes, resident_modules Updated indexes: 06_exec_os/README.md, root README.md
This commit is contained in:
parent
4d136b0672
commit
59929047d4
16 changed files with 4463 additions and 678 deletions
|
|
@ -1,18 +1,55 @@
|
|||
[← Home](../README.md) · [Exec Kernel](README.md)
|
||||
|
||||
# Library System — OpenLibrary Lifecycle
|
||||
# Library System — OpenLibrary Lifecycle, Version Management
|
||||
|
||||
## Overview
|
||||
|
||||
The AmigaOS library system provides **versioned, shared code** via a standardised interface. Libraries are identified by name, opened with a version check, and reference-counted for safe unloading.
|
||||
The AmigaOS library system provides **versioned, shared code** via a standardised interface. Libraries are identified by name, opened with a version check, and reference-counted for safe unloading. This system is the backbone of the Amiga's modular architecture — everything from `dos.library` to `intuition.library` to third-party libraries uses the same open/close/expunge lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Application"
|
||||
APP["OpenLibrary('dos.library', 40)"]
|
||||
end
|
||||
|
||||
subgraph "Exec"
|
||||
SCAN["Scan SysBase→LibList"]
|
||||
DISK["Search LIBS: path"]
|
||||
LOAD["LoadSeg + RomTag init"]
|
||||
INIT["Call library Open vector"]
|
||||
end
|
||||
|
||||
subgraph "Library"
|
||||
LIB["struct Library<br/>JMP table + base data"]
|
||||
OPEN["Open: lib_OpenCnt++"]
|
||||
CLOSE["Close: lib_OpenCnt--"]
|
||||
EXPUNGE["Expunge: if OpenCnt==0,<br/>unload and free"]
|
||||
end
|
||||
|
||||
APP --> SCAN
|
||||
SCAN -->|Found| INIT
|
||||
SCAN -->|Not found| DISK
|
||||
DISK -->|Found on disk| LOAD
|
||||
LOAD --> INIT
|
||||
INIT --> OPEN
|
||||
OPEN --> LIB
|
||||
|
||||
style APP fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
style LIB fill:#e8f5e9,stroke:#4caf50,color:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Library Node
|
||||
|
||||
Every library is an `NT_LIBRARY` node on `SysBase->LibList`:
|
||||
Every library is an `NT_LIBRARY` node on `SysBase→LibList`:
|
||||
|
||||
```c
|
||||
/* exec/libraries.h — NDK39 */
|
||||
struct Library {
|
||||
struct Node lib_Node; /* ln_Name = "dos.library" */
|
||||
UBYTE lib_Flags; /* LIBF_SUMUSED | LIBF_DELEXP */
|
||||
|
|
@ -27,28 +64,76 @@ struct Library {
|
|||
};
|
||||
```
|
||||
|
||||
### Field Reference
|
||||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `lib_Node.ln_Name` | Library name used for `OpenLibrary()` lookup |
|
||||
| `lib_Flags` | `LIBF_SUMUSED`, `LIBF_CHANGED`, `LIBF_DELEXP` |
|
||||
| `lib_NegSize` | Total size of the JMP table (negative offsets from base) |
|
||||
| `lib_PosSize` | Size of the library base structure (positive offsets) |
|
||||
| `lib_Version` | Major version number — checked by `OpenLibrary()` |
|
||||
| `lib_Revision` | Minor revision — informational, not checked at open time |
|
||||
| `lib_IdString` | Human-readable ID string with date |
|
||||
| `lib_Sum` | Checksum of the JMP table (for integrity verification) |
|
||||
| `lib_OpenCnt` | Number of active openers — library can't expunge while > 0 |
|
||||
|
||||
### Library Memory Layout
|
||||
|
||||
```
|
||||
JMP table (lib_NegSize bytes)
|
||||
┌─────────────────────────────┐
|
||||
base - N×6: │ JMP function_N │
|
||||
│ ... │
|
||||
base - 24: │ JMP Reserved │
|
||||
base - 18: │ JMP Expunge │
|
||||
base - 12: │ JMP Close │
|
||||
base - 6: │ JMP Open │
|
||||
├─────────────────────────────┤
|
||||
base + 0: ───→ │ struct Library (header) │ ← OpenLibrary returns this
|
||||
│ (library-specific data...) │
|
||||
base + PosSize: │ (end of base struct) │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OpenLibrary / CloseLibrary
|
||||
|
||||
### Opening
|
||||
|
||||
```c
|
||||
/* Open — get a reference: */
|
||||
struct DosLibrary *DOSBase =
|
||||
(struct DosLibrary *)OpenLibrary("dos.library", 40);
|
||||
|
||||
/* Use the library ... */
|
||||
|
||||
/* Close — release reference: */
|
||||
CloseLibrary((struct Library *)DOSBase);
|
||||
if (!DOSBase)
|
||||
{
|
||||
/* Library not found, or version too old */
|
||||
/* This is how you enforce minimum OS version requirements */
|
||||
}
|
||||
```
|
||||
|
||||
Internally:
|
||||
1. `exec` scans `LibList` for `ln_Name == "dos.library"`
|
||||
2. If not found, searches resident list and `LIBS:` path
|
||||
3. If found on disk: `LoadSeg` + call `InitLib`
|
||||
4. Check `lib_Version >= requested_version`
|
||||
5. Call library's `Open()` vector → `lib_OpenCnt++`
|
||||
6. Return library base
|
||||
### What OpenLibrary Does
|
||||
|
||||
1. **Scan `SysBase→LibList`** for a node whose `ln_Name` matches
|
||||
2. **If not found**: search the resident module list (`FindResident`)
|
||||
3. **If not resident**: search `LIBS:` assign path, `LoadSeg` the file, find RomTag, initialise
|
||||
4. **Check version**: `lib_Version >= requestedVersion`?
|
||||
5. **Call library's `Open()` vector** — library-specific initialisation, `lib_OpenCnt++`
|
||||
6. **Return** library base pointer (or NULL on failure)
|
||||
|
||||
### Closing
|
||||
|
||||
```c
|
||||
CloseLibrary((struct Library *)DOSBase);
|
||||
DOSBase = NULL; /* Good practice — prevent use-after-close */
|
||||
```
|
||||
|
||||
### What CloseLibrary Does
|
||||
|
||||
1. **Call library's `Close()` vector** — `lib_OpenCnt--`
|
||||
2. **If `lib_OpenCnt == 0` and `LIBF_DELEXP` is set**: call `Expunge()` to unload
|
||||
3. **If `Expunge()` returns a segment list**: `UnLoadSeg` to free the code
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -56,22 +141,101 @@ Internally:
|
|||
|
||||
| Flag | Value | Meaning |
|
||||
|---|---|---|
|
||||
| `LIBF_SUMUSED` | 0x01 | Checksum is maintained |
|
||||
| `LIBF_CHANGED` | 0x02 | Checksum needs recalculation |
|
||||
| `LIBF_DELEXP` | 0x04 | Expunge deferred (opened while expunge pending) |
|
||||
| `LIBF_SUMUSED` | `$01` | JMP table checksum is maintained — exec verifies on close |
|
||||
| `LIBF_CHANGED` | `$02` | Checksum needs recalculation (after `SetFunction`) |
|
||||
| `LIBF_DELEXP` | `$04` | Deferred expunge — will expunge when last opener closes |
|
||||
|
||||
---
|
||||
|
||||
## The Expunge Lifecycle
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as Application
|
||||
participant Exec as exec.library
|
||||
participant Lib as Library
|
||||
|
||||
App->>Exec: CloseLibrary(base)
|
||||
Exec->>Lib: Call Close() vector
|
||||
Lib->>Lib: lib_OpenCnt--
|
||||
|
||||
alt lib_OpenCnt == 0
|
||||
Exec->>Lib: Call Expunge() vector
|
||||
alt No other openers
|
||||
Lib->>Lib: Remove from LibList
|
||||
Lib->>Lib: Free JMP table + base
|
||||
Lib->>Exec: Return segment list
|
||||
Exec->>Exec: UnLoadSeg(seglist)
|
||||
else Memory pressure only
|
||||
Lib->>Lib: Set LIBF_DELEXP
|
||||
Lib->>Exec: Return NULL (defer)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### When Expunge Happens
|
||||
|
||||
- **Automatic**: When `CloseLibrary` drops `lib_OpenCnt` to 0 and memory is needed
|
||||
- **System pressure**: Exec calls `Expunge()` on all libraries when `AllocMem` fails (trying to reclaim memory)
|
||||
- **Manual**: `RemLibrary()` requests expunge regardless of open count
|
||||
|
||||
### Implementing Expunge in a Library
|
||||
|
||||
```c
|
||||
BPTR __saveds LibExpunge(void)
|
||||
{
|
||||
struct MyLibBase *base = (struct MyLibBase *)REG_A6;
|
||||
|
||||
if (base->lib_OpenCnt > 0)
|
||||
{
|
||||
/* Can't unload — still in use */
|
||||
base->lib_Flags |= LIBF_DELEXP;
|
||||
return 0; /* Signal: deferred */
|
||||
}
|
||||
|
||||
/* Remove from system list */
|
||||
Remove(&base->lib_Node);
|
||||
|
||||
/* Free library-specific resources */
|
||||
FreeMyResources(base);
|
||||
|
||||
/* Free the library base + JMP table */
|
||||
BPTR segList = base->segList;
|
||||
ULONG negSize = base->lib_NegSize;
|
||||
ULONG posSize = base->lib_PosSize;
|
||||
FreeMem((UBYTE *)base - negSize, negSize + posSize);
|
||||
|
||||
return segList; /* Exec calls UnLoadSeg on this */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Version Numbering Convention
|
||||
|
||||
`lib_Version.lib_Revision`:
|
||||
- `40.1` = OS 3.1 release
|
||||
- `40.x` = OS 3.1 (various revisions)
|
||||
- `44.x` = OS 3.2
|
||||
| Version | OS Release | Example Libraries |
|
||||
|---|---|---|
|
||||
| 33.x | OS 1.2 | exec 33.180, dos 33.124 |
|
||||
| 34.x | OS 1.3 | exec 34.2, dos 34.75 |
|
||||
| 36.x | OS 2.0 | exec 36.174, dos 36.68 |
|
||||
| 37.x | OS 2.04 | exec 37.175, dos 37.10 |
|
||||
| 39.x | OS 3.0 | exec 39.46, dos 39.22 |
|
||||
| 40.x | OS 3.1 | exec 40.70, dos 40.42 |
|
||||
| 44.x | OS 3.1.4 | exec 44.5 |
|
||||
| 45.x | OS 3.2 | exec 45.20 |
|
||||
| 47.x | OS 3.2.2 | exec 47.3 |
|
||||
|
||||
Increment rules:
|
||||
- `lib_Revision` — minor bugfix, compatible
|
||||
- `lib_Version` — API change or major update (requestors check this)
|
||||
### Version Check Pattern
|
||||
|
||||
```c
|
||||
/* Require OS 3.0+ features */
|
||||
struct Library *base = OpenLibrary("intuition.library", 39);
|
||||
if (!base)
|
||||
{
|
||||
/* Display error using OS 1.x compatible methods */
|
||||
/* Can't use features that require V39+ */
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -80,18 +244,68 @@ Increment rules:
|
|||
```c
|
||||
/* Read-only peek — no open count increment */
|
||||
Forbid();
|
||||
struct Library *lib = FindName(&SysBase->LibList, "graphics.library");
|
||||
struct Library *lib = (struct Library *)
|
||||
FindName(&SysBase->LibList, "graphics.library");
|
||||
if (lib)
|
||||
{
|
||||
Printf("Found: %s V%ld.%ld (open: %ld)\n",
|
||||
lib->lib_Node.ln_Name,
|
||||
lib->lib_Version,
|
||||
lib->lib_Revision,
|
||||
lib->lib_OpenCnt);
|
||||
}
|
||||
Permit();
|
||||
if (lib) printf("Found v%d\n", lib->lib_Version);
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Using `FindName` without `Forbid()` is a race condition — the library could be expunged between finding it and using it.
|
||||
> **Caution**: The returned pointer is only valid inside the `Forbid()` section. After `Permit()`, the library could be expunged. If you need to use it, call `OpenLibrary()` instead.
|
||||
|
||||
---
|
||||
|
||||
## Pitfalls
|
||||
|
||||
### 1. Using Library After Close
|
||||
|
||||
```c
|
||||
DOSBase = OpenLibrary("dos.library", 40);
|
||||
/* ... */
|
||||
CloseLibrary(DOSBase);
|
||||
Open("RAM:test", MODE_NEWFILE); /* CRASH — DOSBase is closed */
|
||||
```
|
||||
|
||||
### 2. Not Checking OpenLibrary Return
|
||||
|
||||
```c
|
||||
IntuitionBase = OpenLibrary("intuition.library", 99);
|
||||
/* IntuitionBase is NULL — V99 doesn't exist */
|
||||
OpenWindowTags(NULL, ...); /* Guru — calling through NULL base */
|
||||
```
|
||||
|
||||
### 3. Version Mismatch
|
||||
|
||||
```c
|
||||
/* Opened V36 but calling a V39 function */
|
||||
OpenLibrary("exec.library", 36);
|
||||
CreatePool(...); /* CreatePool was added in V39 — calling garbage */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check** the return value of `OpenLibrary()` — NULL means failure
|
||||
2. **Request the minimum version** you actually need — don't over-specify
|
||||
3. **Close in reverse order** of opening — prevents dangling references
|
||||
4. **Set base pointer to NULL** after `CloseLibrary()` — catches use-after-close
|
||||
5. **Use `OpenLibrary` for runtime version detection** — it's cleaner than checking `lib_Version` manually
|
||||
6. **Don't use `FindName` as a substitute** for `OpenLibrary` — it doesn't bump the reference count
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- NDK39: `exec/libraries.h`
|
||||
- ADCD 2.1: `OpenLibrary`, `CloseLibrary`, `FindName`
|
||||
- [shared_libraries_runtime.md](../04_linking_and_libraries/shared_libraries_runtime.md) — expunge lifecycle
|
||||
- NDK39: `exec/libraries.h`, `exec/resident.h`
|
||||
- ADCD 2.1: `OpenLibrary`, `CloseLibrary`, `MakeLibrary`, `RemLibrary`, `FindName`
|
||||
- See also: [Library Vectors](library_vectors.md) — JMP table and LVO details
|
||||
- See also: [Resident Modules](resident_modules.md) — how libraries are found in ROM
|
||||
- See also: [Shared Libraries Runtime](../04_linking_and_libraries/shared_libraries_runtime.md) — full expunge lifecycle
|
||||
- *Amiga ROM Kernel Reference Manual: Exec* — libraries chapter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue