Zircon 内核概念

简介

内核管理许多不同类型的对象。那些 可通过系统调用直接访问的 C++ 类, 调度程序接口。这些是在 kernel/object。很多都是独立的更高级别对象。 部分封装较低级别 lk 基元。

系统调用

用户空间代码通过系统调用与内核对象交互, 只通过标识名来实现。在用户空间中,句柄表示为 32 位整数(类型 zx_handle_t)。执行系统调用时,内核会检查 句柄参数引用的是调用 进程的句柄表内核会进一步检查句柄是否属于 类型正确(将线程句柄传递给需要事件句柄的系统调用) 将导致错误),且标识名拥有 请求的操作。

从访问的角度来看,系统调用分为三大类:

  1. 没有限制的调用,其中只有很少的 示例 zx_clock_get_monotonic()zx_nanosleep() 可以由任何线程调用。
  2. 将句柄作为第一个参数(表示对哪个对象执行操作)的调用; 占绝大多数,例如 zx_channel_write()zx_port_queue()
  3. 创建新对象但不获取句柄的调用,例如 zx_event_create()zx_channel_create().访问这些内容(和限制) 由包含调用进程的 Job 控制。

系统调用由 libzircon.so 提供,libzircon.so 是共享 Zircon 内核提供给用户空间的一个库,也就是 虚拟动态共享对象,即 vDSO。 它们是 C ELF ABI 函数,格式为 zx_noun_verb()zx_noun_verb_direct-object().

系统调用在 //zircon/vdso 中以自定义形式的 FIDL 进行定义。 这些定义首先由 fidlc 处理,然后由 zither 进行处理,后者接受 IR 表示,并输出各种格式,这些格式在 VDSO、内核、fidlc 等等

标识名权限

对象可以有多个引用它们的句柄(在一个或多个进程中)。

对于几乎所有对象,当引用对象的最后一个打开的句柄关闭时, 对象会被销毁,或置于不可撤消的最终状态。

可以通过将句柄写入通道来将句柄从一个进程移至另一个进程 (使用 zx_channel_write()),或使用 zx_process_start(),用于将句柄作为实参传递 新进程中第一个线程的状态

可以对标识名或其引用的对象执行的操作受 与标识名相关的权利。引用同一对象的两个句柄 可能拥有不同的权利。

zx_handle_duplicate()zx_handle_replace() 系统调用可用于 获得引用与传入的句柄相同的对象的其他句柄, (可选)。zx_handle_close() 系统调用会关闭句柄,从而释放其引用的对象(如果该句柄是 该对象的最后一步了zx_handle_close_many() 系统调用同样会关闭句柄数组。

内核对象 ID

内核中的每个对象都有一个“内核对象 ID”或“koid”。 这是一个 64 位无符号整数,可用于识别对象 并且在运行系统的生命周期内是唯一的。 具体来说,这是指 koid 绝不会被重复使用。

有两个特殊的 Koid 值:

ZX_KOID_INVALID,其值为零,并用作“null”Sentinel。

ZX_KOID_KERNEL,只有一个内核,并且它有自己的 koid。

内核生成的 Koid 仅使用 63 位(足够了)。 这可以为人工分配的 Kids 留出最多空间 有效位设置。内核生成的 koid 的分配顺序 未指定,且可能会发生变化。

人造桌子的存在是为了支持识别人造物体、 就像跟踪中的虚拟线程一样,供工具使用。 如何分配人工 Kids 则由各个程序决定, 本文档并未施加任何规则或惯例。

运行代码:作业、进程和线程。

线程表示 它们所在的进程拥有的地址空间进程 它定义了各种资源限制。招聘信息的所有者是: 父级作业一直到根作业,根作业由内核在 启动并传递给 userboot,即开始执行的第一个用户空间进程

如果没有作业句柄,进程内的线程就无法创建另一个 进程或其他作业。

程序加载由用户空间设施和 高于内核层的协议。

请参阅:zx_process_create()zx_process_start()zx_thread_create(), 和 zx_thread_start()

消息传递:套接字和通道

套接字和通道都是双向和双端 IPC 对象。 创建套接字或通道将返回两个句柄,一个句柄分别引用一个端点 对象。

套接字是面向信息流的,并且数据可以按单元写入或读取出 一个或多个字节短时写入(如果套接字缓冲区已满)和短时读取 (如果请求的数据多于缓冲区中的数据)。

通道是面向数据报的,且消息大小上限由 ZX_CHANNEL_MAX_MSG_BYTES 指定, 并且最多可以为一条消息附加 ZX_CHANNEL_MAX_MSG_HANDLES 个标识名。 它们不支持短时间读写 - 无论消息是否合适。

将标识名写入通道后,它们会从发送进程中移除。 从某个通道读取带有标识名的消息时,会将标识名添加到接收 流程。在这两个事件之间,句柄继续存在(确保对象 它们所指的可继续存在) 关闭 - 此时将丢弃向该端点传输的消息,并 它们包含的任何标识名都会被关闭。

请参阅:zx_channel_create()zx_channel_read()zx_channel_write()zx_channel_call()zx_socket_create()zx_socket_read()、 和 zx_socket_write()

对象和信号

对象最多可包含 32 个信号(分别以 zx_signalst 类型和 ZX SIGNAL 表示) 定义),用于表示有关其当前状态的一段信息。通道和套接字 例如,可能为 READABLE 或 WRITABLE进程或线程可能会被终止。依此类推。

