SEL4 (一) Kernel 服务和对象

简介

微内核提供有限数量的服务基元;更复杂的服务可以被实现为在这些基元之上的应用。通过这种方式可以在不增加代码和复杂性的情况下扩展系统的功能。

在特权模式下,同时仍然支持潜在的大量服务不同的应用领域。请注意,只有在为MCS1配置内核时,某些服务才可用支持 。

seL4提供的基本服务如下:

线程(Thread):    是支持运行软件的CPU执行的抽象。

Scheduling contexts (MCS only):  是CPU执行时间的抽象。

地址空间(Address spaces): 是每个都包含一个应用程序的虚拟内存空间。应用仅限于访问其地址空间中的内存

IPC(Inter-process communication): 通过端点(endpoints)允许线程进行通信使用消息传递

Reply objects (MCS only):用于存储一次性回复功能(capabilities),并且由接收器在消息传递期间提供。

通知(Notifications):提供类似于二进制信号量的非阻塞信令机制。

Device primitives:允许将设备驱动程序实现为非特权应用程序。内核通过IPC消息导出硬件设备中断;

Capability spaces:存储内核服务的功能(即访问权限)以及他们的记账(book-keeping)信息。

本节概述了这些服务,描述了内核对象是如何由用户空间应用程序访问,并描述如何创建新对象

Capability-based 访问控制

seL4微内核提供了一种基于capability 的访问控制模型。访问控制管理所有内核服务;为了执行操作,应用程序必须调用其拥有的对所请求的服务具有足够访问权限的能力。这样,系统可以配置为将软件组件与每个组件隔离,以及实现组件之间的授权、受控通信,通过选择性地授予特定的通信能力。这将启用软件组件具有高度保证的隔离,因为只有那些授权的操作才会被允许执行。

capability 是一个不可伪造的token,它引用特定的内核对象(例如线程控制块),并且携带控制可以是什么方法的访问权限调用。从概念上讲,capability 驻留在应用程序的capability space中;一空间中的地址指的是可能包含也可能不包含capability 的slot中。应用程序可能指的是一种capability ——例如,请求内核服务——使用保持该capability 的slot的地址。这意味着,seL4 capability 模型是一个分离(或分区)能力系统的实例,其中的capability 是由内核管理。

Capability 空间(space)被实现为内核管理能力的有向图(directed graph)节点(CNode)。CNode是一个slots表,其中每个slot可以包含更多的CNode Capabilitys。能力空间中的能力地址是C节点内的时隙的索引形成到目的时隙的路径;我们讨论在相关章节中详细介绍CNode对象。

Capability 可以在功能空间内复制和移动,也可以通过IPC发送。这允许创建具有特定访问权限的应用程序,授权传递给另一个应用程序,并传递给新的应用程序授权机构创建(或选择)的内核服务。此外,可以铸造功能来创建具有原始能力的权利子集的派生能力(从不具有更多权利)。新产生的功能可用于部分授权。

Capability 可以撤销功能用以撤销权限。吊销递归删除从被撤销的原始能力派生的任何能力。通过系统的能力传播由基于接受-授予的方式控制型号[EKE08,Boy09]。

系统调用(system call)

seL4内核为线程之间的通信提供了一个消息传递服务。该机制还用于与内核提供的服务进行通信。那里是一种标准消息格式,每条消息都包含多个数据字,并且可能有一些能力。描述了这些消息的结构和编码在第X章中详细介绍。

线程通过调用其能力空间内的能力来发送消息。什么时候以这种方式调用端点、通知或回复功能时,消息将通过内核传输到另一个线程。当调用内核对象的其他功能时,消息将以某种方式被解释为方法调用特定于内核对象的类型。例如,调用线程控制块具有正确格式化消息的(TCB)功能将挂起目标线程。

从根本上讲,我们可以将内核视为提供三个系统调用:发送、接收和yield。但是,也有基本Send和的组合和变体接听call。一个重要的变体是Call操作,它包含一个标发送操作原子式地跟在等待答复的Receive的变体后面。A回复消息总是通过特殊资源传递,而不是使用标准IPC机制;有关详细信息,请参见下面的seL4_Call()。

调用除端点和通知之外的内核对象上的方法是使用完成的Send或Call,这取决于调用程序是否想要来自内核的回复(Call)是否(发送)。通过使用libsel4 API提供的函数,您可以保证总是使用更合适的。Yield系统调用与无关任何内核对象,并且是唯一不调用功能的操作。在MCS配置,Wait是Receive的一种变体,不需要回复对象提供——在非MCS配置中,等待与接收同义,因为两个调用都不接受回复对象。

seL4_Yield()

是唯一不需要使用Capability 的系统调用。它会占用调用线程的剩余时间片,并导致调用内核的调度程序。如果没有与调用方具有相同优先级的其他可运行线程,则调用线程将立即使用新的时间片进行调度。在MCS配置中,这种行为取决于调度上下文的状态;

seL4_Send()

通过named capability传递消息。如果被调用的能力是endpoint并且没有接收器准备好立即接收消息,发送线程将阻塞,直到可以传递消息为止。接收对象不会返回任何错误代码或响应.

seL4_Recv() (“receive”)

由线程用于通过endpoint或通知接收消息。如果没有任何发件人或通知挂起,则调用方将阻止,直到可以传递消息或通知为止。此系统调用仅适用于Endpoint或Notification功能,当尝试使用其他功能类型时会引发故障。

seL4_NBSend()

seL4_NBRecv()

seL4_Call()

结合了seL4_Send()和seL4_Recv(),并有一些重要的区别。该调用阻塞发送线程,直到其消息被传递并收到回复消息。当对endpoint以外的内核服务调用Capability 时,使用seL4_-Call()允许内核通过回复消息返回错误代码或其他响应。

当发送的消息通过endpoint传递到另一个线程时,内核执行与seL4_Send()相同的操作。接下来会发生什么取决于内核配置。

对于MCS配置,内核随后更新由接收器提供的应答对象。回复对象是用于跟踪回复消息的容器,用于发送回复消息并唤醒呼叫者。

在非MCS配置中,内核然后在接收器的TCB中的专用slot中存储特殊的应答capability 。

这种回复capability 是发送回复消息和唤醒调用方的一次性权限,这意味着内核在调用后立即使其无效。对于这两种变体,调用线程都会被阻塞,直到调用了回复对象的capability.

seL4_Reply()

用于通过调用由seL4_Call()系统调用生成并存储在回复线程的TCB中的专用slot中的回复capability 来响应seL4_Cll()。它的行为与使用seL4_Send()调用回复capability 完全相同

seL4_ReplyRecv()

seL4_Reply() and seL4_Recv()

seL4_Wait()

工作方式类似于seL4_Recv();在非MCS配置上,它们实际上是同义词。在MCS配置中,seL4_Wait()在没有应答时使用。与seL4_Recv()不同,seL4_Wait()不具有回复功能

seL4_NBWait()(MCS only)

is used by a thread to poll for messages through endpoints or notifications. If no sender or notification is pending, the system call returns immediately.

seL4_NBSendWait()(MCS only)

combines an seL4_NBSend() and seL4_Wait() into one atomic system call.

seL4_NBSendRecv() (MCS only)

combines an seL4_NBSend() and seL4_Recv() into
one atomic system call.

Kernel 对象(Objects)

在本节中,我们简要概述了内核实现的对象类型,应用程序可以调用这些对象类型的实例(也称为对象)。这些对象的接口形成了内核本身的接口。内核服务的创建和使用是通过创建、操作和组合这些内核对象来实现的。

CNode

存储capabilities,允许线程调用特定对象上的方法。每个CNode都有固定数量的slots,总是2的幂(power of two),在创建CNode时确定。slots可以是空的,也可以包含capabilities。

Thread Control Blocks

表示seL4中的执行线程。线程是根据应用程序与其他线程的交互而调度、阻止、取消阻止等的执行单元

Scheduling contexts(MCS only)

在seL4表示CPU时间。用户可以从untyped对象创建调度上下文,但是在创建时,调度上下文为空,不表示任何时间。最初,每个Node都有一个Schedule Control capability ,它允许用参数填充调度上下文,这些参数与优先级控制线程对CPU时间的访问相结合。

Endpoints

便于线程之间的消息传递通信。IPC是同步的:试图在endpoint 上发送或接收消息的线程会阻塞,直到消息可以传递为止。

这意味着,只有当发送方和接收方在endpoint 相遇时,才会进行消息传递,并且内核可以使用单个副本传递消息(或者不使用仅使用寄存器的短消息复制)。端点的capability 可以限制为仅发送或仅接收。

此外,Endpoint capability 可以具有授予权限,这允许将功能作为消息的一部分进行发送。

Reply objects (MCS only)

跟踪调度上下文donation 并且提供用于一次性回复能力的容器。它们由seL4_-Recv()提供。

Notification Objects

提供了一种简单的信号机制。Notification是一个字大小的标志数组,每个标志的行为都像一个二进制信号量。操作是在单个操作中用信号通知标志的子集,轮询以检查任何标志,并阻塞直到任何标志被用信号通知。通知功能可以是仅信号或仅等待。

Virtual Address Space Objects

用于为一个或多个线程构造虚拟地址空间(或VSpace)。 这些对象在很大程度上直接对应于硬件的对象,因此依赖于体系结构。内核还包括用于跟踪地址空间状态的ASID池和ASID控制对象。

Interrupt Objects

赋予应用程序接收和确认来自硬件设备的中断的能力。最初,IRQControl有一个功能,它允许创建IRQ处理器功能。IRQ处理器能力允许管理与特定设备相关联的特定中断源。它被委托给设备驱动程序来访问中断源。IRQHandler对象允许线程等待并确认各个中断。

Untyped Memory

是seL4内核中内存分配的基础。非类型化内存(Untyped Memory)capabilities  只有一个方法,可以创建新的内核对象。

