模块 UnixLabels

module UnixLabels: sig .. end

与 Unix 系统的接口。

要使用此模块的带标签版本,请在您的实现中添加 module Unix = UnixLabels

注意:此模块的所有函数(除了 UnixLabels.error_messageUnixLabels.handle_unix_error)都可能在底层系统调用发出错误信号时引发 UnixLabels.Unix_error 异常。


错误报告

type error = Unix.error = 
| E2BIG (*

参数列表过长

*)
| EACCES (*

权限被拒绝

*)
| EAGAIN (*

资源暂时不可用;请重试

*)
| EBADF (*

错误的文件描述符

*)
| EBUSY (*

资源不可用

*)
| ECHILD (*

没有子进程

*)
| EDEADLK (*

资源死锁将发生

*)
| EDOM (*

数学函数等域错误。

*)
| EEXIST (*

文件存在

*)
| EFAULT (*

错误地址

*)
| EFBIG (*

文件太大

*)
| EINTR (*

函数被信号中断

*)
| EINVAL (*

无效参数

*)
| EIO (*

硬件 I/O 错误

*)
| EISDIR (*

是一个目录

*)
| EMFILE (*

进程打开的文件太多

*)
| EMLINK (*

链接太多

*)
| ENAMETOOLONG (*

文件名太长

*)
| ENFILE (*

系统中打开的文件太多

*)
| ENODEV (*

没有这样的设备

*)
| ENOENT (*

没有这样的文件或目录

*)
| ENOEXEC (*

不是可执行文件

*)
| ENOLCK (*

没有可用的锁

*)
| ENOMEM (*

内存不足

*)
| ENOSPC (*

设备上没有剩余空间

*)
| ENOSYS (*

不支持该函数

*)
| ENOTDIR (*

不是目录

*)
| ENOTEMPTY (*

目录不为空

*)
| ENOTTY (*

不适当的 I/O 控制操作

*)
| ENXIO (*

没有这样的设备或地址

*)
| EPERM (*

操作不允许

*)
| EPIPE (*

管道破裂

*)
| ERANGE (*

结果太大

*)
| EROFS (*

只读文件系统

*)
| ESPIPE (*

无效查找(例如,在管道上)

*)
| ESRCH (*

没有这样的进程

*)
| EXDEV (*

无效链接

*)
| EWOULDBLOCK (*

操作将阻塞

*)
| EINPROGRESS (*

操作正在进行

*)
| EALREADY (*

操作正在进行

*)
| ENOTSOCK (*

非套接字上的套接字操作

*)
| EDESTADDRREQ (*

需要目标地址

*)
| EMSGSIZE (*

消息太长

*)
| EPROTOTYPE (*

套接字协议类型错误

*)
| ENOPROTOOPT (*

协议不可用

*)
| EPROTONOSUPPORT (*

不支持该协议

*)
| ESOCKTNOSUPPORT (*

不支持套接字类型

*)
| EOPNOTSUPP (*

套接字不支持该操作

*)
| EPFNOSUPPORT (*

不支持协议族

*)
| EAFNOSUPPORT (*

协议族不支持地址族

*)
| EADDRINUSE (*

地址已被使用

*)
| EADDRNOTAVAIL (*

无法分配请求的地址

*)
| ENETDOWN (*

网络已关闭

*)
| ENETUNREACH (*

网络不可达

*)
| ENETRESET (*

网络在重置时断开了连接

*)
| ECONNABORTED (*

软件导致连接中止

*)
| ECONNRESET (*

连接被对等方重置

*)
| ENOBUFS (*

没有可用的缓冲区空间

*)
| EISCONN (*

套接字已连接

*)
| ENOTCONN (*

套接字未连接

*)
| ESHUTDOWN (*

套接字关闭后无法发送

*)
| ETOOMANYREFS (*

引用过多:无法拼接

*)
| ETIMEDOUT (*

连接超时

*)
| ECONNREFUSED (*

连接被拒绝

*)
| EHOSTDOWN (*

主机已关闭

*)
| EHOSTUNREACH (*

没有到主机的路由

*)
| ELOOP (*

符号链接级数过多

*)
| EOVERFLOW (*

文件大小或位置不可表示

*)
| EUNKNOWNERR of int (*

未知错误

*)

错误代码的类型。POSIX 标准中定义的错误以及 UNIX98 和 BSD 中的附加错误。所有其他错误都映射到 EUNKNOWNERR。

exception Unix_error of error * string * string

在遇到错误时,由以下系统调用引发。第一个组件是错误代码;第二个组件是函数名称;第三个组件是函数的字符串参数(如果有),否则为空字符串。

UnixLabels.Unix_errorUnix.Unix_error 是相同的,捕获其中一个将捕获另一个。

val error_message : error -> string

返回描述给定错误代码的字符串。

val handle_unix_error : ('a -> 'b) -> 'a -> 'b

handle_unix_error f xf 应用于 x 并返回结果。如果引发了异常 UnixLabels.Unix_error,它将打印一条描述错误的消息并以代码 2 退出。

访问进程环境

val environment : unit -> string array

返回进程环境,作为格式为 ``variable=value'' 的字符串数组。如果进程具有特殊权限,则返回的数组为空。

val unsafe_environment : unit -> string array

返回进程环境,作为格式为 ``variable=value'' 的字符串数组。与 UnixLabels.environment 不同,即使进程具有特殊权限,此函数也会返回一个填充的数组。有关更多详细信息,请参阅 UnixLabels.unsafe_getenv 的文档。

val getenv : string -> string

返回与进程环境中变量关联的值,除非进程具有特殊权限。

val unsafe_getenv : string -> string

返回与进程环境中变量关联的值。

UnixLabels.getenv 不同,即使进程具有特殊权限,此函数也会返回该值。它被认为是不安全的,因为设置 UID 或设置 GID 程序的程序员必须注意避免在可执行文件的搜索路径、临时文件或日志的位置等地方使用恶意制作的环境变量。

val putenv : string -> string -> unit

putenv name value 设置与进程环境中变量关联的值。 name 是环境变量的名称,value 是其新的关联值。

进程处理

type process_status = Unix.process_status = 
| WEXITED of int (*

进程通过 exit 正常终止;参数是返回值。

*)
| WSIGNALED of int (*

进程被信号杀死;参数是信号编号。

*)
| WSTOPPED of int (*

进程被信号停止;参数是信号编号。

*)

进程的终止状态。有关标准信号编号的定义,请参阅模块 Sys。请注意,它们不是操作系统使用的编号。

在 Windows 上:仅使用 WEXITED(因为没有进程间信号),但使用特定的返回值来指示特殊的终止原因。在 Windows 文档中查找 NTSTATUS 值以解码此类错误返回值。特别是,STATUS_ACCESS_VIOLATION 错误代码是 32 位 0xC0000005:由于 Int32.of_int 0xC0000005-1073741819,因此 WEXITED -1073741819 等效于 Windows 上的 WSIGNALED Sys.sigsegv

type wait_flag = Unix.wait_flag = 
| WNOHANG (*

如果还没有子进程死亡,则不要阻塞,而是立即返回 pid 等于 0。

*)
| WUNTRACED (*

还报告接收停止信号的子进程。

*)

用于 UnixLabels.waitpid 的标志。

val execv : prog:string -> args:string array -> 'a

execv prog args 使用参数 args 和当前进程环境执行文件 prog 中的程序。请注意,第一个参数 args.(0) 通常是正在执行的程序的文件名,就像 Sys.argv.(0) 一样。这些 execv* 函数永远不会返回:如果成功,当前程序将被新程序替换。

在 Windows 上:CRT 只会生成一个新进程并退出当前进程。如果例如另一个进程正在等待当前进程,这将产生不想要的结果。建议改为使用 UnixLabels.create_process 或其中一个 open_process_* 函数。

val execve : prog:string -> args:string array -> env:string array -> 'a

UnixLabels.execv 相同,但第三个参数提供给执行程序的环境。

val execvp : prog:string -> args:string array -> 'a

UnixLabels.execv 相同,但程序在路径中搜索。

val execvpe : prog:string -> args:string array -> env:string array -> 'a