线程可以等待一个或多个对象上的信号变为活跃状态。

如需了解详情,请参阅信号

等待:等待一次、等待多次和携号转网

Thread 可以使用 zx_object_wait_one() 等待单个句柄上的信号有效 等待 zx_object_wait_many() 多个句柄上的信号两个调用均允许在 即使没有待处理的信号,也会返回这些信号。

根据计时器,超时可能会偏离指定的截止时间 Slack。如需了解详情,请参阅计时器延迟

如果一个线程会等待大量句柄,那么使用 端口,该端口是一个对象,其他对象可能会绑定到该端口,以便在发送信号时 则端口会收到一个数据包,其中包含 待处理信号。

请参阅:zx_port_create()zx_port_queue()zx_port_wait(), 和 zx_port_cancel()

事件、事件对。

事件是最简单的对象,除了其有效信号的集合外,没有其他任何状态。

事件对是指可以相互发出信号的一对事件中的一个。一个有用的属性 事件对是指,事件对的一侧消失时(该对的所有句柄已被 关闭),另一端会断言 PEER_CLOSED 信号。

请参阅:zx_event_create(), 和 zx_eventpair_create()

共享内存:虚拟内存对象 (VMO)

虚拟内存对象代表一组物理内存页面, (将延迟创建/填充,按需使用)。

它们可能会映射到 zx_vmar_map(),未映射 zx_vmar_unmap().以下用户的权限: 映射的网页可以使用 zx_vmar_protect() 进行调整。

也可以直接通过 zx_vmo_read()zx_vmo_write()。 因此对于一次性操作,可以避免将它们映射到地址空间的成本。 例如“创建一个 VMO,向其中写入一个数据集,然后交由其他进程使用”。

地址空间管理

虚拟内存地址区域 (VMAR) 提供了用于管理 进程的地址空间。在创建进程时,根 VMAR 的句柄 指定给进程创建者该标识名是指涵盖 整个地址空间这一空间可通过 zx_vmar_map()zx_vmar_allocate() 接口。 zx_vmar_allocate() 可用于生成新的 VMAR(称为子区域或子区域),可用于分组 部分地址空间。

请参阅:zx_vmar_map()zx_vmar_allocate()zx_vmar_protect()zx_vmar_unmap()、 和 zx_vmar_destroy()

Futexes

Futexe 是与用户空间原子操作结合使用的内核基元,用于实现 有效的同步基元,例如互斥量, 争用案例中的系统调用。通常情况下,只有 标准库。Zircon 的 libc 和 libc++ 提供 C11、C++ 和 pthread API, 互斥量、条件变量等,根据 Futexe 实现。

请参阅:zx_futex_wait()zx_futex_wake()、 和zx_futex_requeue()