我认为微内核的意义(2)-分析zircon

 简介

内核消息是一些不同类型的对象。这些对象可以直接通过syscall调用,并且这些对象是C++的类,由调度接口实现,位于kernel/object目录。许多是自包含的高阶对象,一些是包含低阶的lk原语。

syscall

用户空间代码与内核对象的交互通过syscall,大多数仅通过Handle。在用户空间,一个Handle是32位整型(zx_handle_t)。进程的Handle存在于表中,表中的Handle具有参数,当进程发起syscall,内核会检查发起调用的进程的实际Handle的参数。内核进一步检查Handle类型正确(传递线程Handle给需要事件Handle的syscall将会引起错误),并且Handle必须要有所请求的操作的所需权限。

syscall从访问的角度可分为3类:

1.没有限制(很少),例如zx_clock_get()、zx_nanosleep()可以被任何线程调用。

2.需要Handle作为第一参数的,表示其将要操作的对象(很多且主要),例如zx_channel_write()和zx_port_queue()。

3.创建新对象但不需要Handle的,比如zx_event_create()、zx_channel_create()。这些syscall的调用由进程所属的Job控制。

syscall由libzircon.so提供,libzircon.so是一个虚拟的共享库,由Zircon内核提供到用户空间(详见virtual Dynamic Shared Object or vDSO)。他们是C ELF ABI函数,格式为zx_noun_verb()或zx_noun_verb_direct-object()。

系统调用由syscalls.abigen定义,由abigen工具处理为libzircon中的包含文件和粘合代码,以及内核的syscall库。

Handles和权限

对象可能被关联到多个Handles(在1或多个进程里)。

对于几乎所有对象,当最后一个关联它的Handle被关闭,这个对象会被销毁或放入不可逆的最终状态。

Handles可以通过写入Channel实现从一个进程移动到另一个进程;也可以通过zx_process_start()来将Handle作为一个新进程第一线程的参数传入。

对Handle或对象的操作由与Handle相关的权限支配。两个关联同一对象的Handles可能有不同的权限。

syscall的zx_handle_duplicate()和zx_handle_replace()可能被用于得到相同对象的额外Handles,获得的Handle可以与之前的一样,也可以减少权限。zx_handle_close()会关闭Handle,关闭Handle会释放关联的对象,如果被关闭的Handle是最后一个与对象相关联的Handle,对象将会被释放。zx_handle_close_many()用于关闭一组Handles。

内核对象的ID

每一个内核中的对象都有一个ID,是64位无符号整型,可以用于识别对象,并且在系统运行中是唯一的。这也意味着内核对象ID不可重用。

有两个特别的对河对象ID:ZX_KOID_INVALID是0,被用作哨兵;ZX_KOID_KERNEL只有一个内核,有其自己的ID。

运行代码:Jobs,进程和线程

Jobs含有进程,并定义了多样的资源限制。Jobs归属于父Jobs,一直到根Job,根Job由内核创建并送到userboot。

没有Job Handle,进程里的线程是不可能创建另外的进程或Job的。

程序的加载有用户空间实现。

消息传递:Sockets和通道

Socket和通道都是IPC对象,都是双端双向。创建一个Socket或Channel将会返回两个Handles分别关联到对象的端点。

Socket是面向流的,数据作为1或多个bytes的单元写入或独处。短写(缓存不够)和短读(要读的比缓存中的多)都是支持的。

通道是面向数据报的,有最大消息长度64K(可以配置的更小),一个消息可以最多附属1024个Handles。不支持短读短写,一个消息要不就是合适,要不就是不合适。

当一个消息写入通道,其就会被从发送进程中切除,当接收进程把一个消息和Handle从通道中读出,这个Handles就会被加入到接收进程。在这两个事件期间,Handles会一直存在,除非通道另一端的接收进程关闭了端点,其中的所有Handles都关了。

对象和信号

对象最多可以有32个信号(由zx_signals_t类型和ZX_SIGNAL宏定义),这些信号可以代表一部分对象的当前状态:比如通道和Socket所处的可读或可写的状态;进程和线程则可能是TERMINATED。还有其他状态不列举。

线程可能等一到多个对象的信号来唤醒。

等待:等待一或多或端口

一个线程可能使用zx_object_wait_one()或zx_object_wait_many()陷入等待一到多个信号(一或多个Handles的);这两个syscalls都可以设置超时。

如果一个线程要等待大量的Handles,更高效的做法是使用端口;端口是一个可以绑定其他对象的对象,当信号插入到对象,端口会收到一个包含信号源信息的包。

事件和事件对

事件是最简单的对象,除了采集活跃信号外没有其他动作。

一个事件对是一对可以互相发出信号的事件。一个有用的属性就是,当一个事件对的一端关闭(所有关联的Handles都关闭),另一端就会发生PEER_CLOSED信号。

共享内存:虚拟内存对象

虚拟内存对象表示一组内存物理页面(或等待缺页中断建立映射的页面)。

zx_vmar_map()和zx_vmar_unmap()分别可以将其映射进入进程或断开映射。映射页的权限可以用zx_vmar_protect()调整。

虚拟内存对象也可以通过zx_vmo_read()和zx_vmo_write()直接读或写入。用映射的方式使用可以避免写入和读取,直接在其上操作,并发送到其他进程。

地址空间管理

虚拟内存地址克难攻坚提供管理进程内存空间的抽象。在进程创建时,根虚拟内存地址空间的Handle会提供给进程创建者,这一Handle会关联到一个VMAR,这个VMAR跨有整个地址空间;这个空间可以通过zx_vmar_map()和zx_vmar_allocate()简历到物理地址的映射。zx_vmar_allocate()可以被用于生成另外一个虚拟内存地址空间,被称作子空间或孩子,可以用于将地址空间的一部分组合在一起。

Futexes

Futexes是内核原语,用于用户空间原子操作,实现高效的同步原语;比如Mutexes,只需要在竞争的场合发起一个syscall即可;一般来说,他们只被是现在标准库中。Zircon的libc和libc++为互斥、条件变量等提供C11、C++和线程的API,基于Futexes实现。

你可能感兴趣的:(微内核评估)