UnixLabels.execve 相同,但程序在路径中搜索。

val fork : unit -> int

派生一个新进程。返回的整数对于子进程为 0,对于父进程为子进程的 pid。如果 OCaml 进程是多核的(任何域已被派生),则它将失败。此外,如果已派生 Thread 模块中的任何线程,则子进程可能处于损坏状态。

val wait : unit -> int * process_status

等待直到一个子进程死亡,并返回其 pid 和终止状态。

val waitpid : mode:wait_flag list -> int -> int * process_status

UnixLabels.wait 相同,但等待给定 pid 的子进程。pid 为 -1 表示等待任何子进程。pid 为 0 表示等待与当前进程位于同一进程组中的任何子进程。负 pid 参数表示进程组。选项列表指示 waitpid 是否应该立即返回而不等待,以及它是否应该报告已停止的子进程。

在 Windows 上:只能等待给定 PID,而不是任何子进程。

val system : string -> process_status

执行给定的命令,等待直到它终止,并返回其终止状态。字符串由 shell /bin/sh(或 Windows 上的命令解释器 cmd.exe)解释,因此可以包含重定向、引号、变量等。为了正确引用文件名或命令参数中出现的空格和 shell 特殊字符,建议使用 Filename.quote_command。结果 WEXITED 127 表示 shell 无法执行。

val _exit : int -> 'a

立即终止调用进程,将给定的状态码返回给操作系统:通常为 0 表示没有错误,小正整数表示失败。与 exit 不同,Unix._exit 不会执行任何最终化操作:使用 at_exit 注册的函数不会被调用,输入/输出通道不会被刷新,C 运行时系统也不会被最终化。

Unix._exit 的典型用法是在 Unix.fork 操作之后,当子进程遇到致命错误并必须退出时。在这种情况下,最好不要在子进程中执行任何最终化操作,因为这些操作可能会干扰父进程执行的类似操作。例如,子进程不应刷新输出通道,因为父进程以后可能会再次刷新它们,从而导致重复输出。

val getpid : unit -> int

返回进程的 pid。

val getppid : unit -> int

返回父进程的 pid。

val nice : int -> int

更改进程优先级。整数参数将添加到“nice”值中。(“nice”值越高表示优先级越低。)返回新的 nice 值。

基本文件输入/输出

type file_descr = Unix.file_descr 

文件描述符的抽象类型。

val stdin : file_descr

标准输入的文件描述符。

val stdout : file_descr

标准输出的文件描述符。

val stderr : file_descr

标准错误的文件描述符。

type open_flag = Unix.open_flag = 
| O_RDONLY (*

以只读方式打开

*)
| O_WRONLY (*

以只写方式打开

*)
| O_RDWR (*

以读写方式打开

*)
| O_NONBLOCK (*

以非阻塞模式打开

*)
| O_APPEND (*

以追加方式打开

*)
| O_CREAT (*

如果不存在则创建

*)
| O_TRUNC (*

如果存在则截断为 0 长度

*)
| O_EXCL (*

如果存在则失败

*)
| O_NOCTTY (*

不要将此设备设为控制 tty

*)
| O_DSYNC (*

写入完成为“同步 I/O 数据完整性完成”

*)
| O_SYNC (*

写入完成为“同步 I/O 文件完整性完成”

*)
| O_RSYNC (*

读取完成为写入(取决于 O_SYNC/O_DSYNC)

*)
| O_SHARE_DELETE (*

仅 Windows:允许在仍然打开时删除文件

*)
| O_CLOEXEC (*

在由 UnixLabels.openfile 返回的描述符上设置 close-on-exec 标志。有关更多信息,请参见 UnixLabels.set_close_on_exec

*)
| O_KEEPEXEC (*

清除 close-on-exec 标志。目前,这是默认设置。

*)

UnixLabels.openfile 的标志。

type file_perm = int 

文件访问权限类型,例如 0o640 表示用户可读写、组可读、其他不可访问

val openfile : string ->
mode:open_flag list ->
perm:file_perm -> file_descr

使用给定的标志打开命名文件。第三个参数是如果创建文件要赋予的权限(请参见 UnixLabels.umask)。返回命名文件的描述符。

val close : file_descr -> unit

关闭文件描述符。

val fsync : file_descr -> unit

将文件缓冲区刷新到磁盘。

val read : file_descr -> buf:bytes -> pos:int -> len:int -> int

read fd ~buf ~pos ~len 从描述符 fd 中读取 len 个字节,将它们存储在字节序列 buf 中,从 buf 中的 pos 位置开始。返回实际读取的字节数。

