pub val F_GETFD: i32 = 1
fcntl 命令
F_GETPATH 及其 MAXPATHLEN 伴生者已消失:它们存在仅因为原始系统调用后端没有 getcwd,而 getcwd(3) 取代了两者。
pub val F_SETFD: i32 = 2
pub val F_GETFL: i32 = 3
pub val F_SETFL: i32 = 4
pub val FD_CLOEXEC: i32 = 1
pub rec stat_t;
用于文件元数据的 darwin stat64 结构
pub rec timespec;
包含秒和纳秒的时间规范
pub rec dirent64;
目录项(darwin getdirentries64 布局)
pub val separator: u8 = '/'
路径
pub val STDIN_FD: i32 = 0
可移植常量
pub val STDOUT_FD: i32 = 1
pub val STDERR_FD: i32 = 2
pub val O_RDONLY: i32 = 0
pub val O_WRONLY: i32 = 1
pub val O_RDWR: i32 = 2
pub val O_CREAT: i32 = 0x200
pub val O_EXCL: i32 = 0x800
pub val O_TRUNC: i32 = 0x400
pub val O_APPEND: i32 = 8
pub val O_DIRECTORY: i32 = 0x100000
pub val AT_FDCWD: i32 = -2
pub val AT_SYMLINK_NOFOLLOW: i32 = 0x0020
pub val AT_REMOVEDIR: i32 = 0x0080
pub val S_IFMT: u32 = 0o170000
pub val S_IFDIR: u32 = 0o040000
pub val S_IFREG: u32 = 0o100000
pub val S_IFLNK: u32 = 0o120000
pub val WNOHANG: i32 = 1
pub val WUNTRACED: i32 = 2
pub val WCONTINUED: i32 = 0x10
pub val CLOCK_REALTIME: i32 = 0
pub val CLOCK_MONOTONIC: i32 = 6
pub val EPERM: i64 = -1
pub val ENOENT: i64 = -2
pub val ESRCH: i64 = -3
pub val EINTR: i64 = -4
pub val EIO: i64 = -5
pub val ENXIO: i64 = -6
pub val E2BIG: i64 = -7
pub val EBADF: i64 = -9
pub val ECHILD: i64 = -10
pub val EAGAIN: i64 = -35
pub val ENOMEM: i64 = -12
pub val EACCES: i64 = -13
pub val EFAULT: i64 = -14
pub val EBUSY: i64 = -16
pub val EEXIST: i64 = -17
pub val ENODEV: i64 = -19
pub val ENOTDIR: i64 = -20
pub val EISDIR: i64 = -21
pub val EINVAL: i64 = -22
pub val ENFILE: i64 = -23
pub val EMFILE: i64 = -24
pub val ETXTBSY: i64 = -26
pub val ENOSPC: i64 = -28
pub val EROFS: i64 = -30
pub val EPIPE: i64 = -32
pub val ENOTEMPTY: i64 = -66
pub val ENOTSUP: i64 = -45
pub val ERANGE: i64 = -34
pub val EINTR_MAX_RETRIES: usize = 8
pub fun syscall0(n: usize) i64;
pub fun syscall1(n: usize, a0: usize) i64;
pub fun syscall2(n: usize, a0: usize, a1: usize) i64;
pub fun syscall3(n: usize, a0: usize, a1: usize, a2: usize) i64;
pub fun syscall4(n: usize, a0: usize, a1: usize, a2: usize, a3: usize) i64;
pub fun syscall5(n: usize, a0: usize, a1: usize, a2: usize, a3: usize, a4: usize) i64;
pub fun syscall6(n: usize, a0: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) i64;
pub fun terminate(code: i64);
以给定代码退出进程
code: 退出代码
pub fun abort();
立即中止进程,退出代码为 255
pub fun allocate(size: usize) ptr;
pub fun deallocate(p: ptr, size: usize) i64;
pub fun reallocate(p: ptr, old_size: usize, new_size: usize) ptr;
darwin 没有 mremap,分配新内存、复制、释放旧内存
pub fun heap_region_base() usize;
pub fun heap_region_extend(addr: usize) usize;
pub fun protect(p: ptr, size: usize, prot: u32) i64;
pub fun lock(p: ptr, size: usize) i64;
pub fun unlock(p: ptr, size: usize) i64;
pub fun advise(p: ptr, size: usize, advice: u32) i64;
pub fun map_file(fd: i32, offset: usize, size: usize, prot: u32) ptr;
pub fun sync_file(p: ptr, size: usize) i64;
pub var _envp: usize = 0
环境变量(由运行时在程序启动时设置)
pub fun environ() **u8;
指向运行时在程序启动时捕获的环境变量(以 null 结尾的 "NAME=value" 字符串数组)。在运行时初始化之前为 nil。
pub fun read(fd: i32, buf: *u8, count: usize) i64;
pub fun write(fd: i32, buf: *u8, count: usize) i64;
pub fun open(dirfd: i32, path: *u8, flags: i32, mode: i32) i64;
打开相对于目录描述符的路径
mode 仅在 flags 携带 O_CREAT 时由内核检查,因此仅在那种情况下在可变参数尾部传递。这两种形式不是同一调用的不同拼写:apple arm64 将整个可变参数尾部在栈上传递,因此没有 O_CREAT 的形式真正不传递任何内容,而不是传递一个被忽略的寄存器。
dirfd: 路径解析的目录描述符,或 AT_FDCWD path: 以 null 结尾的路径 flags: O_* 标志 mode: 创建文件的权限位;没有 O_CREAT 时被忽略 ret: 新的描述符,或负数的 errno
pub fun close(fd: i32) i64;
关闭描述符
关闭失败并不表示描述符仍然打开:darwin 在报告错误之前已经释放了它。调用者不得重试,也没有重试——返回值用于显示延迟写入错误。
pub fun sync_fd(fd: i32) i64;
将打开文件的数据和元数据刷新到其后备存储
pub fun stat(fd: i32, st: *stat_t) i64;
pub fun stat_path(dirfd: i32, path: *u8, st: *stat_t, flags: i32) i64;
pub fun stat_mode(st: *stat_t) u32;
从填充的 stat 缓冲区读取类型/权限位作为 32 位字
st_mode 在 darwin stat64 ABI 中是 16 位(mode_t 是 __uint16_t);将其扩展,以便可移植调用者可以与 32 位平台一致地读取模式。
st: 指向已填充 stat_t 的指针 ret: 原始模式位,扩展为 32 位
pub fun unlink(dirfd: i32, path: *u8, flags: i32) i64;
pub fun rename(olddir: i32, oldpath: *u8, newdir: i32, newpath: *u8) i64;
pub fun symlink(target: *u8, linkpath: *u8) i64;
在 linkpath 处创建指向 target 的符号链接
target 原样存储:相对 target 保持相对,因此链接在包含树移动后仍能正确解析。
target: 链接指向的路径(按给定方式存储,不解析) linkpath: 要创建的链接路径 ret: 成功时为 0,或负数的 errno
pub fun make_dir(dirfd: i32, path: *u8, mode: i32) i64;
pub fun read_dir(fd: i32, dirp: *dirent64, count: usize) i64;
darwin 使用 getdirentries64 而不是 getdents64
pub fun access(dirfd: i32, path: *u8, mode: i32, flags: i32) i64;
pub fun seek(fd: i32, offset: i64, whence: i32) i64;
重新定位描述符的偏移量
lseek 的失败哨兵值恰好是 -1,而不是"负数":成功值是文件偏移量,它从不为负,但确实作为有符号 64 位数返回,调用者可以合法地将其与自身比较。测试文档化的哨兵而不是测试符号可以保持这种区分。
ret: 结果的绝对偏移量,或负数的 errno
pub fun fd_flags(fd: i32) i64;
读取描述符的描述符级标志(FD_CLOEXEC)
F_GETFD 是一个双参数 fcntl:它不读取第三个参数,因此不传递任何参数。这不是微优化——在 apple arm64 上,可变参数尾部在栈上传递,因此"传递一个虚拟零"和"不传递任何内容"会在被调用者的栈槽中放置真正不同的字节。这两种形式在每次调用 fcntl 的地方都保持不同。
ret: 标志字,或负数的 errno
pub fun set_fd_flags(fd: i32, flags: i32) i64;
设置描述符的描述符级标志
F_SETFD 是一个三参数 fcntl:恰好一个 int 沿着可变参数尾部传递。
ret: 成功时为 0,或负数的 errno
pub fun file_flags(fd: i32) i64;
读取描述符的文件状态标志(访问模式、O_APPEND、O_NONBLOCK)
F_GETFL,像 F_GETFD 一样,不需要第三个参数。
ret: 状态标志字,或负数的 errno
pub fun set_file_flags(fd: i32, flags: i32) i64;
设置描述符的文件状态标志
ret: 成功时为 0,或负数的 errno
pub fun fork() i64;
通过复制调用者创建子进程
XNU fork 陷阱在主寄存器(rax/x0)中为两个进程返回子进程 PID,并通过第二个返回寄存器(rdx == 1 on x86_64, x1 == 1 on aarch64)标记子进程。普通的 syscall0 会让子进程看到 pid != 0,因此此包装器读取子进程指示器并在子进程中返回 0,镜像 libc 的 fork 存根。进位标志被物化为 fail(setc/cset),分支发生在 mach 代码中,与系统调用包装器和 pipe 匹配。
ret: 父进程中为子进程 PID,子进程中为 0,或负数的 errno
pub fun vfork() i64;
创建一个共享父进程地址空间的子进程, 挂起父进程,直到子进程 exec 或退出。
XNU vfork 陷阱使用与 fork 相同的双寄存器返回约定:子进程 PID 在主寄存器(rax/x0)中为两个进程返回,子进程通过第二个返回寄存器标记(rdx == 1 on x86_64, x1 == 1 on aarch64)。普通的 syscall0 会让子进程看到 pid != 0,因此此包装器读取子进程指示器并在子进程中返回 0,镜像 fork。进位标志被物化为 fail(setc/cset),分支发生在 mach 代码中。父进程挂起语义在内核侧;包装器形状与 fork 匹配。
ret: 父进程中为子进程 PID,子进程中为 0,或负数的 errno
pub fun exec(pathname: *u8, argv: **u8, envp: **u8) i64;
pub fun wait(pid: i64, wstatus: *i32, options: i32, rusage: *u8) i64;
pub fun wait_pid(pid: i64, wstatus: *i32, options: i32) i64;
pub fun terminate_child(pid: i64) i64;
强制停止一个生成的子进程而不回收它
首先使用 WNOWAIT 的 waitid 询问内核验证 pid 仍然是此进程的子进程。这会在 kill 能影响它们之前拒绝未知和已回收的 PID,同时保留已退出子进程的等待状态。调用者不得通过 wait 或 wait_pid 同时回收此子进程:回收器可能在所有权检查和 kill 之间释放数字 PID。
pid: 由 spawn 返回的子进程 ID ret: 成功时为 0,或负数的 errno
pub fun spawn(pathname: *u8, argv: **u8, envp: **u8) i64;
创建运行给定程序的孩子进程
在 exec 之前关闭子进程中继承的文件描述符 >= 3,以防止将父进程 FD(管道、套接字等)泄漏到子进程。
pathname: 可执行文件的路径 argv: 以 null 结尾的参数数组 envp: 以 null 结尾的环境变量数组 ret: 成功时为子进程 PID,或负数的 errno
pub fun spawn_in(pathname: *u8, argv: **u8, envp: **u8, cwd: *u8) i64;
从 cwd 创建运行给定程序的孩子进程
pathname: 可执行文件的路径 argv: 以 null 结尾的参数数组 envp: 以 null 结尾的环境变量数组,或 nil 以继承 cwd: 子进程的工作目录,或 nil 以继承 ret: 成功时为子进程 PID,或负数的 errno
pub fun spawn_redirected(pathname: *u8, argv: **u8, envp: **u8, stdin_fd: i32, stdout_fd: i32, stderr_fd: i32) i64;
生成子进程,其标准流绑定到调用者提供的描述符。
fork,将每个非负描述符复制到匹配的标准流(-1 继承父进程的流),关闭继承的描述符 >= 3(包括捕获管道的两端),然后 exec。 父进程保留传递的描述符的所有权;将一个与管道端配对可以让父进程流式传输子进程的 stdio。当重定向失败时子进程退出 126,当 exec 失败时退出 127。
pathname: 可执行文件的路径 argv: 以 null 结尾的参数数组 envp: 以 null 结尾的环境变量数组 stdin_fd: 安装为子进程 stdin 的描述符,或 -1 以继承 stdout_fd: 安装为子进程 stdout 的描述符,或 -1 以继承 stderr_fd: 安装为子进程 stderr 的描述符,或 -1 以继承 ret: 成功时为子进程 PID,或负数的 errno
pub fun spawn_redirected_in(pathname: *u8, argv: **u8, envp: **u8, cwd: *u8, stdin_fd: i32, stdout_fd: i32, stderr_fd: i32) i64;
生成子进程,带有重定向的流和明确的工作目录
cwd 是子进程启动的目录,或 nil 以继承父进程。chdir 发生在子进程中,在 fork/clone 之后,因此父进程自己的工作目录不受影响。在此传递它而不是将 cd 前缀添加到 shell 命令,可以保持路径不在命令行中,它必须幸存于目标程序可能不共享的引用约定(mach#2587)。
pathname: 可执行文件的路径 argv: 以 null 结尾的参数数组 envp: 以 null 结尾的环境变量数组,或 nil 以继承 cwd: 子进程的工作目录,或 nil 以继承 stdin_fd: 安装为子进程 stdin 的描述符,或 -1 以继承 stdout_fd: 安装为子进程 stdout 的描述符,或 -1 以继承 stderr_fd: 安装为子进程 stderr 的描述符,或 -1 以继承 ret: 成功时为子进程 PID,或负数的 errno
pub fun spawn_shell(command: *u8, envp: **u8, cwd: *u8) i64;
从 cwd 通过主机命令解释器生成 command
posix 将 argv 直接传递给 execve,因此命令是一个参数,没有命令行编码步骤会出错——这是 windows 后端的对应物,其中解释器的引用规则必须显式尊重。
command: 传递给解释器的命令行 envp: 以 null 结尾的环境变量数组,或 nil 以继承 cwd: 子进程的工作目录,或 nil 以继承 ret: 成功时为子进程 PID,或负数的 errno
pub fun getpid() i64;
返回当前进程 ID
ret: 进程 ID
pub fun ignore_sigpipe() i64;
忽略此进程的 SIGPIPE
signal(3) 通过公共 libSystem 安装处理程序。它是进程范围的,因此来自每个线程的后续写入当其管道没有读取器时返回 EPIPE 而不是终止进程。
ret: 成功时为 0,或负数的 errno
pub fun sleep(nsec: i64);
挂起执行给定数量的纳秒
使用 nanosleep(3)。此处的先前注释"darwin 没有 nanosleep 系统调用"对于陷阱表来说是正确的,对于 libSystem 来说是错误的,因此这经过了带有超时的 select,在途中失去了亚微秒分辨率到 timeval。
切断睡眠的信号将未睡眠的剩余部分保留在 rem 中,重试从 THAT 恢复而不是重新启动完整间隔,因此中断的睡眠仍然在约请求的时间结束,而不是延伸到两倍。
nsec: 要睡眠的纳秒数
pub fun cpu_count() i64;
此进程可用的 CPU 数量
通过 __sysctl 系统调用读取 hw.ncpu(mib [CTL_HW, HW_NCPU])。 绝不返回小于 1。
ret: 可用 CPU 计数(>= 1)
pub fun getcwd(buf: *u8, size: usize) i64;
获取当前工作目录
以前是 open(".") + fcntl(F_GETPATH) 舞蹈 + MAXPATHLEN 弹跳缓冲区,因为 F_GETPATH 不携带容量:XNU 将最多 MAXPATHLEN 字节写入目标,无论调用者大小如何,因此结果必须在一个大的临时缓冲区中捕获,仅当它适合时才复制出来。
getcwd(3) 携带容量,因此被调用者本身尊重 size,不适合的路径会从 libSystem 返回 ERANGE,而不是这个函数事后必须防御的溢出。临时缓冲区、复制循环和 F_GETPATH 常量都随之消失(#409,#413)。
buf: 目标缓冲区 size: 缓冲区容量(以字节为单位),包括终止符的位置 ret: 成功时为路径长度,或负数的 errno
pub fun getenv(name: *u8, buf: *u8, cap: usize) i64;
按名称查找环境变量
当值适合时(ret < cap),它被复制并以 null 结尾。 始终返回完整值长度,因此 ret >= cap 表示截断(缓冲区内容未指定),ret + 1 是重试时的容量。
name: 以 null 结尾的变量名 buf: 值的目标缓冲区 cap: 缓冲区容量(以字节为单位) ret: 值的完整长度,或 NOT_FOUND
pub fun thread_spawn(f: fun(*u8), arg: *u8, done_ptr: *i64, stack_base: usize, stack_size: usize) i64;
通过 pthread_create 创建新线程
线程创建为 DETACHED。join 通过 sync.thread.join 完成,它等待完成标志而不是调用 pthread_join,因此可连接的 pthread 将永远留下其描述符未回收。分离将其清理工作交给 libpthread 并保持标志作为单个同步点。
注意错误约定:pthread_* 直接返回错误号并且不设置 errno,因此这些结果按原样取反,并且此函数中故意不使用 fail_errno。
f: 在新线程中执行的入口函数 arg: 传递给 f 的不透明指针 done_ptr: 指向完成标志的指针(在线程退出时设置为 1) stack_base: 用作上下文块的调用者分配区域的基址; 由新线程释放,或如果此调用失败则由调用者释放 stack_size: pthread 为线程分配的栈大小 ret: 成功时为线程句柄,或失败时为负数的 errno
pub fun thread_wait(addr: *i64, expected: i64) i64;
阻塞直到 addr 处的值从预期改变
如果 *addr 不等于预期,则立即返回。可能出现虚假唤醒,并且预期如此:桶共享意味着不同地址的唤醒可能到达这里(见上面的桶表注释),因此每个调用者必须在循环中重新测试其自己的条件。
addr: 指向 futex 字的指针 expected: 要与之比较的值 ret: 唤醒时为 0,或负数的 errno
pub fun thread_wake(addr: *i64) i64;
唤醒在 addr 处被 thread_wait 阻塞的线程
广播而不是信号:桶按地址哈希共享,因此信号可能被传递给无关地址上的等待者,而此调用打算传递的唤醒将丢失。
addr: 指向 futex 字的指针 ret: 成功时为 0,或负数的 errno
pub val AF_INET: i32 = 2
pub val SOCK_STREAM: i32 = 1
pub val SOCK_DGRAM: i32 = 2
pub val SOL_SOCKET: i32 = 0xFFFF
pub val SO_REUSEADDR: i32 = 4
pub val SO_RCVTIMEO: i32 = 0x1006
pub val SHUT_RD: i32 = 0
pub val SHUT_WR: i32 = 1
pub val SHUT_RDWR: i32 = 2
pub val SOCKADDR_IN_SIZE: usize = 16
pub fun sock_addr_init(sa: *u8, port: u16, addr: *u8);
pub fun sock_addr_read(sa: *u8, port: *u16, addr: *u8);
pub fun sock_send(fd: i32, buf: *u8, len: usize) i64;
pub fun sock_recv(fd: i32, buf: *u8, len: usize) i64;
pub fun sock_close(fd: i32) i64;
pub fun sock_create(domain: i32, typ: i32, protocol: i32) i64;
pub fun sock_bind(fd: i32, addr: *u8, addrlen: usize) i64;
pub fun sock_listen(fd: i32, backlog: i32) i64;
pub fun sock_accept(fd: i32, addr: *u8, addrlen: *u32) i64;
pub fun sock_connect(fd: i32, addr: *u8, addrlen: usize) i64;
pub fun sock_sendto(fd: i32, buf: *u8, len: usize, flags: i32, addr: *u8, addrlen: usize) i64;
pub fun sock_recvfrom(fd: i32, buf: *u8, len: usize, flags: i32, addr: *u8, addrlen: *u32) i64;
pub fun sock_shutdown(fd: i32, how: i32) i64;
pub fun sock_setopt(fd: i32, level: i32, opt: i32, value: *u8, vallen: usize) i64;
pub fun sock_set_rcvtimeo(fd: i32, ms: u64) i64;
设置套接字的接收超时
将派生自 ms 的 struct timeval(tv_sec, tv_usec)写入为 SO_RCVTIMEO 选项值。ms == 0 清除超时,恢复无限阻塞。一旦设置,带有无待处理数据的 recv 在间隔过期后以 EAGAIN 失败。 darwin 的 tv_usec 是 32 位,带有 4 个尾随填充字节;亚秒 usec 适合低字,填充保持为零,因此字节图像与 linux 64 位 tv_usec 布局匹配。
fd: 套接字 fd ms: 超时(以毫秒为单位),或 0 表示无超时 ret: 成功时为 0,或负数的 errno
pub val DNS_HOSTS_PATH: str = "/etc/hosts"
pub fun dns_nameserver(ns: *u8) i64;
从 /etc/resolv.conf 读取第一个名称服务器
ns: 指向 4 字节以填充 IPv4 地址的指针 ret: 成功时为 0,或 NOT_FOUND / 负数的 errno
pub fun random_fill(buf: *u8, len: usize) i64;
通过 getentropy 用加密随机字节填充缓冲区
buf: 目标缓冲区 len: 要填充的字节数 ret: 成功时为 0,或负数的 errno
pub fun clock_gettime(clock_id: i32, ts: *timespec) i64;
将时钟读入 timespec
CLOCK_REALTIME (0) 和 CLOCK_MONOTONIC (6) 是 darwin 自己的 clockid_t 值,来自 <time.h>,不是 linux 的——它们恰好已经是正确的,因为常量总是针对 darwin 的头文件编写的,即使底下的调用是根本不存在的陷阱。
clock_id: CLOCK_REALTIME 或 CLOCK_MONOTONIC ts: 输出 timespec ret: 成功时为 0,或负数的 errno
pub fun error_message(code: i64) str;