Overview
Comment: | add kmlini() and kmlina() functions; restructure allocation functions to work more reasonably (returning a tuple struct instead of making a user pass in a void**); update docs accordingly |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
acb4a9944e5a48cbab044b5ec26e8224 |
User & Date: | lexi on 2019-08-22 08:44:29 |
Other Links: | manifest | tags |
Context
2019-08-22
| ||
08:45 | check in missing files check-in: 269baab90a user: lexi tags: trunk | |
08:44 | add kmlini() and kmlina() functions; restructure allocation functions to work more reasonably (returning a tuple struct instead of making a user pass in a void**); update docs accordingly check-in: acb4a9944e user: lexi tags: trunk | |
04:31 | finish moving heap allocation/free functions to the posix syscall apparatus and deprecate the direct assembly implementations of platform_mmap; update the kmem docs to match new function signatures (and remove typos) check-in: 709ffb094d user: lexi tags: trunk | |
Changes
Modified arch/posix/syscalls from [24a70b57fd] to [f436d656b4].
1 2 3 4 5 |
exit read write mmap munmap |
> |
1 2 3 4 5 6 |
exit
read
write
mmap
munmap
brk
|
Modified mod/kcore/testbin.exe.c from [ae0ed8cd11] to [2c9c964882].
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
if (kiosend(e.std, ptr, null) == kiocond_ok) { /* great, continue */ } else { return kbad_io; } void* region; kmcond alloc = kmheapa(®ion, 2048); if (alloc != kmcond_ok) return kbad_mem; kmzero(region,2048); if (kmheapf(region) >= kmcond_fail) return kbad_mem; return kbad_ok; } |
< | | | > |
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
if (kiosend(e.std, ptr, null) == kiocond_ok) { /* great, continue */ } else { return kbad_io; } kmres alloc = kmheapa(2048); if (alloc.cond != kmcond_ok) return kbad_mem; void* region = alloc.raw; kmzero(region,2048); if (kmheapf(region) >= kmcond_fail) return kbad_mem; return kbad_ok; } |
Modified mod/kmem/free.fn.c from [cc32756862] to [bac849b7ff].
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
* kmfree() frees memory allocated in any manner.
* it ignores non-dynamically allocated memory,
* returning kmcond_unnecessary. to check for
* success, compare result < kmcond_fail.
*/
kmcond kmfree(kmptr ptr) {
if (ptr.kind <= kmkind_fail) return kmcond_unnecessary;
switch (ptr.kind) {
case kmkind_heap: return kmheapf(ptr.ref);
}
return kmcond_unhandled;
}
|
| |
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
* kmfree() frees memory allocated in any manner.
* it ignores non-dynamically allocated memory,
* returning kmcond_unnecessary. to check for
* success, compare result < kmcond_fail.
*/
kmcond kmfree(kmptr ptr) {
if (ptr.kind <= kmkind_broken) return kmcond_unnecessary;
switch (ptr.kind) {
case kmkind_heap: return kmheapf(ptr.ref);
}
return kmcond_unhandled;
}
|
Modified mod/kmem/heapa.fn.c from [c8ccec879e] to [c08b42ee14].
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
#include <error_table.h> /* we define all our platform functions here, whether or not * they're for the correct platform - only the ones that are * called by the preprocessed form of the code will actually * be linked, linker errors are our friend here! */ kmcond kmheapa(void** where, sz len) { /* allocate an object on the heap and return * a pointer, or NULL if the allocation failed. */ union { void* raw; ubyte* byte; kmbox* header; } region; /* we need to allocate space for the * header and for the actual object */ sz region_size = sizeof(kmbox) + len; # ifdef KFenv_posix /* posix APIs - we've got it easy. currently for * nonlinear heap allocation kmheapa simply uses * m(un)map and lets the kernel worry about it. it * may ultimately be worth replacing this with a * more sophisticated implementation, most likely an ................................................................................ * a value of -1 */ struct k_platform_syscall_answer r = k_platform_syscall (k_platform_syscall_mmap, Kmsz(args), args); if (r.error == 0) region.byte = (ubyte*)r.ret; else { switch (r.error) { case k_platform_error_EAGAIN: return kmcond_bad_lock; case k_platform_error_EINVAL: return kmcond_bad_size; case k_platform_error_EMFILE: return kmcond_too_many; case k_platform_error_ENOMEM: return kmcond_no_room; default: return kmcond_fail_assert; } } # else Knoimpl(kmheapa,KVos); # error missing implementation # endif void* const object = (region.byte + sizeof (kmbox)); region.header -> kind = kmkind_heap; region.header -> size = len; *where = object; return kmcond_ok; } |
|
>
|
|
|
|
|
>
>
>
|
>
|
|
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
#include <error_table.h> /* we define all our platform functions here, whether or not * they're for the correct platform - only the ones that are * called by the preprocessed form of the code will actually * be linked, linker errors are our friend here! */ kmres kmheapa(sz len) { /* allocate an object on the heap and return * a pointer, or NULL if the allocation failed. */ union { void* raw; ubyte* byte; kmbox* header; } region; /* we need to allocate space for the * header and for the actual object */ sz region_size = sizeof(kmbox) + len; kmres reply; # ifdef KFenv_posix /* posix APIs - we've got it easy. currently for * nonlinear heap allocation kmheapa simply uses * m(un)map and lets the kernel worry about it. it * may ultimately be worth replacing this with a * more sophisticated implementation, most likely an ................................................................................ * a value of -1 */ struct k_platform_syscall_answer r = k_platform_syscall (k_platform_syscall_mmap, Kmsz(args), args); if (r.error == 0) region.byte = (ubyte*)r.ret; else { switch (r.error) { case k_platform_error_EAGAIN: reply.cond = kmcond_bad_lock; break; case k_platform_error_EINVAL: reply.cond = kmcond_bad_size; break; case k_platform_error_EMFILE: reply.cond = kmcond_too_many; break; case k_platform_error_ENOMEM: reply.cond = kmcond_no_room; break; default: reply.cond = kmcond_fail_assert; } reply.raw = (void*)0; return reply; } # else Knoimpl(kmheapa,KVos); # error missing implementation # endif void* const object = (region.byte + sizeof (kmbox)); region.header -> kind = kmkind_heap; region.header -> size = len; reply.cond = kmcond_ok; reply.raw = object; return reply; } |
Modified mod/kmem/heapo.fn.c from [70c3679214] to [5d74e5db6e].
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* heapo.fn.c - kmheapo() "allocate heap object" * ~ lexi hale <lexi@hale.su> * kmheapo() allocates a region in heap memory * and returns a kmptr struct referencing that * newly allocated region. */ kmcond kmheapo(kmptr* where, sz size) { void* ptr; kmcond e = kmheapa(&ptr, size); if (e != kmcond_ok) return e; kmptr p = { .kind = (ptr != null ? kmkind_heap : kmkind_fail), .ref = ptr, .shred = false, }; *where = p; return kmcond_ok; } |
| < | > > | > > > > > > | | > | | > |
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/* heapo.fn.c - kmheapo() "allocate heap object" * ~ lexi hale <lexi@hale.su> * kmheapo() allocates a region in heap memory * and returns a kmptr struct referencing that * newly allocated region. */ kmres kmheapo(sz size) { kmres e = kmheapa(size); kmres reply; if (e.cond != kmcond_ok) { reply.cond = e.cond; reply.ptr.ref = (void*)0; reply.ptr.kind = kmkind_broken; return reply; } kmptr p = { .kind = (e.raw != null ? kmkind_heap : kmkind_broken), .ref = e.raw, .shred = false, }; reply.ptr = p; reply.cond = kmcond_ok; return reply; } |
Modified mod/kmem/kmem.md from [0e44a94bf4] to [35f7501adf].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
# kmem **kmem** is a libk module that contains various functions for memory allocation and deallocation. it uses the **short** naming convention with the glyph `m`. # description kmem allocators can work in several different ways. they can allocate memory directly from the heap (like `kmheapa()` and `kmlina()`), use a header that has already been allocated by another function, or allocate memory only from a pre-allocated pool. linear allocation with pool allocation is particularly useful, as it permits the very rapid allocation and deallocation of lots of objects with only a few adjustments to the heap, and no possibility of fragmentation or need for expensive algorithms like `malloc()` or `kmheapa()` # module functions kmem supplies two module-level functions, used to interact with the `kmptr` container type. * `kmfree(kmptr) → kmcond` - free, downref, or ignore the passed object as appropriate * `kmshred(kmptr) → void` - free, downref, or zero the passed object as appropriate. if downref'ing, mark underlying object to be shredded. otherwise, zero its contents, then deallocate if appropriate. ................................................................................ # naming convention kmem function names are based on the **method** of allocation and the **action** being performed. methods are listed in the section below. kmem defines a number of standardized actions, though not every method uses every action. the character listed in brackets is suffixed to the name of the method to produce a function name: for instance, `kmheapa` will allocate memory on the heap, while `kmrefd` will decrement the reference count of its argument. * initialize [i] - initializes a memory store on the heap * initialize fixed [if] - initialize a memory store on the stack or in a fixed-size global * allocate [a] -a llocate a new region of memory of the given size, ready to write, and write a pointer to it into argument `where`. returns a value of `kmcond`; always check this to ensure allocation succeeded. contents of that region undefined. takes parameters `void** where, size_t sz`. * allocate pointer object [o] - like *allocate*, but fills in a `kmptr` instead of a raw `void*`. takes parameters `kmptr* where, size_t sz`. * zero [z] - allocate a new region of memory and zero it before returning it for writing. * zero pointer object [zo] - like *zero*, but returns a `kmptr` instead of a raw `void*`. * free [f] - free a section of memory, either decrementing a reference count or returning it to whatever pool it came from. * shred [s] - destroy whatever was in the segment of memory, then return it to the pool it came from. * destroy [x] - tears down a memory store * upref [u] - increments a reference counter ## methods kmem currently supports the following methods of memory management, along with which methods are defined for it. (note that `a` implies `z` and `f` implies `s`). a method may be excluded from a libk binary by defining the flag `KFmem_no[name]`, e.g. `KFmem_noheap`. the fastest allocator is the linear allocator, which should be sufficient for most simple programs. it allocates and deallocates memory simply by resizing the stack; there is no fragmentation, but objects must be freed in the order they are allocated. however, entire groups of objects can be freed at once at very little cost. * `lin` [iax] - linear heap allocator * `kmlini(void) → void*` - return a pointer to the current top of the heap * `kmlina(size_t) → void*` - allocate space on the heap and increase its size appropriately * `kmlinz(size_t) → void*` - allocate zero-filled space on the heap and increase its size appropriately * `kmlinx(void*) → void*` - returns the top of the heap to the location specified, freeing all memory allocated since the call to kmlini() or `kmlina()` that produced it * `heap` [af] - random heap allocation * `kmheapa(size_t) → void*` - allocate * `kmheapz(size_t) → void*` - zero-allocate * `kmheapao(size_t) → kmptr` - allocate pointer object * `kmheapzo(size_t) → kmptr` - zero-allocate pointer object * `kmheapf(void*) → void` - free * `kmheaps(void*) → void` - shred * `ref` [afu] - reference-counted heap object * `kmrefa(kmcell*, size_t) → void*` - allocate * `kmrefz(kmcell*, size_t) → void*` - zero-allocate * `kmrefao(kmcell*, size_t) → void*` - allocate pointer object * `kmrefzo(kmcell*, size_t) → void*` - zero-allocate pointer object * `kmreff(void*) → void` - downref; free if last ref * `kmrefs(void*) → void` - downref and mark for shred on last ref * `pool` [ixaf] - memory pool * `kmpooli(kmcell*, size_t sz, size_t n) → kmpool*` - initialize a fixed memory pool (a pool of `n` cells of length `sz`) * `kmpoolx(kmpool*) → void` - tear down a memory pool * `kmpoola(kmpool*) → void*` - allocate from pool * `kmpoolz(kmpool*, size_t) → void*` - zero-allocate from pool * `kmpoolao(kmpool*, size_t) → void*` - allocate pointer object * `kmpoolzo(kmpool*, size_t) → void*` - zero-allocate pointer object * `kmpoolf(void*) → void` - downref; free if last ref * `kmpools(void*) → void` - downref and mark for shred on last ref * `tree` [af] - uses a node-child strategy. when a node is freed, all of its children are automatically freed as well. * `kmtreea(kmcell* src, void* parent, size_t) → void*` - create a tree node. if `parent` is NULL, the node will the top of a new tree. if src is null, allocate on-heap. * `kmtreez(kmcell* src, void* parent, size_t) → void*` - like `kmtreea` but zeroed * `kmtreeao(kmcell* src, void* parent, size_t) → kmptr` - like `kmtreea` but returns a `kmptr` * `kmtreezo(kmcell* src, void* parent, size_t) → kmptr` - like `kmtreez` but returns a `kmptr` * `kmtreef(void*) → kmptr` - frees a node and all its children ## macros kmem defines the following macros. * `Kmsz(array)` - a convenience macro to return the number of elements in a static array. inserts the text `( sizeof (array) / sizeof (array) [0] )` * `Kmszs(type, struct)` - a convenience macro to return the size of a struct. requires compound literals. * `Kmszsa(type, array)` - calculates the number of elements in an array of a given struct type. inserts the text `( sizeof ( (type) array ) / sizeof (type) )` * `Kmpsa(type, struct)` - a convenience macro to insert a "pascal struct array" - that is, a struct array prefixed with a size value. this is equivalent to `Kmszsa(type, array), array` |
|
|
|
>
>
|
|
|
|
<
>
|
|
|
|
|
<
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
# kmem **kmem** is a libk module that contains various functions for memory allocation and deallocation. it uses the **short** naming convention with the glyph `m`. # description kmem allocators can work in several different ways. they can allocate memory directly from the heap (like `kmheapa()` and `kmlina()`), use a header that has already been allocated by another function, or allocate memory only from a pre-allocated pool. linear allocation with pool allocation is particularly useful, as it permits the very rapid allocation and deallocation of lots of objects with only a few adjustments to the heap, and no possibility of fragmentation or need for expensive algorithms like `malloc()` or `kmheapa()`. functions that can return a value and an error condition always return a value of type `kmres`, a struct that contains the field `kmcond` with an error code, and one of `raw` (for functions that return raw pointers) or `ptr` (for functions that return `kmptr` objects). # module functions kmem supplies two module-level functions, used to interact with the `kmptr` container type. * `kmfree(kmptr) → kmcond` - free, downref, or ignore the passed object as appropriate * `kmshred(kmptr) → void` - free, downref, or zero the passed object as appropriate. if downref'ing, mark underlying object to be shredded. otherwise, zero its contents, then deallocate if appropriate. ................................................................................ # naming convention kmem function names are based on the **method** of allocation and the **action** being performed. methods are listed in the section below. kmem defines a number of standardized actions, though not every method uses every action. the character listed in brackets is suffixed to the name of the method to produce a function name: for instance, `kmheapa` will allocate memory on the heap, while `kmrefd` will decrement the reference count of its argument. * initialize [i] - initializes a memory store on the heap * initialize fixed [if] - initialize a memory store on the stack or in a fixed-size global * allocate [a] - allocate a new region of memory of the given size, ready to write, and return a struct `kmres` containing an error code and, if the call succeeded, a pointer in the field `raw`. always check the `cond` field of its return value to ensure allocation succeeded. contents of the region specified by the `raw` field are undefined. takes parameter `size_t sz`. * allocate pointer object [o] - like *allocate*, but yields a `kmptr` in the `ptr` field of its return value instead of a raw `void*`. takes parameters `size_t sz`. * zero [z] - allocate a new region of memory and zero it before returning it for writing. * zero pointer object [zo] - like *zero*, but returns a `kmptr` instead of a raw `void*`. * free [f] - free a section of memory, either decrementing a reference count or returning it to whatever pool it came from. * shred [s] - destroy whatever was in the segment of memory, then return it to the pool it came from. * destroy [x] - tears down a memory store * upref [u] - increments a reference counter ## methods kmem currently supports the following methods of memory management, along with which methods are defined for it. (note that `a` implies `z` and `f` implies `s`). a method may be excluded from a libk binary by defining the flag `KFmem_no[name]`, e.g. `KFmem_noheap`. the fastest allocator is the linear allocator, which should be sufficient for most simple programs. it allocates and deallocates memory simply by resizing the stack; there is no fragmentation, but objects must be freed in the order they are allocated. however, entire groups of objects can be freed at once at very little cost. **caveat scriptor:** the linear allocation method is unfortunately complicated by the fact that the methods it uses make it fundamentally incompatible with use in the program of the "malloc" allocator used by libc. this should not be a problem for programs that only link libk, but if they link in any external libraries that depend on libc and which use malloc internally, **the linear allocator cannot be used.** * `lin` [iax] - linear heap allocator * `kmlini` - return a pointer to the current top of the heap * `kmlina` - allocate space on the heap and increase its size appropriately * `kmlinz` - allocate zero-filled space on the heap and increase its size appropriately * `kmlinx` - returns the top of the heap to the location specified, freeing all memory allocated since the call to kmlini() or `kmlina()` that produced it * `heap` [af] - random heap allocation * `kmheapa` - allocate * `kmheapz` - zero-allocate * `kmheapo` - allocate pointer object * `kmheapzo` - zero-allocate pointer object * `kmheapf` - free * `kmheaps` - shred * `ref` [afu] - reference-counted heap object * `kmrefa` - allocate * `kmrefz` - zero-allocate * `kmrefo` - allocate pointer object * `kmrefzo` - zero-allocate pointer object * `kmreff` - downref; free if last ref * `kmrefs` - downref and mark for shred on last ref * `pool` [ixaf] - memory pool * `kmpooli(kmcell*, size_t sz, size_t n) → kmpool*` - initialize a fixed memory pool (a pool of `n` cells of length `sz`) * `kmpoolx` - tear down a memory pool * `kmpoola` - allocate from pool * `kmpoolz` - zero-allocate from pool * `kmpoolo` - allocate pointer object * `kmpoolzo` - zero-allocate pointer object * `kmpoolf` - downref; free if last ref * `kmpools` - downref and mark for shred on last ref * `tree` [af] - uses a node-child strategy. when a node is freed, all of its children are automatically freed as well. * `kmtreea` - create a tree node. if `parent` is NULL, the node will the top of a new tree. if src is null, allocate on-heap. * `kmtreez` - like `kmtreea` but zeroed * `kmtreeao` - like `kmtreea` but returns a `kmptr` * `kmtreezo` - like `kmtreez` but returns a `kmptr` * `kmtreef` - frees a node and all its children ## macros kmem defines the following macros. * `Kmsz(array)` - a convenience macro to return the number of elements in a static array. inserts the text `( sizeof (array) / sizeof (array) [0] )` * `Kmszs(type, struct)` - a convenience macro to return the size of a struct. requires compound literals. * `Kmszsa(type, array)` - calculates the number of elements in an array of a given struct type. inserts the text `( sizeof ( (type) array ) / sizeof (type) )` * `Kmpsa(type, struct)` - a convenience macro to insert a "pascal struct array" - that is, a struct array prefixed with a size value. this is equivalent to `Kmszsa(type, array), array` |
Modified mod/kmem/mem.h from [d8d36f64f5] to [0dc09a3b20].
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
/* when something truly should * never happen: */ kmcond_fail_assert, } kmcond; typedef enum kmkind { kmkind_none, kmkind_fail, kmkind_linear, kmkind_heap, kmkind_pool, kmkind_ref, kmkind_tree } kmkind; ................................................................................ typedef struct kmptr { kmkind kind; kmshred shred; void* ref; } kmptr; /* heap functions */ kmcond kmheapa (void**, sz); kmcond kmheapao(kmptr*, sz); kmcond kmheapf (void*); /* generic functions */ kmcond kmfree(kmptr); kmkind kmtell(void*); void kmzero(void*,sz); void kmozero(kmptr); #ifdef __cplusplus } #endif #endif |
|
>
>
>
>
>
>
>
>
>
|
<
>
>
>
<
|
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
/* when something truly should * never happen: */ kmcond_fail_assert, } kmcond; typedef enum kmkind { kmkind_none, kmkind_broken, kmkind_linear, kmkind_heap, kmkind_pool, kmkind_ref, kmkind_tree } kmkind; ................................................................................ typedef struct kmptr { kmkind kind; kmshred shred; void* ref; } kmptr; /* this struct is used to return both a pointer * and an error value at the same time. */ typedef struct kmres { kmcond cond; union { void* raw; kmptr ptr; }; } kmres; /* heap functions */ kmres kmheapa (sz); kmres kmheapo (sz); kmcond kmheapf (void*); kmres kmlina (sz); void* kmlini (void); /* generic functions */ kmcond kmfree(kmptr); void kmzero(void*,sz); void kmozero(kmptr); #ifdef __cplusplus } #endif #endif |