diff options
-rwxr-xr-x | configure | 29 | ||||
-rw-r--r-- | ffi.c | 345 | ||||
-rw-r--r-- | ffi.h | 8 | ||||
-rw-r--r-- | stdlib/doc-syms.tl | 21 | ||||
-rw-r--r-- | tests/017/mmap.tl | 52 | ||||
-rw-r--r-- | txr.1 | 498 |
6 files changed, 953 insertions, 0 deletions
@@ -3781,6 +3781,35 @@ else printf "no\n" fi +printf "Checking for mmap ... " +cat > conftest.c <<! +#include <sys/mman.h> +#include <unistd.h> +#include <stdlib.h> + +int main(void) +{ + size_t pgsz = sysconf(_SC_PAGE_SIZE); + void *addr = mmap(0, pgsz, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_SHARED, -1, 0); + if (addr == MAP_FAILED) + return EXIT_FAILURE; + mprotect(addr, pgsz, PROT_WRITE); + madvise(addr, pgsz, MADV_SEQUENTIAL); + msync(addr, pgsz, MS_SYNC); + munmap(addr, pgsz); + return 0; +} +! + +if conftest ; then + printf "yes\n" + printf "#define HAVE_MMAP 1\n" >> config.h +else + printf "no\n" +fi + + # # Dependent variables # @@ -44,6 +44,11 @@ #if HAVE_SYS_TYPES_H #include <sys/types.h> #endif +#if HAVE_MMAP +#include <sys/mman.h> +#include <unistd.h> +#include <errno.h> +#endif #include "alloca.h" #include "lib.h" #include "stream.h" @@ -59,6 +64,9 @@ #include "args.h" #include "utf8.h" #include "hash.h" +#if HAVE_MMAP +#include "sysif.h" +#endif #include "ffi.h" #include "txr.h" @@ -5262,6 +5270,9 @@ struct carray { val ref; cnum offs; val artype[2]; +#if HAVE_MMAP + size_t mm_len; +#endif }; static struct carray *carray_struct(val carray) @@ -5330,6 +5341,9 @@ val make_carray(val type, mem_t *data, cnum nelem, val ref, cnum offs) scry->eltype = type; scry->ref = ref; scry->offs = offs; +#if HAVE_MMAP + scry->mm_len = 0; +#endif return obj; } @@ -6045,6 +6059,289 @@ val fill_carray(val carray, val offs, val stream) return ret; } +#if HAVE_MMAP + +#ifndef MAP_GROWSDOWN +#define MAP_GROWSDOWN 0 +#endif +#ifndef MAP_LOCKED +#define MAP_LOCKED 0 +#endif +#ifndef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif +#ifndef MAP_POPULATE +#define MAP_POPULATE 0 +#endif +#ifndef MAP_NONBLOCK +#define MAP_NONBLOCK 0 +#endif +#ifndef MAP_STACK +#define MAP_STACK 0 +#endif +#ifndef MAP_HUGETLB +#define MAP_HUGETLB 0 +#endif +#ifndef MAP_SHARED +#define MAP_SHARED 0 +#endif +#ifndef MAP_PRIVATE +#define MAP_PRIVATE 0 +#endif +#ifndef MAP_FIXED +#define MAP_FIXED 0 +#endif +#if !defined MAP_ANON && defined MAP_ANONYMOUS +#define MAP_ANON MAP_ANONYMOUS +#elif !defined MAP_ANON +#define MAP_ANON 0 +#endif +#ifndef MAP_HUGE_SHIFT +#define MAP_HUGE_SHIFT 0 +#endif +#ifndef MAP_HUGE_MASK +#define MAP_HUGE_MASK 0 +#endif + +#ifndef PROT_READ +#define PROT_READ 0 +#endif +#ifndef PROT_WRITE +#define PROT_WRITE 0 +#endif +#ifndef PROT_EXEC +#define PROT_EXEC 0 +#endif +#ifndef PROT_NONE +#define PROT_NONE 0 +#endif +#ifndef PROT_GROWSDOWN +#define PROT_GROWSDOWN 0 +#endif +#ifndef PROT_GROWSUP +#define PROT_GROWSUP 0 +#endif + +#ifndef MADV_NORMAL +#define MADV_NORMAL 0 +#endif +#ifndef MADV_RANDOM +#define MADV_RANDOM 0 +#endif +#ifndef MADV_SEQUENTIAL +#define MADV_SEQUENTIAL 0 +#endif +#ifndef MADV_WILLNEED +#define MADV_WILLNEED 0 +#endif +#ifndef MADV_DONTNEED +#define MADV_DONTNEED 0 +#endif +#ifndef MADV_FREE +#define MADV_FREE 0 +#endif +#ifndef MADV_REMOVE +#define MADV_REMOVE 0 +#endif +#ifndef MADV_DONTFORK +#define MADV_DONTFORK 0 +#endif +#ifndef MADV_DOFORK +#define MADV_DOFORK 0 +#endif +#ifndef MADV_MERGEABLE +#define MADV_MERGEABLE 0 +#endif +#ifndef MADV_UNMERGEABLE +#define MADV_UNMERGEABLE 0 +#endif +#ifndef MADV_HUGEPAGE +#define MADV_HUGEPAGE 0 +#endif +#ifndef MADV_NOHUGEPAGE +#define MADV_NOHUGEPAGE 0 +#endif +#ifndef MADV_DONTDUMP +#define MADV_DONTDUMP 0 +#endif +#ifndef MADV_DODUMP +#define MADV_DODUMP 0 +#endif +#ifndef MADV_WIPEONFORK +#define MADV_WIPEONFORK 0 +#endif +#ifndef MADV_KEEPONFORK +#define MADV_KEEPONFORK 0 +#endif +#ifndef MADV_HWPOISON +#define MADV_HWPOISON 0 +#endif + +#ifndef MS_ASYNC +#define MS_ASYNC 0 +#endif +#ifndef MS_SYNC +#define MS_SYNC 0 +#endif +#ifndef MS_INVALIDATE +#define MS_INVALIDATE 0 +#endif + +static void carray_munmap_op(val obj) +{ + struct carray *scry = carray_struct(obj); + munmap(scry->data, scry->mm_len); + scry->data = 0; + free(scry); +} + +static struct cobj_ops carray_mmap_ops = + cobj_ops_init(eq, + carray_print_op, + carray_munmap_op, + carray_mark_op, + cobj_eq_hash_op); + +val mmap_wrap(val type, val len, val prot, val flags, + val source_opt, val offset_opt, val addr_opt) +{ + val self = lit("mmap"); + val source = default_null_arg(source_opt); + val offset = default_arg_strict(offset_opt, zero); + val addr = default_null_arg(addr_opt); + void *ad_req = if3(addr, coerce(void *, c_unum(addr, self)), 0); + mem_t *ad_out; + int fd = -1; + ucnum ln = c_unum(len, self); + struct txr_ffi_type *tft = ffi_type_struct_checked(self, type); + cnum nelem = if3(tft->size, ln / tft->size, 0); + int pro = c_int(prot, self); + int flg = c_int(flags, self); + + if (ln != 0 && nelem == 0) + uw_throwf(error_s, lit("~a: zero-sized element type ~s specified"), + self, type, nao); + + if (streamp(source)) { + val fileno = stream_fd(source); + if (!fileno) + uw_throwf(type_error_s, lit("~a: stream ~s has no file descriptor"), + self, source, nao); + fd = c_int(fileno, self); + } else if (integerp(source)) { + fd = c_int(source, self); + } else if (stringp(source)) { + val mode = if3(pro & PROT_WRITE, lit("r+"), lit("r")); + val stream = open_file(source, mode); + val map = nil; + uw_simple_catch_begin; + map = mmap_wrap(type, len, prot, flags, stream, offset_opt, addr_opt); + uw_unwind { + close_stream(stream, nil); + } + uw_catch_end; + return map; + } else if (source) { + uw_throwf(type_error_s, lit("~a: unsupported map source object ~s"), + self, source, nao); + } + + ad_out = coerce(mem_t *, + mmap(ad_req, ln, pro, flg, fd, c_u64(offset, self))); + + if (ad_out == MAP_FAILED) { + int eno = errno; + uw_throwf(system_error_s, lit("~a: mmap failed: ~d/~s"), + self, num(eno), errno_to_str(eno), nao); + } else { + val ca = make_carray(type, ad_out, nelem, nil, 0); + struct carray *scry = carray_struct(ca); + scry->mm_len = ln; + ca->co.ops = &carray_mmap_ops; + return ca; + } +} + +val munmap_wrap(val carray) +{ + val self = lit("munmap"); + struct carray *scry = carray_struct_checked(self, carray); + + if (carray->co.ops != &carray_mmap_ops) + uw_throwf(type_error_s, lit("~a: ~s isn't a mmapped carray"), + self, carray, nao); + if (scry->data != 0) { + munmap(scry->data, scry->mm_len); + scry->data = 0; + return t; + } + + return nil; +} + +static val mmap_op(val carray, val offset_in, val size_in, + val arg, int (*op_fn)(void *, size_t, int), + val self) +{ + struct carray *scry = carray_struct_checked(self, carray); + size_t off = 0, sz; + + if (carray->co.ops != &carray_mmap_ops) + uw_throwf(type_error_s, lit("~a: ~s isn't a mmaped carray"), + self, carray, nao); + + if (missingp(offset_in) && missingp(size_in)) { + sz = scry->mm_len; + } else if (missingp(offset_in)) { + sz = c_unum(size_in, self); + } else if (missingp(size_in)) { + off = c_unum(offset_in, self); + sz = scry->mm_len - off; + } else { + off = c_unum(offset_in, self); + sz = c_unum(size_in, self); + } + + if (off > scry->mm_len) + uw_throwf(error_s, lit("~a: ~s: offset ~s lies beyond ~s byte mapping"), + self, carray, unum(off), unum(scry->mm_len), nao); + + if (off + sz < off) + uw_throwf(error_s, + lit("~a: ~s: size ~s from offset ~s wraps around"), + self, carray, unum(sz), unum(off), nao); + + if (off + sz > scry->mm_len) + uw_throwf(error_s, + lit("~a: ~s: size ~s from offset ~s extends beyond ~s byte mapping"), + self, carray, unum(sz), unum(off), unum(scry->mm_len), nao); + + if (op_fn(scry->data + off, sz, c_int(arg, self)) < 0) { + int eno = errno; + uw_throwf(system_error_s, lit("~a: ~s: ~a failed: ~d/~s"), + self, carray, self, num(eno), errno_to_str(eno), nao); + } + + return t; +} + +val mprotect_wrap(val carray, val prot, val offset, val size) +{ + return mmap_op(carray, offset, size, prot, mprotect, lit("mprotect")); +} + +val madvise_wrap(val carray, val advice, val offset, val size) +{ + return mmap_op(carray, offset, size, advice, madvise, lit("madvise")); +} + +val msync_wrap(val carray, val flags, val offset, val size) +{ + return mmap_op(carray, offset, size, flags, msync, lit("msync")); +} + +#endif + static val cptr_getobj(val cptr, val type_in) { val self = lit("cptr-get"); @@ -6469,6 +6766,54 @@ void ffi_init(void) reg_fun(intern(lit("fill-carray"), user_package), func_n3o(fill_carray, 1)); reg_fun(intern(lit("cptr-get"), user_package), func_n2o(cptr_getobj, 1)); reg_fun(intern(lit("cptr-out"), user_package), func_n3o(cptr_out, 2)); +#if HAVE_MMAP + reg_fun(intern(lit("mmap"), user_package), func_n7o(mmap_wrap, 4)); + reg_fun(intern(lit("munmap"), user_package), func_n1(munmap_wrap)); + reg_fun(intern(lit("mprotect"), user_package), func_n4o(mprotect_wrap, 2)); + reg_fun(intern(lit("madvise"), user_package), func_n4o(madvise_wrap, 2)); + reg_fun(intern(lit("msync"), user_package), func_n4o(msync_wrap, 2)); + reg_varl(intern(lit("map-growsdown"), user_package), num_fast(MAP_GROWSDOWN)); + reg_varl(intern(lit("map-locked"), user_package), num_fast(MAP_LOCKED)); + reg_varl(intern(lit("map-noreserve"), user_package), num_fast(MAP_NORESERVE)); + reg_varl(intern(lit("map-populate"), user_package), num_fast(MAP_POPULATE)); + reg_varl(intern(lit("map-nonblock"), user_package), num_fast(MAP_NONBLOCK)); + reg_varl(intern(lit("map-stack"), user_package), num_fast(MAP_STACK)); + reg_varl(intern(lit("map-hugetlb"), user_package), num_fast(MAP_HUGETLB)); + reg_varl(intern(lit("map-shared"), user_package), num_fast(MAP_SHARED)); + reg_varl(intern(lit("map-private"), user_package), num_fast(MAP_PRIVATE)); + reg_varl(intern(lit("map-fixed"), user_package), num_fast(MAP_FIXED)); + reg_varl(intern(lit("map-anon"), user_package), num_fast(MAP_ANON)); + reg_varl(intern(lit("map-huge-shift"), user_package), num_fast(MAP_HUGE_SHIFT)); + reg_varl(intern(lit("map-huge-mask"), user_package), num_fast(MAP_HUGE_MASK)); + reg_varl(intern(lit("prot-read"), user_package), num_fast(PROT_READ)); + reg_varl(intern(lit("prot-write"), user_package), num_fast(PROT_WRITE)); + reg_varl(intern(lit("prot-exec"), user_package), num_fast(PROT_EXEC)); + reg_varl(intern(lit("prot-none"), user_package), num_fast(PROT_NONE)); + reg_varl(intern(lit("prot-growsdown"), user_package), num_fast(PROT_GROWSDOWN)); + reg_varl(intern(lit("prot-growsup"), user_package), num_fast(PROT_GROWSUP)); + reg_varl(intern(lit("madv-normal"), user_package), num_fast(MADV_NORMAL)); + reg_varl(intern(lit("madv-random"), user_package), num_fast(MADV_RANDOM)); + reg_varl(intern(lit("madv-sequential"), user_package), num_fast(MADV_SEQUENTIAL)); + reg_varl(intern(lit("madv-willneed"), user_package), num_fast(MADV_WILLNEED)); + reg_varl(intern(lit("madv-dontneed"), user_package), num_fast(MADV_DONTNEED)); + reg_varl(intern(lit("madv-free"), user_package), num_fast(MADV_FREE)); + reg_varl(intern(lit("madv-remove"), user_package), num_fast(MADV_REMOVE)); + reg_varl(intern(lit("madv-dontfork"), user_package), num_fast(MADV_DONTFORK)); + reg_varl(intern(lit("madv-dofork"), user_package), num_fast(MADV_DOFORK)); + reg_varl(intern(lit("madv-mergeable"), user_package), num_fast(MADV_MERGEABLE)); + reg_varl(intern(lit("madv-unmergeable"), user_package), num_fast(MADV_UNMERGEABLE)); + reg_varl(intern(lit("madv-hugepage"), user_package), num_fast(MADV_HUGEPAGE)); + reg_varl(intern(lit("madv-nohugepage"), user_package), num_fast(MADV_NOHUGEPAGE)); + reg_varl(intern(lit("madv-dontdump"), user_package), num_fast(MADV_DONTDUMP)); + reg_varl(intern(lit("madv-dodump"), user_package), num_fast(MADV_DODUMP)); + reg_varl(intern(lit("madv-wipeonfork"), user_package), num_fast(MADV_WIPEONFORK)); + reg_varl(intern(lit("madv-keeponfork"), user_package), num_fast(MADV_KEEPONFORK)); + reg_varl(intern(lit("madv-hwpoison"), user_package), num_fast(MADV_HWPOISON)); + reg_varl(intern(lit("ms-async"), user_package), num_fast(MS_ASYNC)); + reg_varl(intern(lit("ms-sync"), user_package), num_fast(MS_SYNC)); + reg_varl(intern(lit("ms-invalidate"), user_package), num_fast(MS_INVALIDATE)); + reg_varl(intern(lit("page-size"), user_package), num_fast(sysconf(_SC_PAGESIZE))); +#endif reg_fun(intern(lit("make-union"), user_package), func_n3o(make_union, 1)); reg_fun(intern(lit("union-members"), user_package), func_n1(union_members)); reg_fun(intern(lit("union-get"), user_package), func_n2(union_get)); @@ -129,6 +129,14 @@ val uint_carray(val carray); val int_carray(val carray); val put_carray(val carray, val offs, val stream); val fill_carray(val carray, val offs, val stream); +#if HAVE_MMAP +val mmap_wrap(val type, val len, val prot, val flags, + val source_opt, val offset_opt, val addr_opt); +val munmap_wrap(val carray); +val mprotect_wrap(val carray, val prot, val offset, val size); +val madvise_wrap(val carray, val advice, val offset, val size); +val msync_wrap(val carray, val flags, val offset, val size); +#endif mem_t *union_get_ptr(val self, val uni); val make_union(val type, val init, val memb); val union_members(val uni); diff --git a/stdlib/doc-syms.tl b/stdlib/doc-syms.tl index b9877466..81ec7211 100644 --- a/stdlib/doc-syms.tl +++ b/stdlib/doc-syms.tl @@ -1165,6 +1165,12 @@ ("macroexpand-1-lisp1" "N-01E62179") ("macroexpand-lisp1" "N-01E62179") ("macrolet" "N-00AC12C0") + ("madv-dontneed" "N-027D1E84") + ("madv-normal" "N-027D1E84") + ("madv-random" "N-027D1E84") + ("madv-sequential" "N-027D1E84") + ("madv-willneed" "N-027D1E84") + ("madvise" "N-02805A83") ("major" "N-02F0F482") ("make-buf" "N-011445E1") ("make-buf-stream" "N-03F5647C") @@ -1195,6 +1201,10 @@ ("make-zstruct" "N-03855D2D") ("makedev" "N-02F0F482") ("makunbound" "N-01FA4070") + ("map-anon" "N-029B13AF") + ("map-fixed" "N-029B13AF") + ("map-private" "N-029B13AF") + ("map-shared" "N-029B13AF") ("mapcar" "N-0202F92F") ("mapcar*" "N-0202F92F") ("mapdo" "N-03A943EE") @@ -1251,10 +1261,17 @@ ("mkstring" "N-033DD796") ("mlet" "N-008216E0") ("mmakunbound" "N-02964FC0") + ("mmap" "N-03C6CE44") ("mod" "D-003F") ("mode-t" "N-01D716FE") + ("mprotect" "N-02805A83") + ("ms-async" "N-01F782B2") + ("ms-invalidate" "N-01F782B2") + ("ms-sync" "N-01F782B2") + ("msync" "N-02805A83") ("multi" "N-034946BA") ("multi-sort" "N-0132852F") + ("munmap" "N-00E1BF52") ("n-choose-k" "N-02ACFDE6") ("n-perm-k" "N-02ACFDE6") ("name" "N-01557906") @@ -1438,6 +1455,10 @@ ("promisep" "N-00C7553F") ("prop" "N-01C6D406") ("proper-list-p" "N-03F70343") + ("prot-exec" "N-0212DB35") + ("prot-none" "N-0212DB35") + ("prot-read" "N-0212DB35") + ("prot-write" "N-0212DB35") ("pset" "N-008211EC") ("ptr" "N-027B04D0") ("ptr-in" "N-00A494BF") diff --git a/tests/017/mmap.tl b/tests/017/mmap.tl new file mode 100644 index 00000000..aab86a0e --- /dev/null +++ b/tests/017/mmap.tl @@ -0,0 +1,52 @@ +(load "../common") + +(defun parent (wp mm) + (with-stream (s (open-fileno wp "w")) + (each ((i 0..1024)) + (set [mm i] i)) + (put-char #\X s))) + +(defun child (rp mm) + (let ((s (open-fileno rp "r"))) + (assert (eq (get-char s) #\X)) + (each ((i 0..1024)) + (assert (eql [mm i] i))))) + +(let ((mm (mmap (ffi uint32) 4096 + (logior prot-read prot-write) + (logior map-anon map-shared)))) + (tree-bind (rp . wp) (pipe) + (match-ecase (fork) + (0 (child rp mm) + (exit t)) + (-1 (error "fork failed")) + (@pid (parent wp mm) + (tree-bind (p . s) (wait pid) + (unless (zerop s) + (error "child failed"))))))) + +(assert (plusp page-size)) + +(let* ((mk-rnd-buf (opip (expt 256 page-size) rand buf-uint)) + (rndbuf0 [mk-rnd-buf]) + (rndbuf1 [mk-rnd-buf]) + (fname "rand.bin")) + (unwind-protect + (progn + (file-put-buf fname rndbuf0) + (let* ((mm (mmap (ffi uchar) page-size + (logior prot-read prot-write) + (logior map-shared) + fname))) + (each ((i 0..page-size)) + (assert (eq [rndbuf0 i] [mm i])) + (set [mm i] [rndbuf1 i])) + (msync mm ms-sync) + (assert (equal (file-get-buf fname) rndbuf1)) + (each ((i 0..page-size)) + (set [mm i] [rndbuf0 i])) + (munmap mm)) + (assert (equal (file-get-buf fname) rndbuf0))) + (remove-path fname))) + +(assert (null (ignerr (mmap (ffi char) 4096 prot-read map-anon)))) @@ -72101,6 +72101,497 @@ returns Further information about resource limits is available in the POSIX standard and platform documentation. +.SS Unix Memory Mapping + +The \*(TL interface to the POSIX +.code mmap +family of functions is based around the +.code carray +type. The +.code mmap +function returns a special variant of a +.code carray +object which keeps track of the memory mapping. When such an object +becomes unreachable and is reclaimed by garbage collection, the mapping +is automatically unmapped. + +In addition to +.codn mmap , +the functions +.codn munmap , +.codn mprotect , +.code madvise +and +.code msync +are provided, all taking a +.code carray +as their leftmost argument. + +The \*(TL functions do not strictly follow the argument conventions of the +same-named, corresponding POSIX functions. Adjustments which are likely to +be defaulted are moved to the right. +For instance, the +.code msync +operation is often applied to the entire memory mapping. Therefore, +the first argument is the +.code carray +object which keeps track of the mapping. The second argument specifies +the flags to be applied, which constitute the last argument of the +underlying POSIX function. +The remaining two arguments are the size and offset. If these are omitted, +then +.code msync +applies to the entire region, whose address and size are known to the +.code carray +object. + +Cautionary note: misuse of +.code mmap +and related functions can easily cause the \*(TX image to receive +a fatal signal due to a bad memory access. Care must be taken to prevent +such a situation, or else to catch such signals and recover. + +.coNP Function @ mmap +.synb +.mets (mmap < ffi-type < length < prot < flags +.mets \ \ \ \ \ >> [ source >> [ offset <> [ addr ]]]) +.syne +.desc +The +.code mmap +function provides access to the same-named POSIX platform function +for creating memory mappings. The POSIX function cab be used for creating +virtual memory views of files and special devices. Views can be read-only, +and they can be mutable. They can be in such a way that changes appear +only in the mapping itself, or in such a way that the changes are actually +propagated to the mapped object itself. Mappings can be shared among +processes, providing a shared memory mechanism: for instance, if +.code fork +is called, any +.code map-shared +mappings created by the parent are shared with the child: the child +process does not get a copy of a shared mapping, but a reference to it. +The function can also be used simply for allocating memory: on some +platforms, the POSIX +.code mmap +is used as the basis for the +.code malloc +function. It behaves as a pure allocator when asked to create a mapping which +is private, and anonymous (not backed by an object). + +The \*(TL +.code mmap +function is integrated with the +.code carray +type and the FFI type system. A mapping returned by +.code mmap +is represented by a +.code carray +object. + +The required +.meta ffi-type +argument specifies the element type of the array; it must be a compiled +FFI type. Note: this may be produced by the +.code ffi +macro. For instance, the type +.code int +may be specified using the expression +.codn "(ffi int)" . +The type must be a complete type suitable as the element type of an array; +a type with a zero size such as +.code void +is invalid. + +The +.meta length +argument specifies the length in bytes. Note that +.code mmap +works allocates or configures virtual memory pages, not bytes. Internally +to the system, the +.meta length +argument is converted to a number of pages. If it specifies a fractional +number of pages, it is rounded up. For instance, if the page size is 4096 +bytes, and +.meta length +is specified as 5000, it will be internally rounded up to 8192. +The returned \*(TL +.code carray +object, is oblivious to this padding: it works with the given 5000 byte size. +Note: the +.code page-size +variable holds the system's page size. However, by the use of +.code mmap +extensions, it is possible for individual mappings to have their own page size. +Mixed page size virtual memory systems exist. + +The +.code mmap +function determines the number of elements in the array by dividing the +.meta length +by the size of +.metn type , +using a division that truncates toward zero. The returned +.code carray +shall have that many elements. If the division is inexact, it means that +some bytes from the underlying memory mapping are unused, even if +.code length +is a multiple of the page size. + +The required +.meta prot +argument must some bitwise combination of the portable values +.codn prot-read , +.code prot-write +and +.codn prot-exec . +Additional system-specific +.code prot- +values may be available also for specifying additional properties. If +.meta prot +is specified as zero, then the mapping, if successfully created, may be +inaccessible: +.code prot-read +must be present to ensure read access, and +.code prot-write +to ensure write access. + +The +.meta flags +argument is a bitwise combination of values given by various +.code map- +variables. At the very least, it must contain exactly one of +.code map-shared +or +.codn map-private , +to request a shared or private mapping, respectively. +If a mapping is requested which is neither shared nor private, +the underlying POSIX function will likely fail. +If a +.meta source +is being specified, indicating a filesystem object to be mapped, the +.code map-anon +flag must be omitted. Vice versa, if +.meta source +is not being specified, this means that the mapping will be anonymous. +In this situation, the +.code map-anon +flag must be present. + +Note: in the context of +.codn mmap , +"anonymous" means "not associated with a filesystem object referenced by a +descriptor". It does not mean "without a name", but refers to a pure memory +allocation from virtual memory. Memory maps do not have a name, whether +anonymous or not. Moreover, the filesystem object associated with a memory map +itself does not necessarily have a name. An open file that has been deleted +from the directory structure is anonymous, yet a memory mapping can be created +using its descriptor, and that mapping is not "anonymous". + +The +.meta offset +argument is used with a non-anonymous mapping. It specifies that the mapping +doesn't begin at the start of the file or file-like object, but rather at +the specified offset. The offset may not be an arbitrary integer; it must be +multiples of the page size. Unless certain nonportable +.meta flags +are used to specify an alternative page size, the value of the +.code page-size +variable may be relied upon to indicate the page size. If an +.meta offset +is specified for an anonymous mapping, with a nonzero value, the +underlying POSIX function may indicate failure. + +If the +.meta length +and +.meta offset +values cause one or more pages to be mapped which are beyond the end of the +file, then accessing those pages may produce a signal which is fatal if +not handled. + +The +.meta addr +argument is used for specifying the address in conjunction with the +.code map-fixed +flag. Possibly, certain nonportable values in the +.meta flags +field may similarly require +.metn addr . +If no bit is present to +.meta flags +which requires +.metn addr , +then +.meta addr +should either not be specified, or specified as zero. +A non-zero value of +.meta addr +must be a multiple of the page size. + +The +.code mmap +function returns a +.code carray +object if successful. Upon failure, an exception derived from +.code error +is thrown. + +Note: when a +.code carray +object returned by +.code mmap +is identified by the garbage collector as unreachable, and reclaimed, +the memory mapping is unmapped. The +.code munmap +function can be invoked on the +.code carray +to release the mapping before the object becomes garbage. The +.code carray-free +function cannot be used on a mapped +.codn carray . + +.coNP Function @ munmap +.synb +.mets (munmap << carray ) +.syne +.desc +The +.code munmap +function releases the memory mapping tracked by +.metn carray , +which must be an object previously returned by +.codn mmap . +An exception is thrown if the object is any other kind of +.codn carray . + +Note: the memory mapping is released by means of the same-named POSIX function. +No provision is made for selectively unmapping the pages of a mapping; +the entire mapping associated with a +.meta carray +is removed. + +when the memory mapping is released, +.code munmap +returns +.codn t . +Thereafter, the +.meta carray +contents may no longer be accessed, subject to +.code error +exceptions being thrown. + +If +.code munmap +is called again on a +.code carray +on which it had previously been successfully called, the additional calls +return +.codn nil . + +.coNP Functions @, mprotect @ madvise and @ msync +.synb +.mets (mprotect < carray < prot >> [ offset <> [ size ]]) +.mets (madvise < carray < advice >> [ offset <> [ size ]]) +.mets (msync < carray < flags >> [ offset <> [ size ]]) +.syne +.desc +The functions +.codn mprotect , +.code madvise +and +.code msync +perform various operations and adjustments on a memory mapping, using the +same-named, corresponding POSIX functions. + +All functions follow the same argument conventions with regard to the +.meta carray +argument and the optional +.meta offset +and +.meta size +arguments. The respective second arguments +.metn prot , +.meta advice +and +.meta flags +are all integers. Of these, +.meta prot +and +.meta flags +are bitmapped flags, whereas +.meta advice +specifies an enumerated command. + +The +.meta prot +argument is a bitwise combination of +.code prot- +values such as +.codn prot-read , +.code prot-write +and +.codn prot-exec . +The +.code mprotect +function adjusts the protection bits of the mapping accordingly. + +The +.meta advice +command of +.code madvise +should specify one of the following portable values, or else some +system-specific nonportable +.code madv- +value: +.codn madv-normal , +.codn madv-random , +.codn madv-sequential , +.code madv-willneed +or +.codn madv-dontneed . + +The +.code flags +argument of +.code msync +should specify exactly one of the values +.code ms-async +or +.codn ms-sync . +Additional +.code ms- +values such as +.code ms-invalidate +may be combined in. + +If +.meta offset +and +.meta size +are omitted, they default to zero, and the size of the entire mapping, respectively, +so the operation applies to the entire mapping. + +If only a +.meta size +is specified, it must not exceed the mapping size, or an error exception is +thrown. The +.meta offset +argument defaults to zero. + +If only the +.meta offset +is specified, it must not exceed the length of the mapping, or else +an error exception is thrown. The size is calculated as the difference between +the offset and the length. It may be zero. + +If both +.meta offset +and +.meta size +are specified, they must not specify a region any portion of which lies outside +of the mapping. If +.meta size +is zero, +.meta offset +may be equal to the length of the mapping. + +The +.meta offset +must be a multiple of the page size, or else the operation will fail, +since these functions work with virtual memory pages, and not individual +bytes. The +.meta length +is adjusted by the system to a multiple of the applicable page size, +as noted in the description of +.codn mmap . + +When any of these three functions succeeds, it returns +.codn t . +Otherwise, it throws an exception. + +.coNP Variables @, map-shared @, map-private @ map-anon and @ map-fixed +.desc +The integer values of these variables are bitmasks, intended to be combined with +.code logior +to prepare a value for the +.meta flags +argument of +.codn mmap . + +Additional nonportable, system-dependent +.code map- +variables may be available. Their names are derived by taking the +.codn MAP_ -prefixed +symbol from the platform header file, converting it to lower case and +replacing underscores by hyphen characters. +Any such variable which exists, but has a value of zero, is +present only for compatibility with another system. For instance +.code map-huge-shift +may be present in non-Linux ports of \*(TX, but with a zero value; it has +a nonzero value on Linux systems to which it specific. Applications critically +relying on certain flags should test the corresponding variables for nonzero to +make sure they are actually available. + +.coNP Variables @, prot-none @, prot-read @ prot-write and @ prot-exec +.desc +The integer values of these variable are bitmasks, intended to be combined with +.code logior +to prepare a value for the +.meta prot +argument of +.code mmap +and +.codn mprotect . + +Additional nonportable, system-dependent +.code prot- +variables may be available. Their names are derived by taking the +.codn PROT_ -prefixed +symbol from the platform header file, converting it to lower case and +replacing underscores by hyphen characters. +Any such variable which exists, but has a value of zero, is +present only for compatibility with another system. + +.coNP Variables @, madv-normal @, madv-random @, madv-sequential @ madv-willneed and @ madv-dontneed +.desc +The integer values of these variable are bitmasks, intended to be combined with +.code logior +to prepare a value for the +.meta advice +argument of the +.code madvise +function. + +Additional nonportable, system-dependent +.code madv- +variables may be available. Their names are derived by taking the +.codn MADV_ -prefixed +symbol from the platform header file, converting it to lower case and +replacing underscores by hyphen characters. +Any such variable which exists, but has a value of zero, is +present only for compatibility with another system. + +.coNP Variables @, ms-async @ ms-sync and @ ms-invalidate +.desc +The integer values of these variable are bitmasks, intended to be combined with +.code logior +to prepare a value for the +.meta advice +argument of the +.code msync +function. + +As described under +.codn msync , +at least one of +.code ms-async +and +.code ms-sync +should be present; +.code ms-invalidate +is optional. + .SS* Web Programming Support .coNP Functions @ url-encode and @ url-decode @@ -76735,6 +77226,13 @@ It is possible to create a view over a buffer, using .codn carray-buf . +Lastly, the +.code carray +type is the basis for the \*(TL +.code mmap +function, which is documented in the section +.BR "Unix Memory Mapping" . + .coNP FFI type @ cptr .synb .mets (cptr << type-sym ) |