val read_bigarray : file_descr ->
buf:('a, Bigarray.int8_unsigned_elt, Bigarray.c_layout)
Bigarray.Array1.t ->
pos:int -> len:int -> int

UnixLabels.read 相同,但将数据读入 bigarray 中。

val write : file_descr -> buf:bytes -> pos:int -> len:int -> int

write fd ~buf ~pos ~lenlen 个字节写入描述符 fd,从字节序列 buf 中获取它们,从 buff 中的 pos 位置开始。返回实际写入的字节数。 write 重复写入操作,直到所有字节都已写入或发生错误。

val write_bigarray : file_descr ->
buf:('a, Bigarray.int8_unsigned_elt, Bigarray.c_layout)
Bigarray.Array1.t ->
pos:int -> len:int -> int

UnixLabels.write 相同,但从 bigarray 中获取数据。

val single_write : file_descr -> buf:bytes -> pos:int -> len:int -> int

UnixLabels.write 相同,但尝试只写入一次。因此,如果发生错误,则 single_write 保证没有数据被写入。

val write_substring : file_descr -> buf:string -> pos:int -> len:int -> int

UnixLabels.write 相同,但从字符串而不是字节序列中获取数据。

val single_write_substring : file_descr -> buf:string -> pos:int -> len:int -> int

UnixLabels.single_write 相同,但从字符串而不是字节序列中获取数据。

val single_write_bigarray : file_descr ->
buf:('a, Bigarray.int8_unsigned_elt, Bigarray.c_layout)
Bigarray.Array1.t ->
pos:int -> len:int -> int

UnixLabels.single_write 相同,但从 bigarray 中获取数据。

与标准输入/输出库的接口

val in_channel_of_descr : file_descr -> in_channel

创建一个从给定描述符中读取的输入通道。该通道最初处于二进制模式;如果需要文本模式,请使用 set_binary_mode_in ic false。仅当描述符引用文件或管道时才支持文本模式,但如果它引用套接字,则不支持。

在 Windows 上:set_binary_mode_in 在使用此函数创建的通道上始终失败。

请注意,输入通道是缓冲的,因此可能从描述符中读取了比使用通道函数访问的字符更多的字符。通道还保留文件当前位置的副本。

使用 close_in ic 关闭由 in_channel_of_descr fd 返回的通道 ic 也会关闭底层描述符 fd。关闭通道 ic 和描述符 fd 两者是不正确的。

如果在同一个描述符上创建了多个通道,则必须关闭其中一个通道,而不能关闭其他通道。例如,考虑与套接字连接的描述符 s 和两个通道 ic = in_channel_of_descr soc = out_channel_of_descr s。建议的关闭协议是执行 close_out oc,它将缓冲的输出刷新到套接字,然后关闭套接字。ic 通道不能关闭,并且最终将由 GC 收集。

val out_channel_of_descr : file_descr -> out_channel

创建一个在给定描述符上写入的输出通道。该通道最初处于二进制模式;如果需要文本模式,请使用 set_binary_mode_out oc false。仅当描述符引用文件或管道时才支持文本模式,但如果它引用套接字,则不支持。

在 Windows 上:set_binary_mode_out 在使用此函数创建的通道上始终失败。

请注意,输出通道是缓冲的,因此您可能需要调用 flush 以确保所有数据都已发送到描述符。通道还保留文件当前位置的副本。

使用 close_out oc 关闭由 out_channel_of_descr fd 返回的通道 oc 也会关闭底层描述符 fd。关闭通道 ic 和描述符 fd 两者是不正确的。

请参见 Unix.in_channel_of_descr,了解在同一个描述符上创建了多个通道时的关闭协议。

val descr_of_in_channel : in_channel -> file_descr

返回与输入通道对应的描述符。

val descr_of_out_channel : out_channel -> file_descr

返回与输出通道对应的描述符。

查找和截断

type seek_command = Unix.seek_command = 
| SEEK_SET (*

表示相对于文件开头的定位

*)
| SEEK_CUR (*

表示相对于当前位置的定位

*)
| SEEK_END (*

表示相对于文件末尾的定位

*)

UnixLabels.lseek 的定位模式。

val lseek : file_descr -> int -> mode:seek_command -> int

设置文件描述符的当前位置,并返回结果偏移量(相对于文件开头)。

val truncate : string -> len:int -> unit

将命名文件截断为给定大小。

val ftruncate : file_descr -> len:int -> unit

将对应于给定描述符的文件截断为给定大小。

文件状态

type file_kind = Unix.file_kind = 
| S_REG (*

普通文件

*)
| S_DIR (*

目录

*)
| S_CHR (*

字符设备

*)
| S_BLK (*

块设备

*)
| S_LNK (*

符号链接

*)
| S_FIFO (*

命名管道

*)
| S_SOCK (*

套接字

*)
type stats = Unix.stats = {
   st_dev : int; (*

设备号

*)
   st_ino : int; (*

节点号

*)
   st_kind : file_kind; (*

文件类型

*)
   st_perm : file_perm; (*

访问权限

*)
   st_nlink : int; (*

链接数

*)
   st_uid : int; (*

所有者用户 ID

*)
   st_gid : int; (*

文件所属组的组 ID

*)
   st_rdev : int; (*

设备 ID(如果是特殊文件)

*)
   st_size : int; (*

大小(字节)

*)
   st_atime : float; (*

最后访问时间

*)
   st_mtime : float; (*

最后修改时间

*)
   st_ctime : float; (*

最后状态更改时间

*)
}

UnixLabels.stat 调用返回的信息。

val stat : string -> stats

返回指定文件的相关信息。

val lstat : string -> stats

UnixLabels.stat 相同,但如果文件是符号链接,则返回链接本身的信息。

val fstat : file_descr -> stats

返回与给定描述符关联的文件的相关信息。

val isatty : file_descr -> bool

如果给定文件描述符指向终端或控制台窗口,则返回 true,否则返回 false

大型文件上的文件操作

module LargeFile: sig .. end

大型文件上的文件操作。

将文件映射到内存

val map_file : file_descr ->
?pos:int64 ->
kind:('a, 'b) Bigarray.kind ->
layout:'c Bigarray.layout ->
shared:bool -> dims:int array -> ('a, 'b, 'c) Bigarray.Genarray.t

将文件作为 Bigarray 映射到内存。 map_file fd ~kind ~layout ~shared ~dims 返回一个类型为 kind,布局为 layout,维度如 dims 所指定的 Bigarray。此 Bigarray 中包含的数据是文件描述符 fd 所引用的文件的内容(例如,之前使用 UnixLabels.openfile 打开)。可选的 pos 参数是在要映射的数据中文件中的字节偏移量;默认值为 0(从文件开头映射)。

如果 sharedtrue,则对数组执行的所有修改都反映在文件中。这需要 fd 以写入权限打开。如果 sharedfalse,则对数组执行的修改仅在内存中完成,使用修改页面的写时复制;基础文件不受影响。

UnixLabels.map_file 比将整个文件读入 Bigarray、修改该 Bigarray 然后写入更有效。

为了自动调整 Bigarray 的维度以适应文件的实际大小,主维度(即,对于使用 C 布局的数组,是第一个维度,而对于使用 Fortran 布局的数组,是最后一个维度)可以指定为 -1。然后,UnixLabels.map_file 会根据文件的大小确定主维度。文件必须包含根据非主维度确定的整数个子数组,否则会引发 Failure

如果指定了 Bigarray 的所有维度,则会将文件大小与 Bigarray 的大小进行匹配。如果文件大于 Bigarray,则只有文件的初始部分被映射到 Bigarray。如果文件小于 Bigarray,则文件会自动扩展到 Bigarray 的大小。这需要对 fd 具有写入权限。

对数组的访问会进行边界检查,但边界由对 map_file 的初始调用确定。因此,您应该确保在访问映射文件时,没有其他进程修改映射文件,否则可能会引发 SIGBUS 信号。例如,当文件缩小时会发生这种情况。

如果参数验证失败,可能会引发 Invalid_argumentFailure

文件名操作

val unlink : string -> unit

删除指定文件。

如果指定文件是目录,则会引发

  • EPERM 在符合 POSIX 标准的系统上
  • EISDIR 在 Linux >= 2.1.132 上
  • EACCESS 在 Windows 上
val rename : src:string -> dst:string -> unit

rename ~src ~dst 将文件的名称从 src 更改为 dst,根据需要在目录之间移动。如果 dst 已经存在,则其内容将被 src 的内容替换。根据操作系统的不同,dst 的元数据(权限、所有者等)可以保留或被 src 的元数据替换。

val link : ?follow:bool -> src:string -> dst:string -> unit

link ?follow ~src ~dst 为名为 src 的文件创建一个名为 dst 的硬链接。

follow:指示是遵循 src 符号链接,还是创建指向 src 本身的硬链接。在 Unix 系统上,这是使用 linkat(2) 函数完成的。如果未提供 ?follow,则使用 link(2) 函数,其行为是操作系统相关的,但更广泛地可用。
val realpath : string -> string

realpath pp 的绝对路径名,它是通过解析所有额外的 / 字符、相对路径段和符号链接获得的。

文件权限和所有权

type access_permission = Unix.access_permission = 
| R_OK (*

读取权限

*)
| W_OK (*

写入权限

*)
| X_OK (*

执行权限

*)
| F_OK (*

文件存在

*)

UnixLabels.access 调用的标志。

val chmod : string -> perm:file_perm -> unit

更改指定文件的权限。

val fchmod : file_descr -> perm:file_perm -> unit

更改已打开文件的权限。

val chown : string -> uid:int -> gid:int -> unit

更改指定文件的拥有者 uid 和拥有者 gid。

val fchown : file_descr -> uid:int -> gid:int -> unit

更改已打开文件的拥有者 uid 和拥有者 gid。

val umask : file_perm -> file_perm

设置进程的文件模式创建掩码,并返回之前的掩码。

val access : string -> perm:access_permission list -> unit

检查进程是否对指定文件具有给定的权限。

在 Windows 上:无法测试执行权限 X_OK,只测试读取权限。

文件描述符操作

val dup : ?cloexec:bool -> file_descr -> file_descr

返回一个新的文件描述符,它引用与给定描述符相同的文件。有关 cloexec 可选参数的文档,请参阅 UnixLabels.set_close_on_exec

val dup2 : ?cloexec:bool ->
src:file_descr -> dst:file_descr -> unit

dup2 ~src ~dstsrc 复制到 dst,如果 dst 已经打开,则关闭它。有关 cloexec 可选参数的文档,请参阅 UnixLabels.set_close_on_exec

val set_nonblock : file_descr -> unit

在给定描述符上设置“非阻塞”标志。当设置非阻塞标志时,在暂时没有可用数据的描述符上读取会导致引发 EAGAINEWOULDBLOCK 错误,而不是阻塞;在暂时没有写入空间的描述符上写入也会引发 EAGAINEWOULDBLOCK

val clear_nonblock : file_descr -> unit

清除给定描述符上的“非阻塞”标志。请参阅 UnixLabels.set_nonblock

val set_close_on_exec : file_descr -> unit

在给定描述符上设置“关闭-在-执行”标志。当当前进程使用 execcreate_processopen_process 函数启动另一个程序时,带有关闭-在-执行标志的描述符会自动关闭。

将文件描述符(例如,在私有文件上打开的文件描述符)泄漏到外部程序通常是一个安全漏洞:然后,程序将获得对私有文件的访问权限,并可以使用它做一些不好的事情。因此,强烈建议将所有文件描述符设置为“关闭-在-执行”,除非在极少数情况下,文件描述符实际上需要传递到另一个程序。

将文件描述符设置为“关闭-在-执行”的最佳方法是在这种状态下创建它。为此,openfile 函数具有 O_CLOEXECO_KEEPEXEC 标志,分别用于强制执行“关闭-在-执行”模式或“保持-在-执行”模式。Unix 模块中创建文件描述符的所有其他操作都有一个可选参数 ?cloexec:bool,用于指示是否应该在“关闭-在-执行”模式(通过写入 ~cloexec:true)或“保持-在-执行”模式(通过写入 ~cloexec:false)下创建文件描述符。出于历史原因,如果未给出 cloexec 可选参数,则默认文件描述符创建模式为“保持-在-执行”。这不是安全的默认值,因此强烈建议将显式的 cloexec 参数传递给创建文件描述符的操作。

cloexec 可选参数和 O_KEEPEXEC 标志是在 OCaml 4.05 中引入的。早些时候,常见的做法是在默认的“保持-在-执行”模式下创建文件描述符,然后在这些新创建的文件描述符上调用 set_close_on_exec。这不如在“关闭-在-执行”模式下创建文件描述符安全,因为在多线程程序中,在创建文件描述符和 set_close_on_exec 完成之间存在漏洞窗口。如果另一个线程在此窗口期间生成另一个程序,则描述符将泄漏,因为它仍然处于“保持-在-执行”模式。

关于由 ~cloexec:true 或使用 O_CLOEXEC 标志提供的原子性保证:在所有平台上,都保证并发执行的 Caml 线程无法通过启动新进程泄漏描述符。在 Linux 上,此保证扩展到并发执行的 C 线程。截至 2017 年 2 月,其他操作系统缺乏必要的系统调用,并且仍然在 C 线程可以看到新创建的文件描述符处于“保持-在-执行”模式期间暴露漏洞窗口。

val clear_close_on_exec : file_descr -> unit

清除给定描述符上的“关闭-在-执行”标志。请参阅 UnixLabels.set_close_on_exec

目录

val mkdir : string -> perm:file_perm -> unit

使用给定的权限创建目录(请参阅 UnixLabels.umask)。

val rmdir : string -> unit

删除空目录。

val chdir : string -> unit

更改进程工作目录。

val getcwd : unit -> string

返回当前工作目录的名称。

val chroot : string -> unit

更改进程根目录。

type dir_handle = Unix.dir_handle 

打开目录上的描述符的类型。

val opendir : string -> dir_handle

在目录上打开描述符

val readdir : dir_handle -> string

返回目录中的下一个条目。

val rewinddir : dir_handle -> unit

将描述符重新定位到目录的开头

val closedir : dir_handle -> unit

关闭目录描述符。

管道和重定向

val pipe : ?cloexec:bool -> unit -> file_descr * file_descr

创建管道。结果的第一部分是为读取打开的,即管道的出口。第二部分是为写入打开的,即管道的入口。有关 cloexec 可选参数的文档,请参阅 UnixLabels.set_close_on_exec

val mkfifo : string -> perm:file_perm -> unit

使用给定的权限创建一个命名管道(参见 UnixLabels.umask)。

高级进程和重定向管理

val create_process : prog:string ->
args:string array ->
stdin:file_descr ->
stdout:file_descr -> stderr:file_descr -> int

create_process ~prog ~args ~stdin ~stdout ~stderr 创建一个新的进程,该进程执行文件 prog 中的程序,参数为 args。请注意,第一个参数 args.(0) 通常是正在执行的程序的文件名,就像 Sys.argv.(0) 一样。新进程的 pid 会立即返回;新进程与当前进程并发执行。新进程的标准输入和输出连接到描述符 stdinstdoutstderr。例如,将 Unix.stdout 传递给 stdout 将阻止重定向,并使新进程与当前进程具有相同的标准输出。可执行文件 prog 在路径中进行搜索。新进程与当前进程具有相同的环境。

val create_process_env : prog:string ->
args:string array ->
env:string array ->
stdin:file_descr ->
stdout:file_descr -> stderr:file_descr -> int

create_process_env ~prog ~args ~env ~stdin ~stdout ~stderr 的工作原理与 UnixLabels.create_process 相同,只是额外的参数 env 指定了传递给程序的环境。

val open_process_in : string -> in_channel

高级管道和进程管理。此函数与程序并行运行给定的命令。命令的标准输出被重定向到管道,可以通过返回的输入通道读取。命令由 shell /bin/sh(或 Windows 上的 cmd.exe)解释,参见 UnixLabels.system。函数 Filename.quote_command 可用于根据正在使用的 shell 对命令及其参数进行适当的引用。如果不需要通过 shell 运行命令,则可以使用 UnixLabels.open_process_args_in 作为 UnixLabels.open_process_in 的更健壮、更高效的替代方法。

val open_process_out : string -> out_channel

UnixLabels.open_process_in 相同,但将命令的标准输入重定向到管道。写入返回的输出通道的数据将发送到命令的标准输入。警告:对输出通道的写入会被缓冲,因此请务必在正确的时间调用 flush 以确保正确的同步。如果不需要通过 shell 运行命令,则可以使用 UnixLabels.open_process_args_out 而不是 UnixLabels.open_process_out

val open_process : string -> in_channel * out_channel

UnixLabels.open_process_out 相同,但将命令的标准输入和标准输出都重定向到连接到两个返回通道的管道。输入通道连接到命令的输出,输出通道连接到命令的输入。如果不需要通过 shell 运行命令,则可以使用 UnixLabels.open_process_args 而不是 UnixLabels.open_process

val open_process_full : string ->
env:string array ->
in_channel * out_channel * in_channel

类似于 UnixLabels.open_process,但第二个参数指定了传递给命令的环境。结果是一个三元组通道,分别连接到命令的标准输出、标准输入和标准错误。如果不需要通过 shell 运行命令,则可以使用 UnixLabels.open_process_args_full 而不是 UnixLabels.open_process_full

val open_process_args : string -> string array -> in_channel * out_channel

open_process_args prog args 运行程序 prog,参数为 args。请注意,第一个参数 args.(0) 通常是正在执行的程序的文件名,就像 Sys.argv.(0) 一样。新进程与当前进程并发执行。新进程的标准输入和输出被重定向到管道,可以通过返回的通道分别进行读取和写入。输入通道连接到程序的输出,输出通道连接到程序的输入。

警告:对输出通道的写入会被缓冲,因此请务必在正确的时间调用 flush 以确保正确的同步。

在路径中搜索可执行文件 prog。此行为在 4.12 中发生更改;之前 prog 仅在当前目录中查找。

新进程与当前进程具有相同的环境。

val open_process_args_in : string -> string array -> in_channel

UnixLabels.open_process_args 相同,但仅重定向新进程的标准输出。

val open_process_args_out : string -> string array -> out_channel

UnixLabels.open_process_args 相同,但仅重定向新进程的标准输入。

val open_process_args_full : string ->
string array ->
string array -> in_channel * out_channel * in_channel

类似于 UnixLabels.open_process_args,但第三个参数指定了传递给新进程的环境。结果是一个三元组通道,分别连接到程序的标准输出、标准输入和标准错误。

val process_in_pid : in_channel -> int

返回通过 UnixLabels.open_process_args_in 打开的进程的 pid 或通过 UnixLabels.open_process_in 打开的 shell 的 pid。

val process_out_pid : out_channel -> int

返回通过 UnixLabels.open_process_args_out 打开的进程的 pid 或通过 UnixLabels.open_process_out 打开的 shell 的 pid。

val process_pid : in_channel * out_channel -> int

返回通过 UnixLabels.open_process_args 打开的进程的 pid 或通过 UnixLabels.open_process_args 打开的 shell 的 pid。

val process_full_pid : in_channel * out_channel * in_channel -> int

返回通过 UnixLabels.open_process_args_full 打开的进程的 pid 或通过 UnixLabels.open_process_full 打开的 shell 的 pid。

val close_process_in : in_channel -> process_status

关闭通过 UnixLabels.open_process_in 打开的通道,等待关联的命令终止,并返回其终止状态。

val close_process_out : out_channel -> process_status

关闭通过 UnixLabels.open_process_out 打开的通道,等待关联的命令终止,并返回其终止状态。

val close_process : in_channel * out_channel -> process_status

关闭通过 UnixLabels.open_process 打开的通道,等待关联的命令终止,并返回其终止状态。

val close_process_full : in_channel * out_channel * in_channel ->
process_status

关闭通过 UnixLabels.open_process_full 打开的通道,等待关联的命令终止,并返回其终止状态。

val symlink : ?to_dir:bool -> src:string -> dst:string -> unit

symlink ?to_dir ~src ~dst 将文件 dst 创建为指向文件 src 的符号链接。在 Windows 上,~to_dir 指示符号链接指向目录还是文件;如果省略,symlink 将使用 stat 检查 src 并相应地选择,如果 src 不存在,则假定为 false(出于此原因,建议在新代码中指定 ~to_dir 参数)。在 Unix 上,~to_dir 被忽略。

Windows 符号链接在 Windows Vista 及更高版本中可用。Windows 符号链接与其 POSIX 对应物之间存在一些重要区别。

Windows 符号链接有两种类型:目录和常规,它们指定符号链接指向目录还是文件。类型必须正确 - 实际上指向文件的目录符号链接不能使用 chdir 选择,而实际上指向目录的文件符号链接不能读取或写入(请注意,Cygwin 的模拟层会忽略这种区别)。

当创建指向现有目标的符号链接时,这种区别并不重要,symlink 将自动创建正确类型的符号链接。当创建指向不存在的目标的符号链接时,这种区别就变得很重要。

另一个需要注意的是,默认情况下,符号链接是特权操作。管理员始终需要以提升的权限运行(或禁用 UAC),并且默认情况下,普通用户帐户需要通过本地安全策略 (secpol.msc) 或 Active Directory 授予 SeCreateSymbolicLinkPrivilege。

UnixLabels.has_symlink 可用于检查进程是否能够创建符号链接。

val has_symlink : unit -> bool

如果用户能够创建符号链接,则返回 true。在 Windows 上,这表示用户不仅拥有 SeCreateSymbolicLinkPrivilege,而且还以提升的权限运行(如果需要)。在其他平台上,这仅表示 symlink 系统调用可用。

val readlink : string -> string

读取符号链接的内容。

轮询

val select : read:file_descr list ->
write:file_descr list ->
except:file_descr list ->
timeout:float ->
file_descr list * file_descr list *
file_descr list

等待某些通道上的某些输入/输出操作变得可能。这三个列表参数分别是:要检查读取的描述符集(第一个参数)、要检查写入的描述符集(第二个参数)或要检查异常条件的描述符集(第三个参数)。第四个参数是最大超时时间(以秒为单位);负的第四个参数表示没有超时(无限等待)。结果由三个描述符集组成:准备读取的描述符集(第一个组件)、准备写入的描述符集(第二个组件)以及挂起异常条件的描述符集(第三个组件)。

锁定

type lock_command = Unix.lock_command = 
| F_ULOCK (*

解锁一个区域

*)
| F_LOCK (*

锁定一个区域以供写入,如果已被锁定则阻塞

*)
| F_TLOCK (*

锁定一个区域以供写入,如果已被锁定则失败

*)
| F_TEST (*

测试一个区域以查看其他进程的锁

*)
| F_RLOCK (*

锁定一个区域以供读取,如果已被锁定则阻塞

*)
| F_TRLOCK (*

锁定一个区域以供读取,如果已被锁定则失败

*)

UnixLabels.lockf 的命令。

val lockf : file_descr -> mode:lock_command -> len:int -> unit

lockf fd ~mode ~len 对作为 fd 打开的文件的一个区域进行锁定。该区域从 fd 的当前读/写位置(由 UnixLabels.lseek 设置)开始,如果 len 为正,则向前扩展 len 个字节,如果 len 为负,则向后扩展 len 个字节,如果 len 为零,则扩展到文件末尾。写锁阻止任何其他进程获取该区域的读锁或写锁。读锁阻止任何其他进程获取该区域的写锁,但允许其他进程获取该区域的读锁。

命令 F_LOCKF_TLOCK 尝试对指定区域加写锁。 命令 F_RLOCKF_TRLOCK 尝试对指定区域加读锁。 如果另一个进程加的锁阻止当前进程获取锁,F_LOCKF_RLOCK 会阻塞,直到这些锁被移除,而 F_TLOCKF_TRLOCK 会立即抛出异常并失败。 命令 F_ULOCK 会移除当前进程在指定区域加的任何锁。 最后,命令 F_TEST 会测试是否可以在指定区域加写锁,但不实际加锁。 如果成功,则立即返回;否则失败。

当一个进程尝试锁定已经被同一个进程锁定的文件区域时会发生什么取决于操作系统。 在 POSIX 兼容系统上,第二次锁定操作会成功,并可能将旧锁从读锁提升为写锁。 在 Windows 上,第二次锁定操作会阻塞或失败。

信号

注意:信号处理程序的安装是通过函数 Sys.signalSys.set_signal 完成的。

val kill : pid:int -> signal:int -> unit

kill ~pid ~signal 向具有 ID 为 pid 的进程发送信号号为 signal 的信号。

在 Windows 上:只模拟了 Sys.sigkill 信号。

type sigprocmask_command = Unix.sigprocmask_command = 
| SIG_SETMASK
| SIG_BLOCK
| SIG_UNBLOCK
val sigprocmask : mode:sigprocmask_command -> int list -> int list

sigprocmask ~mode sigs 更改被阻塞信号集。 如果 modeSIG_SETMASK,则被阻塞信号被设置为列表 sigs 中的那些信号。 如果 modeSIG_BLOCK,则将 sigs 中的信号添加到被阻塞信号集中。 如果 modeSIG_UNBLOCK,则将 sigs 中的信号从被阻塞信号集中移除。 sigprocmask 返回先前被阻塞信号集。

当加载 Thread 模块的 systhreads 版本时,此函数会重定向到 Thread.sigmask。 也就是说,sigprocmask 只改变当前线程的屏蔽。

val sigpending : unit -> int list

返回当前挂起的被阻塞信号集。

val sigsuspend : int list -> unit

sigsuspend sigs 原子地将被阻塞信号设置为 sigs,并等待非忽略、非阻塞信号被传递。 返回时,被阻塞信号将重置为其初始值。

val pause : unit -> unit

等待非忽略、非阻塞信号被传递。

时间函数

type process_times = Unix.process_times = {
   tms_utime : float; (*

进程的用户时间

*)
   tms_stime : float; (*

进程的系统时间

*)
   tms_cutime : float; (*

子进程的用户时间

*)
   tms_cstime : float; (*

子进程的系统时间

*)
}

进程的执行时间(CPU 时间)。

type tm = Unix.tm = {
   tm_sec : int; (*

秒 0..60

*)
   tm_min : int; (*

分钟 0..59

*)
   tm_hour : int; (*

小时 0..23

*)
   tm_mday : int; (*

月中的日期 1..31

*)
   tm_mon : int; (*

年的月份 0..11

*)
   tm_year : int; (*

年份 - 1900

*)
   tm_wday : int; (*

星期几(星期日为 0)

*)
   tm_yday : int; (*

年的日期 0..365

*)
   tm_isdst : bool; (*

是否启用了夏令时

*)
}

表示墙上时钟时间和日历日期的类型。

val time : unit -> float

返回自 1970 年 1 月 1 日 00:00:00 GMT 以来的当前时间(以秒为单位)。

val gettimeofday : unit -> float

UnixLabels.time 相同,但分辨率优于 1 秒。

val gmtime : float -> tm

将时间(以秒为单位,由 UnixLabels.time 返回)转换为日期和时间。 假设为 UTC(协调世界时),也称为 GMT。 要执行逆转换,将 TZ 环境变量设置为“UTC”,使用 UnixLabels.mktime,然后恢复 TZ 的原始值。

val localtime : float -> tm

将时间(以秒为单位,由 UnixLabels.time 返回)转换为日期和时间。 假设为本地时区。 执行逆转换的函数是 UnixLabels.mktime

val mktime : tm -> float * tm

将由 tm 参数指定的日期和时间转换为时间(以秒为单位,由 UnixLabels.time 返回)。 tmtm_isdsttm_wdaytm_yday 字段被忽略。 还会返回给定 tm 记录的规范化副本,其中 tm_wdaytm_ydaytm_isdst 字段已从其他字段重新计算,其他字段已规范化(因此,例如,10 月 40 日将更改为 11 月 9 日)。 tm 参数被解释为本地时区。

val alarm : int -> int

在给定的秒数之后安排 SIGALRM 信号。

val sleep : int -> unit

停止执行给定的秒数。

val sleepf : float -> unit

停止执行给定的秒数。 与 sleep 相同,但支持秒的几分之一。

val times : unit -> process_times

返回进程的执行时间。

在 Windows 上:部分实现,不会报告子进程的计时。

val utimes : string -> access:float -> modif:float -> unit

设置文件的最后访问时间(第二个参数)和最后修改时间(第三个参数)。 时间以自 1970 年 1 月 1 日 00:00:00 GMT 以来的秒数表示。 如果两个时间都为 0.0,则访问时间和最后修改时间都将设置为当前时间。

type interval_timer = Unix.interval_timer = 
| ITIMER_REAL (*

在实际时间中递减,并在到期时发送信号 SIGALRM

*)
| ITIMER_VIRTUAL (*

在进程虚拟时间中递减,并在到期时发送 SIGVTALRM

*)
| ITIMER_PROF (*

(用于分析)在进程运行时和系统代表进程运行时都递减;在到期时发送 SIGPROF

*)

三种间隔计时器。

type interval_timer_status = Unix.interval_timer_status = {
   it_interval : float; (*

周期

*)
   it_value : float; (*

计时器的当前值

*)
}

描述间隔计时器状态的类型

val getitimer : interval_timer -> interval_timer_status

返回给定间隔计时器的当前状态。

val setitimer : interval_timer ->
interval_timer_status -> interval_timer_status

setitimer t s 设置间隔计时器 t 并返回其先前状态。 s 参数的解释如下:s.it_value(如果非零)是到下一个计时器到期的时间;s.it_interval(如果非零)指定在计时器到期时用于重新加载 it_value 的值。 将 s.it_value 设置为零将禁用计时器。 将 s.it_interval 设置为零会导致计时器在下一个到期后被禁用。

用户 ID,组 ID

val getuid : unit -> int

返回执行进程的用户 ID。

在 Windows 上:始终返回 1

val geteuid : unit -> int

返回进程运行的有效用户 ID。

在 Windows 上:始终返回 1

val setuid : int -> unit

设置进程的真实用户 ID 和有效用户 ID。

val getgid : unit -> int

返回执行进程的组 ID。

在 Windows 上:始终返回 1

val getegid : unit -> int

返回进程运行的有效组 ID。

在 Windows 上:始终返回 1

val setgid : int -> unit

设置进程的真实组 ID 和有效组 ID。

val getgroups : unit -> int array

返回执行进程的用户所属的组列表。

在 Windows 上:始终返回 [|1|]

val setgroups : int array -> unit

setgroups groups 设置调用进程的补充组 ID。 需要适当的权限。

val initgroups : string -> int -> unit

initgroups user group 通过读取组数据库 /etc/group 并使用 user 是成员的所有组来初始化组访问列表。 附加组 group 也将添加到列表中。

type passwd_entry = Unix.passwd_entry = {
   pw_name : string;
   pw_passwd : string;
   pw_uid : int;
   pw_gid : int;
   pw_gecos : string;
   pw_dir : string;
   pw_shell : string;
}

passwd 数据库中条目的结构。

type group_entry = Unix.group_entry = {
   gr_name : string;
   gr_passwd : string;
   gr_gid : int;
   gr_mem : string array;
}

groups 数据库中条目的结构。

val getlogin : unit -> string

返回执行进程的用户登录名。

val getpwnam : string -> passwd_entry

passwd 中查找具有给定名称的条目。

val getgrnam : string -> group_entry

group 中查找具有给定名称的条目。

val getpwuid : int -> passwd_entry

passwd 中查找具有给定用户 ID 的条目。

val getgrgid : int -> group_entry

group 中查找具有给定组 ID 的条目。

互联网地址

type inet_addr = Unix.inet_addr 

Internet 地址的抽象类型。

val inet_addr_of_string : string -> inet_addr

从 Internet 地址的可打印表示形式转换为其内部表示形式。 参数字符串包含 4 个用点分隔的数字(XXX.YYY.ZZZ.TTT)用于 IPv4 地址,以及最多 8 个用冒号分隔的数字用于 IPv6 地址。

val string_of_inet_addr : inet_addr -> string

返回给定 Internet 地址的可打印表示形式。 有关可打印表示形式的描述,请参阅 UnixLabels.inet_addr_of_string

val inet_addr_any : inet_addr

一个特殊的 IPv4 地址,仅与 bind 一起使用,表示主机拥有的所有 Internet 地址。

val inet_addr_loopback : inet_addr

表示主机(127.0.0.1)的特殊 IPv4 地址。

val inet6_addr_any : inet_addr

一个特殊的 IPv6 地址,仅与 bind 一起使用,表示主机拥有的所有 Internet 地址。

val inet6_addr_loopback : inet_addr

表示主机(::1)的特殊 IPv6 地址。

val is_inet6_addr : inet_addr -> bool

给定的 inet_addr 是否为 IPv6 地址。

套接字

type socket_domain = Unix.socket_domain = 
| PF_UNIX (*

Unix 域

*)
| PF_INET (*

Internet 域 (IPv4)

*)
| PF_INET6 (*

Internet 域 (IPv6)

*)

套接字域的类型。 并非所有平台都支持 IPv6 套接字(类型 PF_INET6)。

在 Windows 上:自 4.14.0 起支持 PF_UNIX,在 Windows 10 1803 及更高版本上支持。

type socket_type = Unix.socket_type = 
| SOCK_STREAM (*

流套接字

*)
| SOCK_DGRAM (*

数据报套接字

*)
| SOCK_RAW (*

原始套接字

*)
| SOCK_SEQPACKET (*

排序数据包套接字

*)

套接字类型的种类,指定通信的语义。SOCK_SEQPACKET 出于完整性而包含在内,但很少受操作系统支持,并且需要此库中不可用的系统调用。

type sockaddr = Unix.sockaddr = 
| ADDR_UNIX of string
| ADDR_INET of inet_addr * int

套接字地址的类型。ADDR_UNIX name 是 Unix 域中的套接字地址;name 是文件系统中的文件名。ADDR_INET(addr,port) 是互联网域中的套接字地址;addr 是机器的互联网地址,port 是端口号。

val socket : ?cloexec:bool ->
domain:socket_domain ->
kind:socket_type -> protocol:int -> file_descr

在给定域中创建一个新的套接字,并使用给定的类型。第三个参数是协议类型;0 选择该类型套接字的默认协议。有关 cloexec 可选参数的文档,请参见 UnixLabels.set_close_on_exec

val domain_of_sockaddr : sockaddr -> socket_domain

返回适合给定套接字地址的套接字域。

val socketpair : ?cloexec:bool ->
domain:socket_domain ->
kind:socket_type ->
protocol:int -> file_descr * file_descr

创建一个连接在一起的无名套接字对。有关 cloexec 可选参数的文档,请参见 UnixLabels.set_close_on_exec

val accept : ?cloexec:bool ->
file_descr -> file_descr * sockaddr

接受给定套接字上的连接。返回的描述符是连接到客户端的套接字;返回的地址是连接客户端的地址。有关 cloexec 可选参数的文档,请参见 UnixLabels.set_close_on_exec

val bind : file_descr -> addr:sockaddr -> unit

将套接字绑定到地址。

val connect : file_descr -> addr:sockaddr -> unit

将套接字连接到地址。

val listen : file_descr -> max:int -> unit

为接收连接请求设置套接字。整数参数是待处理请求的最大数量。

type shutdown_command = Unix.shutdown_command = 
| SHUTDOWN_RECEIVE (*

关闭接收

*)
| SHUTDOWN_SEND (*

关闭发送

*)
| SHUTDOWN_ALL (*

同时关闭两者

*)

用于 shutdown 的命令类型。

val shutdown : file_descr -> mode:shutdown_command -> unit

关闭套接字连接。SHUTDOWN_SEND 作为第二个参数会导致连接另一端上的读取返回文件结束条件。SHUTDOWN_RECEIVE 会导致连接另一端上的写入返回关闭管道条件(SIGPIPE 信号)。

val getsockname : file_descr -> sockaddr

返回给定套接字的地址。

val getpeername : file_descr -> sockaddr

返回连接到给定套接字的主机地址。

type msg_flag = Unix.msg_flag = 
| MSG_OOB
| MSG_DONTROUTE
| MSG_PEEK
val recv : file_descr ->
buf:bytes -> pos:int -> len:int -> mode:msg_flag list -> int

从连接的套接字接收数据。

val recvfrom : file_descr ->
buf:bytes ->
pos:int ->
len:int -> mode:msg_flag list -> int * sockaddr

从未连接的套接字接收数据。

val send : file_descr ->
buf:bytes -> pos:int -> len:int -> mode:msg_flag list -> int

通过连接的套接字发送数据。

val send_substring : file_descr ->
buf:string -> pos:int -> len:int -> mode:msg_flag list -> int

send 相同,但从字符串而不是字节序列中获取数据。

val sendto : file_descr ->
buf:bytes ->
pos:int ->
len:int -> mode:msg_flag list -> addr:sockaddr -> int

通过未连接的套接字发送数据。

val sendto_substring : file_descr ->
buf:string ->
pos:int ->
len:int -> mode:msg_flag list -> sockaddr -> int

sendto 相同,但从字符串而不是字节序列中获取数据。

套接字选项

type socket_bool_option = Unix.socket_bool_option = 
| SO_DEBUG (*

记录调试信息

*)
| SO_BROADCAST (*

允许发送广播消息

*)
| SO_REUSEADDR (*

允许为 bind 重用本地地址

*)
| SO_KEEPALIVE (*

保持连接活动

*)
| SO_DONTROUTE (*

绕过标准路由算法

*)
| SO_OOBINLINE (*

将带外数据保留在行内

*)
| SO_ACCEPTCONN (*

报告套接字监听是否已启用

*)
| TCP_NODELAY (*

控制 TCP 套接字的 Nagle 算法

*)
| IPV6_ONLY (*

禁止将 IPv6 套接字绑定到 IPv4 地址

*)
| SO_REUSEPORT (*

允许重用地址和端口绑定

*)

可以使用 UnixLabels.getsockopt 查询,并可以使用 UnixLabels.setsockopt 修改的套接字选项。这些选项具有布尔值(true/false)。

type socket_int_option = Unix.socket_int_option = 
| SO_SNDBUF (*

发送缓冲区的大小

*)
| SO_RCVBUF (*

接收缓冲区的大小

*)
| SO_ERROR (*
已弃用。 请改用 Unix.getsockopt_error。

已弃用。请改用 UnixLabels.getsockopt_error

*)
| SO_TYPE (*

报告套接字类型

*)
| SO_RCVLOWAT (*

对输入操作进行处理的最小字节数

*)
| SO_SNDLOWAT (*

对输出操作进行处理的最小字节数

*)

可以使用 UnixLabels.getsockopt_int 查询,并可以使用 UnixLabels.setsockopt_int 修改的套接字选项。这些选项具有整数值。

type socket_optint_option = Unix.socket_optint_option = 
| SO_LINGER (*

是否在存在数据的关闭连接上停留,以及停留多长时间(以秒为单位)

*)

可以使用 UnixLabels.getsockopt_optint 查询,并可以使用 UnixLabels.setsockopt_optint 修改的套接字选项。这些选项的值类型为 int option,其中 None 表示“禁用”。

type socket_float_option = Unix.socket_float_option = 
| SO_RCVTIMEO (*

输入操作的超时时间

*)
| SO_SNDTIMEO (*

输出操作的超时时间

*)

可以使用 UnixLabels.getsockopt_float 查询,并可以使用 UnixLabels.setsockopt_float 修改的套接字选项。这些选项具有表示以秒为单位的时间的浮点值。值 0 表示无限超时。

val getsockopt : file_descr -> socket_bool_option -> bool

返回给定套接字中布尔值选项的当前状态。

val setsockopt : file_descr -> socket_bool_option -> bool -> unit

在给定套接字中设置或清除布尔值选项。

val getsockopt_int : file_descr -> socket_int_option -> int

UnixLabels.getsockopt 相同,用于整数值套接字选项。

val setsockopt_int : file_descr -> socket_int_option -> int -> unit

UnixLabels.setsockopt 相同,用于整数值套接字选项。

val getsockopt_optint : file_descr -> socket_optint_option -> int option

UnixLabels.getsockopt 相同,用于值为 int option 的套接字选项。

val setsockopt_optint : file_descr ->
socket_optint_option -> int option -> unit

UnixLabels.setsockopt 相同,用于值为 int option 的套接字选项。

val getsockopt_float : file_descr -> socket_float_option -> float

UnixLabels.getsockopt 相同,用于值为浮点数的套接字选项。

val setsockopt_float : file_descr -> socket_float_option -> float -> unit

UnixLabels.setsockopt 相同,用于值为浮点数的套接字选项。

val getsockopt_error : file_descr -> error option

返回与给定套接字关联的错误条件,并清除它。

高级网络连接函数

val open_connection : sockaddr -> in_channel * out_channel

连接到给定地址的服务器。返回一对连接到服务器的缓冲通道。请记住在适当的时候对输出通道调用 flush,以确保正确的同步。

open_connection 返回的两个通道共享一个指向套接字的描述符。因此,当连接结束后,你应该对输出通道调用 close_out,这也会关闭底层套接字。不要对输入通道调用 close_in;它最终会由 GC 收集。

val shutdown_connection : in_channel -> unit

“关闭”使用 UnixLabels.open_connection 建立的连接;也就是说,将文件结束条件传输到连接另一端读取的服务器。这不会关闭套接字和连接使用的通道。有关连接结束后如何关闭它们,请参见 Unix.open_connection

val establish_server : (in_channel -> out_channel -> unit) ->
addr:sockaddr -> unit

在给定地址上建立服务器。作为第一个参数给出的函数针对每个连接调用,并使用连接到客户端的两个缓冲通道。针对每个连接创建新的进程。UnixLabels.establish_server 函数永远不会正常返回。

给定函数的两个通道共享一个指向套接字的描述符。函数不需要关闭通道,因为这会在函数返回时自动发生。如果函数更喜欢显式关闭,它应该使用 close_out 关闭输出通道,并将输入通道保持未关闭状态,原因在 Unix.in_channel_of_descr 中进行了说明。

主机和协议数据库

type host_entry = Unix.host_entry = {
   h_name : string;
   h_aliases : string array;
   h_addrtype : socket_domain;
   h_addr_list : inet_addr array;
}

hosts 数据库中条目的结构。

type protocol_entry = Unix.protocol_entry = {
   p_name : string;
   p_aliases : string array;
   p_proto : int;
}

protocols 数据库中条目的结构。

type service_entry = Unix.service_entry = {
   s_name : string;
   s_aliases : string array;
   s_port : int;
   s_proto : string;
}

services 数据库中条目的结构。

val gethostname : unit -> string

返回本地主机的名称。

val gethostbyname : string -> host_entry

hosts 中查找具有给定名称的条目。

val gethostbyaddr : inet_addr -> host_entry

hosts 中查找具有给定地址的条目。

val getprotobyname : string -> protocol_entry

protocols 中查找具有给定名称的条目。

val getprotobynumber : int -> protocol_entry

protocols 中查找具有给定协议号的条目。

val getservbyname : string -> protocol:string -> service_entry

services 中查找具有给定名称的条目。

val getservbyport : int -> protocol:string -> service_entry

services 中查找具有给定服务号的条目。

type addr_info = Unix.addr_info = {
   ai_family : socket_domain; (*

套接字域

*)
   ai_socktype : socket_type; (*

套接字类型

*)
   ai_protocol : int; (*

套接字协议号

*)
   ai_addr : sockaddr; (*

地址

*)
   ai_canonname : string; (*

规范主机名

*)
}

UnixLabels.getaddrinfo 返回的地址信息。

type getaddrinfo_option = Unix.getaddrinfo_option = 
| AI_FAMILY of socket_domain (*

施加给定的套接字域

*)
| AI_SOCKTYPE of socket_type (*

施加给定的套接字类型

*)
| AI_PROTOCOL of int (*

施加给定的协议

*)
| AI_NUMERICHOST (*

不要调用名称解析器,预期为数字 IP 地址

*)
| AI_CANONNAME (*

填充结果的 ai_canonname 字段

*)
| AI_PASSIVE (*

将地址设置为“任何”地址,用于 UnixLabels.bind

*)

选项用于 UnixLabels.getaddrinfo.

val getaddrinfo : string ->
string -> getaddrinfo_option list -> addr_info list

getaddrinfo host service opts 返回一个 UnixLabels.addr_info 记录列表,描述了用于与给定主机和服务通信的套接字参数和地址。如果主机或服务名称未知,或者 opts 中表达的约束条件无法满足,则返回空列表。

host 是主机名或 IP 地址的字符串表示。 host 可以为空字符串;在这种情况下,将使用“任何”地址或“环回”地址,具体取决于 opts 是否包含 AI_PASSIVEservice 是服务名称或端口号的字符串表示。 service 可以为空字符串;在这种情况下,返回地址的端口字段将设置为 0。 opts 是一个可能为空的选项列表,允许调用者强制使用特定的套接字域(例如,仅 IPv6 或仅 IPv4)或特定的套接字类型(例如,仅 TCP 或仅 UDP)。

type name_info = Unix.name_info = {
   ni_hostname : string; (*

主机名称或 IP 地址

*)
   ni_service : string; (*

服务名称或端口号

*)
}

UnixLabels.getnameinfo 返回的主机和服务信息。

type getnameinfo_option = Unix.getnameinfo_option = 
| NI_NOFQDN (*

不要限定本地主机名

*)
| NI_NUMERICHOST (*

始终将主机作为 IP 地址返回

*)
| NI_NAMEREQD (*

如果无法确定主机名,则失败

*)
| NI_NUMERICSERV (*

始终将服务作为端口号返回

*)
| NI_DGRAM (*

将服务视为基于 UDP,而不是默认的 TCP

*)

选项用于 UnixLabels.getnameinfo.

val getnameinfo : sockaddr ->
getnameinfo_option list -> name_info

getnameinfo addr opts 返回与套接字地址 addr 相对应的主机名和服务名。 opts 是一个可能为空的选项列表,它控制这些名称是如何获取的。

终端接口

以下函数实现了 POSIX 标准终端接口。它们提供对异步通信端口和伪终端的控制。有关完整描述,请参阅 termios 手册页。

type terminal_io = Unix.terminal_io = {
   mutable c_ignbrk : bool; (*

忽略中断条件。

*)
   mutable c_brkint : bool; (*

在中断条件下发出中断信号。

*)
   mutable c_ignpar : bool; (*

忽略奇偶校验错误的字符。

*)
   mutable c_parmrk : bool; (*

标记奇偶校验错误。

*)
   mutable c_inpck : bool; (*

启用输入的奇偶校验检查。

*)
   mutable c_istrip : bool; (*

剥离输入字符的第 8 位。

*)
   mutable c_inlcr : bool; (*

将 NL 映射到输入的 CR。

*)
   mutable c_igncr : bool; (*

忽略输入的 CR。

*)
   mutable c_icrnl : bool; (*

将 CR 映射到输入的 NL。

*)
   mutable c_ixon : bool; (*

识别输入的 XON/XOFF 字符。

*)
   mutable c_ixoff : bool; (*

发出 XON/XOFF 字符来控制输入流。

*)
   mutable c_opost : bool; (*

启用输出处理。

*)
   mutable c_obaud : int; (*

输出波特率(0 表示关闭连接)。

*)
   mutable c_ibaud : int; (*

输入波特率。

*)
   mutable c_csize : int; (*

每个字符的位数(5-8)。

*)
   mutable c_cstopb : int; (*

停止位的数量(1-2)。

*)
   mutable c_cread : bool; (*

接收已启用。

*)
   mutable c_parenb : bool; (*

启用奇偶校验生成和检测。

*)
   mutable c_parodd : bool; (*

指定奇校验而不是偶校验。

*)
   mutable c_hupcl : bool; (*

在最后一次关闭时挂断。

*)
   mutable c_clocal : bool; (*

忽略调制解调器状态线。

*)
   mutable c_isig : bool; (*

在 INTR、QUIT、SUSP 上生成信号。

*)
   mutable c_icanon : bool; (*

启用规范处理(行缓冲和编辑)

*)
   mutable c_noflsh : bool; (*

在 INTR、QUIT、SUSP 后禁用刷新。

*)
   mutable c_echo : bool; (*

回显输入字符。

*)
   mutable c_echoe : bool; (*

回显 ERASE(擦除前一个字符)。

*)
   mutable c_echok : bool; (*

回显 KILL(擦除当前行)。

*)
   mutable c_echonl : bool; (*

即使未设置 c_echo,也回显 NL。

*)
   mutable c_vintr : char; (*

中断字符(通常为 ctrl-C)。

*)
   mutable c_vquit : char; (*

退出字符(通常为 ctrl-\)。

*)
   mutable c_verase : char; (*

擦除字符(通常为 DEL 或 ctrl-H)。

*)
   mutable c_vkill : char; (*

杀死行字符(通常为 ctrl-U)。

*)
   mutable c_veof : char; (*

文件结束符(通常为 ctrl-D)。

*)
   mutable c_veol : char; (*

备用换行符(通常为无)。

*)
   mutable c_vmin : int; (*

在读取请求满足之前要读取的最小字符数。

*)
   mutable c_vtime : int; (*

最大读取等待时间(以 0.1 秒为单位)。

*)
   mutable c_vstart : char; (*

启动字符(通常为 ctrl-Q)。

*)
   mutable c_vstop : char; (*

停止字符(通常为 ctrl-S)。

*)
}
val tcgetattr : file_descr -> terminal_io

返回给定文件描述符所引用的终端的状态。

type setattr_when = Unix.setattr_when = 
| TCSANOW
| TCSADRAIN
| TCSAFLUSH
val tcsetattr : file_descr ->
mode:setattr_when -> terminal_io -> unit

设置给定文件描述符所引用的终端的状态。第二个参数指示状态更改发生的时间:立即(TCSANOW)、当所有待发送的输出都已发送时(TCSADRAIN),或者在刷新所有已接收但尚未读取的输入后(TCSAFLUSH)。当更改输出参数时,建议使用 TCSADRAIN;当更改输入参数时,建议使用 TCSAFLUSH

val tcsendbreak : file_descr -> duration:int -> unit

在给定文件描述符上发送中断条件。第二个参数是中断的持续时间,以 0.1 秒为单位;0 表示标准持续时间(0.25 秒)。

val tcdrain : file_descr -> unit

等待直到所有写入给定文件描述符的输出都已发送。

type flush_queue = Unix.flush_queue = 
| TCIFLUSH
| TCOFLUSH
| TCIOFLUSH
val tcflush : file_descr -> mode:flush_queue -> unit

丢弃写入给定文件描述符但尚未发送的数据,或者接收但尚未读取的数据,具体取决于第二个参数:TCIFLUSH 刷新已接收但尚未读取的数据,TCOFLUSH 刷新已写入但尚未发送的数据,TCIOFLUSH 同时刷新两者。

type flow_action = Unix.flow_action = 
| TCOOFF
| TCOON
| TCIOFF
| TCION
val tcflow : file_descr -> mode:flow_action -> unit

暂停或重新开始对给定文件描述符的数据接收或传输,具体取决于第二个参数:TCOOFF 暂停输出,TCOON 重新开始输出,TCIOFF 发送一个 STOP 字符来暂停输入,TCION 发送一个 START 字符来重新开始输入。

val setsid : unit -> int

将调用进程置于一个新的会话中,并将其从其控制终端分离。