查看源代码 系统 (Elixir v1.16.2)
The System
模块提供与 VM 或主机系统直接交互的函数。
时间
The System
模块还提供与时间相关的函数,返回系统保存的不同时间,并支持不同的时间单位。
依赖系统时间的一个复杂之处在于它们可能会被调整。例如,当您进入和离开夏令时时,系统时钟将被调整,通常会增加或减少一个小时。我们称这种变化为“时间扭曲”。为了理解这种变化可能造成危害的方式,请想象以下代码
## DO NOT DO THIS
prev = System.os_time()
# ... execute some code ...
next = System.os_time()
diff = next - prev
如果在代码执行期间,系统时钟发生变化,一些在 1 秒内执行的代码可能会被报告为耗时超过 1 小时!为了解决这些问题,VM 通过 System.monotonic_time/0
提供了单调时间,该时间永远不会减少并且不会跳跃
## DO THIS
prev = System.monotonic_time()
# ... execute some code ...
next = System.monotonic_time()
diff = next - prev
一般来说,VM 提供三种时间测量方法
os_time/0
- 操作系统 (OS) 报告的时间。此时间可能会不受限制地向前或向后调整;system_time/0
- VM 对os_time/0
的看法。在发生时间扭曲的情况下,系统时间和操作系统时间可能不匹配,尽管 VM 会努力使它们保持一致。此时间不是单调的(即,它可能减少),因为它的行为配置 由 VM 时间扭曲模式配置;monotonic_time/0
- 由 Erlang VM 提供的单调递增时间。这不是严格的单调递增。函数的多次连续调用可能会返回相同的值。
此模块中的时间函数以 :native
单位工作(除非另有说明),该单位取决于操作系统。大多数情况下,所有计算都在 :native
单位内完成,以避免精度损失,最后调用 convert_time_unit/3
转换为特定的时间单位,例如 :millisecond
或 :microsecond
。有关更多信息,请参见 time_unit/0
类型。
有关 VM 对不同时间的支持的更完整概述,请参阅 Erlang 文档中的 关于时间和时间校正的章节。
总结
函数
列出命令行参数。
修改命令行参数。
注册程序退出处理函数。
Elixir 构建信息。
使用 args
执行给定的 command
。
返回系统编译时的字节序。
将 time
从时间单位 from_unit
转换为时间单位 to_unit
。
当前工作目录。
当前工作目录,错误时抛出异常。
删除环境变量。
返回字节序。
返回给定环境变量的值,如果未找到则返回 :error
。
返回给定环境变量的值,如果未找到则抛出异常。
在系统上查找可执行文件。
返回所有系统环境变量。
返回给定环境变量的值。
Erlang VM 进程标识符。
立即停止 Erlang 运行时系统。
以 :native
时间单位返回当前单调时间。
以给定时间单位返回当前单调时间。
检查系统是否将在 ARGV 处理结束时停止。
标记系统是否应该在 ARGV 处理结束时停止。
返回当前操作系统 (OS) 时间。
以给定时间 unit
返回当前操作系统 (OS) 时间。
返回 Erlang/OTP 版本号。
返回当前 Erlang 运行时系统实例的操作系统 PID。
设置多个环境变量。
设置环境变量值。
重启 Erlang 运行时系统中的所有应用程序。
返回 VM 中的调度程序数量。
返回 VM 中在线的调度程序数量。
在 OS shell 中执行给定的 command
。
已弃用的获取最后一个异常堆栈跟踪的机制。
异步且谨慎地停止 Erlang 运行时系统。
以 :native
时间单位返回当前系统时间。
以给定时间单位返回当前系统时间。
返回 Erlang VM 单调时间和 Erlang VM 系统时间之间的当前时间偏移量。
返回 Erlang VM 单调时间和 Erlang VM 系统时间之间的当前时间偏移量。
可写入的临时目录。
可写入的临时目录,错误时抛出异常。
捕获给定的 signal
以执行 fun
。
生成并返回在当前运行时实例中唯一的整数。
删除先前注册的 signal
和 id
。
用户主目录。
用户主目录,错误时抛出异常。
Elixir 版本信息。
等待系统启动。
类型
@type signal() ::
:sigabrt
| :sigalrm
| :sigchld
| :sighup
| :sigquit
| :sigstop
| :sigterm
| :sigtstp
| :sigusr1
| :sigusr2
@type time_unit() :: :second | :millisecond | :microsecond | :nanosecond | pos_integer()
要传递给诸如 monotonic_time/1
等函数的时间单位。
The :second
, :millisecond
, :microsecond
和 :nanosecond
时间单位控制接受时间单位的函数的返回值。
时间单位也可以是严格的正整数。在这种情况下,它表示“每秒的份数”:时间将以 1 / parts_per_second
秒返回。例如,使用 :millisecond
时间单位等效于使用 1000
作为时间单位(因为时间将以 1/1000 秒 - 毫秒返回)。
函数
@spec argv() :: [String.t()]
列出命令行参数。
返回传递给程序的命令行参数列表。
@spec argv([String.t()]) :: :ok
修改命令行参数。
更改命令行参数列表。谨慎使用,因为它会破坏任何先前的 argv 信息。
@spec at_exit((non_neg_integer() -> any())) :: :ok
注册程序退出处理函数。
注册一个函数,该函数将在 Elixir 脚本结束时调用。脚本通常通过命令行通过 elixir
和 mix
可执行文件启动。
处理程序始终在与注册它的进程不同的进程中执行。因此,调用进程管理的任何资源(ETS 表、打开的文件等)在处理程序函数被调用时将不可用。
函数必须接收退出状态代码作为参数。
如果 VM 通过 System.stop/1
、System.halt/1
或退出信号以编程方式终止,则无法保证 at_exit/1
回调将被执行。
@spec build_info() :: %{ build: String.t(), date: String.t(), revision: String.t(), version: String.t(), otp_release: String.t() }
Elixir 构建信息。
返回一个包含 Elixir 版本、它编译的 Erlang/OTP 版本、简短的 Git 修订版哈希值以及它构建的日期和时间的映射。
映射中的每个值都是一个字符串,并且这些值是
:build
- Elixir 版本、简短的 Git 修订版哈希值以及它编译的 Erlang/OTP 版本:date
- 它构建的 ISO8601 日期和时间的字符串表示形式:otp_release
- 它编译的 OTP 版本:revision
- 简短的 Git 修订版哈希值。如果构建时 Git 不可用,则将其设置为""
:version
- Elixir 版本
不应该依赖每个字段返回的特定格式。相反,应该使用专门的函数,例如 version/0
来检索 Elixir 版本,以及 otp_release/0
来检索 Erlang/OTP 版本。
示例
iex> System.build_info()
%{
build: "1.9.0-dev (772a00a0c) (compiled with Erlang/OTP 21)",
date: "2018-12-24T01:09:21Z",
otp_release: "21",
revision: "772a00a0c",
version: "1.9.0-dev"
}
@spec cmd(binary(), [binary()], keyword()) :: {Collectable.t(), exit_status :: non_neg_integer()}
使用 args
执行给定的 command
。
command
预计是 PATH 中可用的可执行文件,除非给出绝对路径。
args
必须是二进制文件的列表,可执行文件将按原样接收这些二进制文件作为其参数。这意味着
- 环境变量不会被插值
- 通配符扩展不会发生(除非显式使用
Path.wildcard/2
) - 参数不需要转义或引用以确保 shell 安全
此函数返回一个元组,其中包含收集的结果和命令退出状态。
在内部,此函数使用 Port
与外部世界交互。但是,如果您计划运行一个长时间运行的程序,端口保证 stdin/stdout 设备将被关闭,但它不会自动终止程序。Port
模块的文档在“僵尸进程”部分描述了此问题以及可能的解决方案。
示例
iex> System.cmd("echo", ["hello"])
{"hello\n", 0}
iex> System.cmd("echo", ["hello"], env: [{"MIX_ENV", "test"}])
{"hello\n", 0}
如果您希望在输出到达时将其流式传输到标准 IO
iex> System.cmd("echo", ["hello"], into: IO.stream())
hello
{%IO.Stream{}, 0}
如果您希望读取行
iex> System.cmd("echo", ["hello\nworld"], into: [], lines: 1024)
{["hello", "world"], 0}
选项
:into
- 将结果注入到给定的可收集对象中,默认值为""
:lines
- (自 v1.15.0 起)按行读取输出,而不是按字节读取。它需要一个内部缓冲的最大字节数(1024 是一个合理的默认值)。可收集对象将在每完成一行时(无论缓冲区大小)被调用,并且没有 EOL 字符:cd
- 在其中运行命令的目录:env
- 一个包含环境键值对(二进制形式)的可枚举对象。子进程从其父进程(Elixir 应用程序)继承所有环境变量,除了使用此选项覆盖或清除的环境变量。指定nil
值以清除(取消设置)环境变量,这对于防止传递给应用程序的凭据泄漏到子进程中很有用:arg0
- 设置命令 arg0:stderr_to_stdout
- 当true
时将 stderr 重定向到 stdout:parallelism
- 当true
时,VM 将调度端口任务以提高系统中的并行性。如果设置为false
,VM 将尝试立即执行命令,以牺牲并行性为代价提高延迟。默认值为false
,可以在系统启动时通过将+spp
标志传递给--erl
来设置。使用:erlang.system_info(:port_parallelism)
检查是否已启用。
错误原因
如果给出无效的参数,则 ArgumentError
由 System.cmd/3
抛出。 System.cmd/3
还需要一组严格的选项,如果给出未知或无效的选项,它将抛出错误。
此外,System.cmd/3
可能会因以下列出的 POSIX 原因之一而失败
:system_limit
- Erlang 模拟器中的所有可用端口都在使用中:enomem
- 没有足够的内存来创建端口:eagain
- 没有更多可用的操作系统进程:enametoolong
- 给出的外部命令过长:emfile
- 没有更多可用的文件描述符(用于 Erlang 模拟器运行的操作系统进程):enfile
- 文件表已满(对于整个操作系统):eacces
- 命令不指向可执行文件:enoent
- 命令不指向现有文件
Shell 命令
如果你希望在一个 shell 中执行一个受信任的命令,使用管道、重定向等等,请查看 shell/2
。
@spec compiled_endianness() :: :little | :big
返回系统编译时的字节序。
将 time
从时间单位 from_unit
转换为时间单位 to_unit
。
结果通过地板函数进行舍入。
convert_time_unit/3
接受一个额外的时单位(除了 time_unit/0
类型中的时单位)称为 :native
。 :native
是 Erlang 运行时系统使用的时单位。它在运行时启动时确定,并在运行时停止之前保持不变,但在相同机器上下次启动运行时时可能会不同。出于这个原因,你应该使用此函数将 :native
时单位转换为可预测的单位,然后再将其显示给人类。
要确定当前运行时 :native
单位表示多少秒,你可以调用此函数将 1 秒转换为 :native
时单位: System.convert_time_unit(1, :second, :native)
。
@spec cwd() :: String.t() | nil
当前工作目录。
返回当前工作目录,如果不可用则返回 nil
。
@spec cwd!() :: String.t()
当前工作目录,错误时抛出异常。
返回当前工作目录,或者抛出 RuntimeError
。
@spec delete_env(String.t()) :: :ok
删除环境变量。
从环境中删除变量 varname
。
@spec endianness() :: :little | :big
返回字节序。
返回给定环境变量的值,如果未找到则返回 :error
。
如果环境变量 varname
已设置,则返回 {:ok, value}
,其中 value
是一个字符串。如果 varname
未设置,则返回 :error
。
示例
iex> System.fetch_env("PORT")
{:ok, "4000"}
iex> System.fetch_env("NOT_SET")
:error
返回给定环境变量的值,如果未找到则抛出异常。
与 get_env/1
相同,但在变量未设置时抛出异常,而不是返回 nil
。
示例
iex> System.fetch_env!("PORT")
"4000"
iex> System.fetch_env!("NOT_SET")
** (System.EnvError) could not fetch environment variable "NOT_SET" because it is not set
在系统上查找可执行文件。
此函数使用 Windows 和类 Unix 操作系统上的环境变量 PATH 查找给定其名称的可执行程序。它还考虑每个操作系统的正确可执行文件扩展名,因此对于 Windows,它将尝试查找带有 .com
、.cmd
或类似扩展名的文件。
返回所有系统环境变量。
返回值是一个包含名称-值对的映射。变量名称及其值是字符串。
@spec get_env(String.t(), String.t()) :: String.t()
@spec get_env(String.t(), nil) :: String.t() | nil
返回给定环境变量的值。
环境变量 varname
的返回值是一个字符串。如果环境变量未设置,则返回 default
中指定的字符串,如果未指定则返回 nil
。
示例
iex> System.get_env("PORT")
"4000"
iex> System.get_env("NOT_SET")
nil
iex> System.get_env("NOT_SET", "4001")
"4001"
@spec get_pid() :: binary()
Erlang VM 进程标识符。
返回当前 Erlang 模拟器的进程标识符,格式通常用于操作系统环境。
有关更多信息,请参见 :os.getpid/0
。
@spec halt(non_neg_integer() | binary() | :abort) :: no_return()
立即停止 Erlang 运行时系统。
终止 Erlang 运行时系统,而不正确地关闭应用程序和端口。请参见 stop/1
,了解系统的谨慎关闭方法。
status
必须是一个非负整数、原子 :abort
或一个二进制。
如果是一个整数,则运行时系统以整数返回值退出,该返回值返回给操作系统。
如果为
:abort
,则运行时系统会中止,如果操作系统中启用了此功能,则会产生核心转储。如果是一个字符串,则会生成一个 Erlang 崩溃转储,其中 status 作为标语,然后运行时系统以状态码 1 退出。
请注意,在许多平台上,操作系统只支持状态码 0-255。
有关更多信息,请参见 :erlang.halt/1
。
示例
System.halt(0)
System.halt(1)
System.halt(:abort)
@spec monotonic_time() :: integer()
以 :native
时间单位返回当前单调时间。
此时间是单调递增的,并在时间上的某个未指定点开始。这不是严格的单调递增。函数的多次连续调用可能会返回相同的值。
由编译器内联。
以给定时间单位返回当前单调时间。
此时间是单调递增的,并在时间上的某个未指定点开始。
@spec no_halt() :: boolean()
检查系统是否将在 ARGV 处理结束时停止。
@spec no_halt(boolean()) :: :ok
标记系统是否应该在 ARGV 处理结束时停止。
@spec os_time() :: integer()
返回当前操作系统 (OS) 时间。
结果以 :native
时单位返回。
此时间可能会向前或向后调整时间,没有限制,并且不是单调的。
由编译器内联。
以给定时间 unit
返回当前操作系统 (OS) 时间。
此时间可能会向前或向后调整时间,没有限制,并且不是单调的。
@spec otp_release() :: String.t()
返回 Erlang/OTP 版本号。
@spec pid() :: String.t()
返回当前 Erlang 运行时系统实例的操作系统 PID。
返回一个包含进程的(通常是)数值标识符的字符串。在类 Unix 操作系统上,这通常是 getpid()
系统调用的返回值。在 Windows 上,使用 GetCurrentProcessId()
系统调用返回的进程 ID。
示例
System.pid()
@spec put_env(Enumerable.t()) :: :ok
设置多个环境变量。
为每个环境变量设置一个新值,这些环境变量对应于 enum
中的每个 {key, value}
对。键和非空值会自动转换为字符列表。 nil
值会擦除给定的键。
总的来说,这是 put_env/2
和 delete_env/2
的一个便利包装器,支持不同的键和值格式。
设置环境变量值。
为环境变量 varname
设置一个新的 value
。
@spec restart() :: :ok
重启 Erlang 运行时系统中的所有应用程序。
所有应用程序都会顺利关闭,所有代码都会卸载,所有端口都会关闭,然后系统会再次启动所有应用程序。
示例
System.restart()
@spec schedulers() :: pos_integer()
返回 VM 中的调度程序数量。
@spec schedulers_online() :: pos_integer()
返回 VM 中在线的调度程序数量。
@spec shell( binary(), keyword() ) :: {Collectable.t(), exit_status :: non_neg_integer()}
在 OS shell 中执行给定的 command
。
它对类 Unix 系统使用 sh
,对 Windows 系统使用 cmd
。
注意
谨慎使用此函数。特别地,**切勿将不受信任的用户输入传递给此函数**,因为用户将能够通过直接在机器上执行任何代码来执行“命令注入攻击”。一般来说,建议使用
cmd/3
而不是此函数。
示例
iex> System.shell("echo hello")
{"hello\n", 0}
如果您希望在输出到达时将其流式传输到标准 IO
iex> System.shell("echo hello", into: IO.stream())
hello
{%IO.Stream{}, 0}
选项
它接受与 cmd/3
相同的选项(arg0
除外)。它还接受以下互斥选项
:close_stdin
(从 v1.14.1 开始)- 如果在 Unix 系统上应该关闭标准输入,则强制任何等待标准输入的命令立即终止。默认值为 false。
已弃用的获取最后一个异常堆栈跟踪的机制。
它始终返回一个空列表。
@spec stop(non_neg_integer() | binary()) :: :ok
异步且谨慎地停止 Erlang 运行时系统。
在通过调用 halt/1
终止系统之前,所有应用程序都将顺利关闭,所有代码都将卸载,所有端口都将关闭。
status
必须是非负整数或二进制。
如果是整数,则运行时系统将退出,并返回整数作为退出码,该退出码将返回给操作系统。在许多平台上,操作系统仅支持 0-255 的退出码。
如果是二进制,则会生成一个 Erlang 崩溃转储,其状态作为口号,然后运行时系统退出,退出码为 1。
请注意,此函数是异步的,在调用此函数后,当前进程将继续执行。如果您希望阻塞当前进程,直到系统有效关闭,则可以调用 Process.sleep(:infinity)
。
示例
System.stop(0)
System.stop(1)
@spec system_time() :: integer()
以 :native
时间单位返回当前系统时间。
它是 VM 对 os_time/0
的视图。在发生时间扭曲的情况下,它们可能不匹配,尽管 VM 会努力使它们保持一致。此时间不是单调的。
由编译器内联。
以给定时间单位返回当前系统时间。
它是 VM 对 os_time/0
的视图。在发生时间扭曲的情况下,它们可能不匹配,尽管 VM 会努力使它们保持一致。此时间不是单调的。
@spec time_offset() :: integer()
返回 Erlang VM 单调时间和 Erlang VM 系统时间之间的当前时间偏移量。
结果以 :native
时单位返回。
有关更多信息,请参见 time_offset/1
。
由编译器内联。
返回 Erlang VM 单调时间和 Erlang VM 系统时间之间的当前时间偏移量。
结果将以给定的时间单位 unit
返回。返回的偏移量加上 Erlang 单调时间(例如,使用 monotonic_time/1
获得的时间),将得到与该单调时间相对应的 Erlang 系统时间。
@spec tmp_dir() :: String.t() | nil
可写入的临时目录。
返回一个可写的临时目录。按以下顺序搜索目录
- 由 TMPDIR 环境变量命名的目录
- 由 TEMP 环境变量命名的目录
- 由 TMP 环境变量命名的目录
- Windows 上的
C:\TMP
或类 Unix 操作系统上的/tmp
- 最后,当前工作目录
如果以上都不可写,则返回 nil
。
@spec tmp_dir!() :: String.t()
可写入的临时目录,错误时抛出异常。
与 tmp_dir/0
相同,但如果未设置临时目录,则会引发 RuntimeError
,而不是返回 nil
。
@spec trap_signal(signal(), id, (-> :ok)) :: {:ok, id} | {:error, :already_registered} | {:error, :not_sup} when id: term()
捕获给定的 signal
以执行 fun
。
避免在库中设置陷阱
捕获信号可能会对系统在生产环境中的关闭和行为方式产生重大影响,因此强烈建议库不要设置自己的陷阱。相反,它们应该将用户重定向到自行配置。库设置自己陷阱的唯一情况是在脚本模式下使用 Elixir 时,例如在
.exs
文件中以及通过 Mix 任务。
可以提供一个可选的 id
,它唯一标识函数,否则会自动生成一个唯一的 id。如果提供了以前注册的 id
,则此函数将返回一个错误元组。可以使用 id
通过调用 untrap_signal/2
来删除注册的信号。
给定的 fun
不接收任何参数,并且它必须返回 :ok
。
如果成功,它将返回 {:ok, id}
,如果该 id 已经为给定的信号注册,则返回 {:error, :already_registered}
,如果当前操作系统不支持捕获存在的信号,则返回 {:error, :not_sup}
。
首次捕获信号时,它将覆盖操作系统的默认行为。如果同一信号被捕获多次,则随后传递给 trap_signal
的函数将首先执行。换句话说,您可以认为每个函数都被预先添加到信号处理程序中。
默认情况下,Erlang VM 将陷阱注册到三个信号
因此,如果您向上述信号添加陷阱,则上述默认行为将在所有用户信号之后执行。
实现说明
所有信号都从单个进程运行。因此,阻塞 fun
将阻塞后续陷阱。也不可能在陷阱本身内添加或删除陷阱。
在内部,此功能建立在 :os.set_signal/2
之上。当您注册一个陷阱时,Elixir 会自动将其设置为 :handle
,并在删除所有陷阱后将其恢复为 :default
(除了 :sigquit
、:sigterm
和 :sigusr1
,它们始终被处理)。如果您或库直接调用 :os.set_signal/2
,则它可能会禁用 Elixir 陷阱(或 Elixir 可能会覆盖您的配置)。
@spec unique_integer([:positive | :monotonic]) :: integer()
生成并返回在当前运行时实例中唯一的整数。
“唯一”表示此函数在使用相同的 modifiers
列表调用时,在当前运行时实例上永远不会返回相同的整数超过一次。
如果 modifiers
为 []
,则返回一个唯一的整数(可以是正数或负数)。可以传递其他修饰符来更改返回的整数的属性
:positive
- 保证返回的整数为正数。:monotonic
- 返回的整数单调递增。这意味着,在同一个运行时实例上(甚至在不同的进程上),使用:monotonic
修饰符返回的整数始终严格小于使用:monotonic
修饰符的后续调用返回的整数。
上面列出的所有修饰符都可以组合使用;modifiers
中重复的修饰符将被忽略。
由编译器内联。
删除先前注册的 signal
和 id
。
@spec user_home() :: String.t() | nil
用户主目录。
返回用户主目录(平台无关)。
@spec user_home!() :: String.t()
用户主目录,错误时抛出异常。
与 user_home/0
相同,但如果未设置用户主目录,则会引发 RuntimeError
,而不是返回 nil
。
@spec version() :: String.t()
Elixir 版本信息。
以二进制形式返回 Elixir 的版本。
@spec wait_until_booted() :: :ok
等待系统启动。
调用此函数将阻塞,直到处理完所有 ARGV。在发布版中,这意味着启动脚本和 ARGV 已被处理。这仅对那些在 Elixir 之上实现自定义 shell/控制台的人有用。
但是,小心不要从处理命令行参数的进程中调用此命令,因为这样做会导致死锁。