Fuchsia系列博客(三) Zircon内核核心概念
Fuchsia系列博客(三) Zircon内核核心概念
- 翻译自(不是严格翻译):
https://fuchsia.googlesource.com/fuchsia/+/master/zircon/docs/concepts.md
介绍
Zircon内核负责管理大量不同类型的对象.
那些实现了Dispather
介面的对象可以通过系统调用直接访问, 这些对象在kernal/object
目录下, 包括许多自包含的高级对象, 和一些对lk原语
的封装.
系统调用(System Call)
用户空间的代码通过系统调用与内核对象交互, 甚至可以说, 绝大多数是通过Handles(句柄)
.(译者按: 下文中, Handle一词一律不翻译.)Handle实际上是一个32bit 的整数(zx_handle_t
类型).
在系统调用被实际执行前, 内核将进行三项检查.
- 检查Handle参数是否指向调用process进程的
Handle表
中存在的实际句柄. - 检查Handle的类型是否正确. (例如:将
Thread Handle
传给需要Event Handle
的系统调用会导致错误.) - 检查Handle是否具有所请求的操作许可权.
从访问限制的角度看, 系统调用分为三大类:
- 无限制系统调用. 可以被任何thread(线程)调用, 极少数的系统调用属于这一类, 例如:
zx_clock_get()
和zx_nanosleep()
. - 消费一个Handle的系统调用. Handle作为第一个参数, 代表系统调用作用的对象. 绝大多数的系统调用属于该类, 例如:
zx_channel_write()
和zx_port_queue()
. - 无需Handle并创造新对象的系统调用. 他们的调用以及访问限制由包含该process(进程)的Job(作业)控制.例如:
zx_event_create()
和zx_channel_create()
.
系统调用由libzircon.so
提供, libzircon.so
是一个由内核提供的"虚拟"共享库, 也被称之为virtual Dynamic Shared Object
或者vDSO
. 系统调用都是C ELF ABI
形式的函数: zx_名词_动词
或者zx_名词_动词_直接对象
.
系统调用由syscall.abigen
定义, 并且由abigen tool
处理头文件和libzircon
中与内核libsyscalls
连接的胶水代码.
Handles(句柄) 和 Rights(许可权)
- 可以有很多分布在一个或多个进程(process)中的Handle指向同一个对象.
- 对于绝大多数对象来说, 最后一个指向他的Handle被关闭, 该对象也随之消亡, 抑或进入一种无法撤销的最终状态.
- 有两种方式把句柄在进程之间转移, 第一种是调用
zx_channel_write()
把Handle写入Channel(通道), 另一种方法是调用zx_process_start()
, 把这个Handle传递给新进程的第一个线程. - Rights负责管理相关Handle和Object可能进行的任何行为, 指向同一对象的不同Handle完全可能用有不同的许可权.
zx_handle_duplicate()
和zx_handle_replace()
系统调用可以产生指向该对象的新的Handle, 并且可以选择性减少新Handle的许可权.zx_handle_close()
负责关闭Handle, 如上所述, 该方法也将销毁对象, 如果关闭Handle后再没有Handle持有该对象.zx_handle_close_many()
与其相似, 只是关闭一个Handle数组中的所有Handle.
内核对象ID (Kernel Object IDs)
每个对象都有个内核对象ID
, 简称koid
. 这是一个64位整数, 是对象的唯一标识符, 并且在系统运行的周期内是唯一的. 这意味著"koid"永远不会被重用.(译者按: 真的不会被用完吗? 0.0)
ZX_KOID_INVALID
: 值是0
, 用来做null
. ZX_KOID_KERNEL
: 内核自己的唯一koid. 内核只用63个位(真的是很多了)生成koid. 用户可以通过设置最高有效位, 分配剩余的koid. 内核分配koid的顺序没有被指定, 并且可能发生变化. 人工分配的koid提供了更多可能:例如辨别人工对象, 跟踪虚拟线程, 这些都可以被各种工具使用. 而对于每个程序如何分配人工koid, 本文档不做任何限制和规则约束.
运行中的代码: 作业(Jobs), 进程(Processes)和线程(Threads)
线程代表地址空间中实际执行的线程(CPU 寄存器, 栈等), 线程的地址空间被他们所在的进程拥有.
进程又由作业拥有, 作业定义了各种各样的资源限制. 作业又被它的父作业拥有, 层层传递, 直到根作业(Root Job). 根作业在系统启动时由内核创建, 进而传递给第一个用户空间进程--userboot. 没有作业的Handle, 进程中的线程不可能创建另一个进程或者另一个作业.程序如何载入
由内核空间上层的用户空间设施和协议规定. 请参阅:zx_process_create()
, zx_process_start()
, zx_thread_create()
和 zx_thread_start()
.
消息传递: 套接字(Sockets)和通道(Channels)
套接字和通道都是用于双向通信的IPC对象, 创建套接字或者通道将返回两个对象, 一边一个.
套接字是面向流的编程模型, 允许短读和短写, 数据以一个或多个位元组作为单元传输. 通道是面相数据报的编程模型, 拥有最大长度限制ZX_CHANNEL_MAX_MSG_BYTES
, 还拥有消息句柄数量限制, 即ZX_CHANNEL_MAX_MSG_HANDLES
. 不支持短写和短读 -- 严格判定每个消息是否合适. 当Handle被写入通道时, 他们从发送端进程移除. 当Handle从通道读取时, 他们被加入到接收端进程. 在传输过程中, Handle并未消失, 这意味著它指向的对象仍旧存在. 然而, 如果通道的终点被关闭, 传输中的消息将会被丢弃, 所有指向该消息的句柄都会被关闭.
请参阅: zx_channel_create()
, zx_channel_read()
, zx_channel_write()
, zx_channel_call()
, zx_socket_create()
, zx_socket_read()
和 zx_socket_write()
.
对象(Objects)和信号(Signals)
对象有至多32种信号(zx_signals_t
类型, 由ZX_SIGNAL
定义), 这些信号代表对象当前状态的信息. 例如: 通道(Channel)和套接字(Socket), 可能是READABLE
或者WRITABLE
的. 进程(Process)和线程(Thread)可以是TERMINATED
, 等等...
Signals
获取更多信息.
等待:等待一个, 等待许多, 等待埠
线程可以使用zx_object_wait_one()
等待一个信号Handle, 或者用zx_object_wait_many()
等待一组信号Handle.
这两种方法都允许超时设置, 一旦超时, 即使没有响应信号, 方法也会返回.
由于timer slcak
(计时器松弛)的存在, 超时设置将会有误差, 并不一定在设置的时间返回. 详情请见timer slack
. 如果一个线程想要等待大量的Handle, 那不如使用埠(Port). 埠是一个对象, 可以被其他对象绑定, 这样, 当其他对象上的信号到达时, 埠就会收到与这些信号的信息相关的数据包.
请参阅:zx_port_create()
, zx_port_queue()
, zx_port_wait()
和 zx_port_cancel()
.
事件 (Events), 事件对 (Event Pairs)
事件是最简单的对象,除了其活动信号集合之外没有其他状态。
事件对是可以相互发信号的一对事件之一。事件对的一个有用属性是当一侧消失(所有句柄都已关闭)时,PEER_CLOSED信号在另一侧被断言。(译者按: 量子纠缠?)
请参阅:zx_event_create()
和 zx_eventpair_create()
.
共享内存: 虚拟内存对象(VMO)
虚拟内存对象代表一组物理内存页面,或"潜在"的页面(可以按需创建/填充的页面).
虚拟内存对象可以通过zx_vmar_map()
来映射到进程的地址空间, 用zx_vmar_unmap()
取消该映射. 被映射页面的许可权可由zx_vmar_protect()
来调整.
VMO也可以通过zx_vmo_read()
和zx_vmo_write()
直接读取和写入. 因此,对于一次性操作, 如"创建一个VMO, 将数据集写入其中, 并将其交给另一个进程使用", 可以避免将它们映射到地址空间的成本。
地址空间管理
虚拟内存地址区域(VMAR)提供了管理进程地址空间的抽象。在进程创建时,将向进程创建者提供root VMAR
的句柄。该句柄指的是跨越整个地址空间的VMAR
。这个空间可以通过zx_vmar_map()
和zx_vmar_allocate()
介面进行分割. zx_vmar_allocate()
可以用于生成新的VMAR(称为subregions
或children
), 这可以将地址空间的某些部分组合在一起。
请参阅: zx_vmar_map()
, zx_vmar_allocate()
, zx_vmar_protect()
,zx_vmar_unmap()
和 zx_vmar_destroy()
.
Futexes
Futexes
是与用户空间原子操作一起使用的内核原语, 用于实现高效的同步原语 -- 例如, 竞争情况下的互斥锁实现, 只需要一个系统调用. 通常只有标准库的实现者对他们感兴趣. Zircon
的libc
和libc++
为互斥体, 条件变数等以Futexes的形式提供了C11
, C ++
和pthread
的API.
请参阅: zx_futex_wait()
, zx_futex_wake()
和 zx_futex_requeue()
.
推荐阅读: