简介
内核管理许多不同类型的对象。那些 可通过系统调用直接访问的 C++ 类, 调度程序接口。这些是在 kernel/object。很多都是独立的更高级别对象。 部分封装较低级别 lk 基元。
系统调用
用户空间代码通过系统调用与内核对象交互, 只通过标识名来实现。在用户空间中,句柄表示为 32 位整数(类型 zx_handle_t)。执行系统调用时,内核会检查 句柄参数引用的是调用 进程的句柄表内核会进一步检查句柄是否属于 类型正确(将线程句柄传递给需要事件句柄的系统调用) 将导致错误),且标识名拥有 请求的操作。
从访问的角度来看,系统调用分为三大类:
- 没有限制的调用,其中只有很少的
示例
zx_clock_get_monotonic()
和zx_nanosleep()
可以由任何线程调用。 - 将句柄作为第一个参数(表示对哪个对象执行操作)的调用;
占绝大多数,例如
zx_channel_write()
和zx_port_queue()
。 - 创建新对象但不获取句柄的调用,例如
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()
。