如果该方法成功,调用线程将获得对新创建对象的功能的访问权限。

此外,非类型化的内存对象可以划分为一组较小的非类型化内存对象,允许委派部分(或全部)系统内存。

Kernel 内存分配

seL4微内核不会为内核对象动态分配内存。相反,对象必须通过Untyped memory功能从应用程序控制的内存区域显式创建。

应用程序必须具有明确的内存权限(通过这些Untyped memory功能)才能创建新对象,并且所有对象在创建后都会消耗固定数量的内存。

这些机制可用于精确控制应用程序可用的物理内存的特定数量,包括能够强制隔离应用程序或设备之间的物理内存访问。除了硬件2规定的资源限制外,内核中没有任意的资源限制,避免了许多通过资源耗尽的拒绝服务攻击。

在boot时,seL4预先分配内核本身所需的内存,包括代码、数据和堆栈部分(seL4是单个内核堆栈操作系统)。然后,它创建一个初始用户线程(具有适当的地址和功能空间)。然后,内核将所有剩余的内存以Untyped memory的功能的形式交给初始线程,并将一些额外的功能交给引导初始线程所需的内核对象。

然后,可以使用seL4_Untyped_Type()方法将这些非类型化内存区域拆分为较小的区域或其他内核对象,创建的对象称为原始非类型化存储器对象的子对象。使用seL4_Untyped_Type()创建对象的用户级应用程序接收对结果对象的完全权限。然后,它可以将对该对象拥有的全部或部分权限委托给一个或多个客户端。

UntypedMemory对象表示两种不同类型的内存:通用存储器或设备存储器。

通用内存可以被非类型化为任何其他对象类型,并用于内核提供的非类型化内存上的任何操作。

设备内存覆盖由硬件平台确定的为设备保留的内存区域,内核通过以下方式限制这些对象的使用:

        * 设备非类型化的对象只能重新键入到帧或其他非类型化对象中;例如,开发人员不能从设备内存中创建端点。

        * 从设备重新键入的帧对象未键入的对象不能设置为线程IPC缓冲区,也不能用于创建ASID池

子非类型化对象的类型属性(无论是表示通用内存还是设备内存)是从其父非类型化的对象继承的。也就是说,未键入设备的任何子级也将是未键入设备。开发人员无法更改非类型化的类型属性。

Reusing Memory

到目前为止描述的模型足以让应用程序分配内核对象,在客户端应用程序之间分配权限,并获得这些对象提供的各种内核服务。仅此一点就足以用于简单的静态系统配置。seL4内核还允许重用非类型化内存区域。只有当内存中的对象没有悬空引用(即功能)时,才允许重用该内存区域。内核跟踪功能派生,即由方法seL4_Untyped_Type()、seL4_CNode_Mint()、seL4_CNode_Copy()和seL4_CNode_Mutate()生成的子级。

这样生成的树结构被称为能力推导树(CDT)。例如,当用户通过重新键入非类型内存创建新的内核对象时,新创建的功能将作为非类型内存功能的子级插入CDT。

对于每个Untyped Memory区域,内核都会保留一个水印,记录该区域之前分配了多少。每当用户请求内核在非类型化内存区域中创建新对象时,内核将执行以下两个操作之一:

如果该区域中已经分配了现有对象,则内核将在当前水印级别分配新对象,并增加水印。

如果之前在该区域中分配的所有对象都已删除,内核将重置水印,并再次从该区域的开头开始分配新对象。

最后,CNode对象提供的seL4_CNode_Revoke()方法破坏了从参数功能派生的所有功能。撤销内核对象的最后一个功能会触发对现在未引用的对象的销毁操作。这只需清理它、其他对象和内核之间的任何内核内依赖关系。

通过对非类型化内存对象的原始功能调用seL4_CNode_Revoke(),用户将删除该非类型化存储器对象的所有子对象,即指向非类型化存储区域中对象的所有功能。

因此,在该调用之后,在非类型化区域内没有对任何对象的有效引用,并且可以安全地重新类型化和重用该区域。

Summary of Object Sizes

当重新键入非类型化内存时,了解对象需要多少内存是很有用的。对象大小在libsel4中定义。

请注意,CNode、ScheduleContexts(仅限MCS)和Untyped Object具有变量大小。

将非类型化内存重新键入CNode或ScheduleContexts,或将非类型对象拆分为较小的非类型对象时,seL4_untyped_Type()的size_bits参数用于指定生成对象的大小。对于所有其他对象类型,大小是固定的,并且忽略seL4_Untyped_Type()的size_bits参数。

对seL4_Untyped_Type()的单个调用可以将单个Untyped对象重新键入到多个对象中。要创建的对象数由其num_objects参数指定。

所有创建的对象都必须是由类型参数指定的相同类型。对于大小可变的对象,每个对象也必须具有相同的大小。如果所需内存区域的大小(由对象大小乘以num_objects计算)大于Untyped object的剩余未分配内存,则会导致错误。

  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值