Mach overview (Mach概述)
OS X内核的基本服务和基元都是基于Mach 3.0的。苹果已经修改和扩展了Mach,以更好地满足OS X的功能和性能目标。
Mach3.0最初被构思成一个简单的可扩展的通信为内核。它是有可以作为一个独立内核运行的能力的,和其他传统的操作系统服务(比如I/O、文件系统、网络进程)一样作为服务运行在用户模式下。
然后,在OS X中,Mach和其他内核组建链接在一起放在一个单独的内核地址空间中。这样做是为了性能考虑;在分开的任务之间通过直接访问调用(在链接的组建中)比通过发送消息或者远程程序访问快的多。这种模块化结构产生了一个比单一内核更健壮和可扩展的系统。
因此在OS X中,Mach不是主要的通信中心在用户和服务之间。相反,他的价值在于他的抽象性、可扩展性和灵活性。特别是,Mach提供了
基于对象APIs带有通信通道作为对象引用
高并发执行、包含抢先调度线程为了支持SMP
灵活的调度框架,支持实时使用
一个完整的IPC基元集,包含发送消息、RPC、异步和同志
支持更大哦的虚拟地址空间、共享内存区域和依靠持久化存储的内存对象
拥有可扩展性和可移植性,比如跨指令集架构和分布式环境
安全和资源管理作为最基础的原则来设计;所有的资源都是虚拟化的
Mach内核抽象
Mach提供了设计中一小部分抽象,这些都很简单而强大。这些都是主要内核的抽象:
任务。资源拥有者的单元;每个任务都拥有一个虚拟地址空间,一个端口权限命名空间,还有一个或多个线程(类似进程)
线程。在任务中CPU执行单元
地址空间。和内存管理一起,Mach提供了一个离散虚拟地址空间和共享内存。
内存对象。内存管理内部单元。内存对象包含了命名实体和范围;他们是隐藏数据的表示,这些数据可能已经被映射到地址空间。
端口。安全单一的通信通道,具有只能通过发送和接受来访问的能力(端口权限)
IPC。消息队列,远程程序访问,同志,信号量,和锁集合
时间。计时器,定时器和等待。
在陷阱级别,大多数Mach抽象的接口都包含了发送和接受来自通过对象表示的内核端口的消息。
线程是基于运算的实体(存在),一个线程只归属于一个task,并且这个task定义了它的虚拟地址空间。影响着这块地址空间结构,或者引用除地址空间之外的一些资源,线程必须执行一个指定的陷阱指令,原因是内核代表线程执行一些操作或者是发送消息给某些代理。通常这些陷阱操作修改任务中线程相关的资源。这些请求都是内核创建出来去操作这种实体的:创建他们,删除他们并且修改他们的状态。
Mach为线程调度策略提供了灵活的框架。早期OS X版本支持分时和灵活优先级的策略。(分时(Time Sharing)操作系统的工作方式是:一台主机连接了若干个终端,每个终端有一个用户在使用。用户交互式地向系统提出命令请求,系统接受每个用户的命令,采用时间片轮转方式处理服务请求,并通过交互方式在终端上向用户显示结果。用户根据上步结果发出下道命令)。分时线程通过升高和降低其优先级来平衡其与其他分时线程的资源消费。
灵活优先级是为了线程在一个非常准确的的时间点去执行,他们会被放在相同优先级线程队列的尾部。在允许的范围中,设置一个线程的固定优先级为无穷大,这个线程会被执行直到阻塞,或者倍更高优先级的线程抢占。高优先级实时线程通常都是有固定优先级的。
OSX为了能实时执行,提供了时间约束调度。这个调度允许你在一个确定时间周期中指定你自己线程的获取特定时间量。
Mach 调度具体描述是在这里Mach Scheduling and Thread Interfaces
端口,端口权限,端口设置和端口命名空间
任务虚拟地址空间的异常和其他所有的Mach资源都可以通过端口访问,端口被称作间接层。一个端口是一个单向通信通道的端口,这个通道是在请求服务的客户和提供服务的服务之间建立的。如果对这样的服务请求提供回复,就必须用到第二个端口。类似UNIX中的单向通信管道pipe。
在很多情况下,通过端口访问的资源都会被作为一个对象被引用。许多对象都是通过端口来命名,这些对象有一个单独的接受者还有可能存在很多发送者。对于一个典型的对象,比如一个消息队列,就会存在一个确定的接收端口,和至少一个的发送端口。
通过对象提供的服务取决于一个管理者,这个管理者是可以接受发送请求的对象。对于提供内核对象关联的端口,内核是个接受者,而且这个提供内核对象关联端口的接受者是任务提供的。
对于提供任务对象的端口,可以将该端口请求的接收方更改为不同的任务,例如,通过在消息中将端口传递给该任务。一个单独的任务可能有很多个端口来引用它支持的资源。对于这个问题,任何被给与的实体,都可能有多个端口来代表它,它们每个都包含了不同的可允许操作,很多对象都有命名端口和控制端口(有时被称为特权端口)。这些对象可以通过访问控制端口来操作修改;对名称端口的访问只是对对象进行命名,以便您可以获取有关它的信息或对它执行其他非特权操作。
任务通过确定的方式来访问端口(发送,接受,一次性发送);这些都称为端口权限。一个端口只能通过一种权限访问。端口通常可以保证代理用户访问Mach中的对象的。拥有向对象的IPC端口发送数据的权限意味着有权以规定的方式操作对象。如上所述,端口权限所有权在Mach中是安全机制的基础。拥有对象的权限就是拥有访问或操作该对象的能力。
端口权限可以被复制并且可以通过IPC在任务之间被移动。实际上,这样做是将功能传递给某些对象或服务。
端口引用一种类型的所有对象,是一个端口集。正如名字代表的含义,一个端口集是一组端口权限,当这个集合中的任意一个成员收到消息和事件,他们可以被作为一个单元被处理。端口集允许一个线程等待许多消息和时间资源,比如工作循环。
习惯上,在Mach中,端口表示的通信通道经常被视为一个消息队列。然而,OSX支持通信通道的附加类型,这些IPC对象的新类型是通过端口和端口权限来表示。更多消息和其他IPC类型的相信信息可以在这部分看Interprocess Communication (IPC)
没有系统范围命名的端口和端口权限可以被任意的端口或者权限去直接修改。端口仅仅可以被拥有此端口命名空间权限的任务进行修改。一个端口权限可以通过端口名称来指定,这个名称是32位命名空间的一个整数索引。每个任务都会有一个单一端口命名空间。
当其他任务需要将他们的命名空间插入到当前任务的命名空间时他们需要当前任务的端口权限,当它们在消息中接收到权限时,这些任务将通过创建返回该对象的权限的对象以及通过对某些特殊端口的Mach调用来获取端口权限
内存管理
跟大多数现代操作系统一样,Mach 提供了更大更宽松的虚拟地址空间。运行时访问就是通过虚拟地址来实现的。这些虚拟地址在没有访问到初始化的时候,在物理内存中可能没有相对应的位置。Mach负责获取请求的虚拟地址并为其分配物理内存中的相应位置。它通过请求分页来实现这一点。
当内存对象映射到这段虚拟地址空间时,使用数据填充到此位置。虚拟地址空间的所有数据基本上都是通过内存对象提供的。当在物理内存中建立页面时,Mach会向存储对象(页面调度器)的所有者询问页面的内容,并在回收页面之前将可能修改的数据返回给页面调度器。OS X包含两个内置页面调度器,一个是默认的一个是vnode调度器。
默认页调度器处理非持久的内存,有我们知道的匿名内存(例如栈堆,可回收的,没有对应的外置内存)。匿名内存是初始化是0,而且他只存在于任务的生命周期中。vnode页调度器将文件映射到内存对象。Mach提供了内存对象的一些接口,这些接口可以让他们在用户模式下的任务将其内容分享被使用。这个接口就是我们所知道的扩展内存管理接口,或者简称EMMI。
内存管理子系统导出虚拟内存句柄,称为命名条目或命名内存条目。大多数内核资源,他们都可以通过端口来表示。拥有命名内存实体的允许拥有者将其映射到基础虚拟内存对象,或者通过权限将基础内存对象映射给其他。
在两个任务中映射命名实体,会导致两个不同任务中有共享内存窗口,因此为了建立共享内存,提供一个灵活的方法。
从OS X 10.1版本开始,扩展内存管理接口系统会支持“portless”,在传统的扩展内存管理接口中,对于每个内存空间,只创建两个Mach端口,同样对于每个缓存vnode也会创建两个端口。Portless EMMI,在其初始化实现中,取代之前的是直接引用内存(基于指针)。在将来的release版本,端口将被用来内核以外页调度器之间的通信,在内核控件中,使用直接引用来进行页调度器之间的通信。这些更改在早期版本中的portless EMMI,是不支持内核空间以外的页调度器的。希望能再未来版本中尽快恢复支持。
虚拟地址空间的地址范围可以通过直接初始化来进行分配(vm_allocate)。基础虚拟内存对象是通过默认页调度器来匿名和支持的。地址空间的共享范围可以通过寄存来设置。当一个新的任务被创建,他们可以通过父任务克隆出来。克隆也适用于基础内存地址空间。基于与映射关联的属性,对象的映射部分可以作为副本继承,也可以作为共享继承,或者根本不继承。映射的部分对象可以被继承,然后作为复制或者共享或者什么都不是,Mach实现了一种叫做copy-on-write的延时复制,用于优化任务创建中的继承复制。
相比于直接复制一段范围,copy-on-write的优势在保护共享的前提下是很完善的。两个任务共享的内存会被复制,但是只读访问。当其中一个任务试图修改这段内存的某一部分时,这部分将在这个时间被复制。内存复制的这种懒惰是一个重要的优化,可以简化多个领域,尤为显著的是消息APIs。
另一种共享形式是Mach提供的,通过导出命名范围。命名范围是命名实体的一种形式,但不是由虚拟内存对象来支持,他是由虚拟映射碎片支持的。这些碎片可能拥有许多虚拟内存对象的映射。他们是有映射到其他虚拟映射的能力,他们提供的继承方式不仅有一组虚拟内存对象而且还保留他们的映射关系。这些特点在任务设置中提供了有意义的优化,比如当分享一个复杂的地址空间区域,可以使用共享库。
进程间通信Interprocess Communication(IPC)
通信在Mach的哲学中是重要的一个元素。Mach支持客户机/服务器系统结构,其中任务(客户机)通过经由通信通道发送的消息发出其他任务(服务器)的请求来访问服务。
这些通信通道的终端在Mach中被称作端口,端口权限表示允许使用此通道。由Mach内部提供的一种IPC形式。
消息队列
信号
通知
锁集合
远程程序访问
IPC形式的对象表示通过端口决定的可允许操作,在此端口上,以及如何(以及是否)进行数据传输。
重点:在OS X中的IPC设施都处于过渡状态。在早期系统版本中,不是所有的IPC类型都被实现了。
对于原生端口修改,这里有两个基础的不同MachAPI,一个是Mach_ipc家族一个是mach_msg家族。其中的原有,两个家族都可以跟任意IPC对象一起被使用;然而,在新版代码中,mach_ipc调用被优先使用。mach_ipc调用在适当的地方维护状态信息,以支持事务的概念。旧代码支持mach_msg调用,但已弃用;他们都是可以使用的。
IPC事务和时间分发
当线程调用mach_ipc_dispatch,它会反复的处理来自注册端口集合中的事件。这些事件可以是来自RPC对象的参数块(作为用户代理调用的结果)、正在获取的锁对象(作为其他线程释放锁的结果))、正在发布的通知或信号量,或者来自传统消息队列的消息。
这些事件通过mach_msg_dispatch的callouts(标注)处理。在callouts的生命周期中,这些事件都含有一个事务。就锁而言,锁拥有者的状态在callouts的时候,这个锁会被释放掉。就远程程序访问来说,状态就是用户标志、参数块、回复端口。当callout return的时候,回复将会被发送出去。
当callout 返回时,事务也就结束了,线程会等待下一个事件。mach_ipc_dispatch的作用是为了支持工作环。
消息队列
最初,Mach中进程通信的唯一方式是消息队列。仅有一个任务拥有表示消息队列端口的接收权限。这个任务被允许接收或读取来自其他端口队列的消息。大多数任务拥有访问端口的权限,这些端口允许他们发送消息到队列中。
一个任务和另外一个任务通信,需要通过创建包含数据元素集合的数据结构,然后在他们所拥有权限的端口执行发送消息的操作。一段时间后,有接受权限端口的任务会执行消息接受操作。
一个消息可能由下面全部或者部分组成:
纯数据
内存范围复制副本
端口权限
内核的属性,比如发送者安全token
消息的传输是一个异步的操作。消息会被逻辑复制放到接收的任务中,可能会有写时复制的优化。接收任务中的多个线程都会尝试去从给定的端口接受消息,但是只有一个线程可以接受被给定的消息。
信号
信号IPC对象支持等待、发送,和发送的所有操作。这就是计数信号,信号等待队列中如果当前没有线程等待,这个发送将会被保存(被计数)。一个发送的所有操作会唤醒当前所有等待的线程。
通知
像信号,通知对象,也支持发送和等待操作,但是会有一个状态范围。这个状态是固定大小固定格式的,会在通知对象被创建的时候定义。每个发送都会更新状态范围;状态时单一的,会被每个端口复写。
锁
锁是一个提供互斥访问临界区的对象。锁的主要接口是面向事务的。在事务中,一个线程持有了锁。在这个事务返回的时候,锁会被释放。
远程程序访问对象(RPC)
正如名字的含义,一个被设计的RPC对象是为了促进和优化远程程序访问的。RPC对象的主要接口是面向事务的。
当RPC对象被创建的时候,一套参数块格式被定义。当通过用户创建一个RPC(发送对象)时,它将导致创建一种预定义格式的消息并在对象上排队,最终传送到服务端。当服务端将要返回事务时,这个返回的回复将会被发送出去。Mach尝试通过服务端使用用户资源执行来优化事务;这被称作线程迁移(thread migration)。
时间管理
Mach中时间的传统抽象是时钟,提供了基于Mach_timespace_t的一些列异步时钟。其中会有一个或多个时钟对象,每个都定义了用纳秒为单位的单一递增时间值。实时时间锁的创建是非常重要的,在系统中还有不同其他时间观念的锁。时钟支持获取当前时间、在给定时间段内休眠、设置闹钟(在给定时间发送的通知)等操作。
在OS X中mach_timespec_t API是被弃用的。新的基于时间对象的API,转换使用ABsoluteTime作为基础数据类型。AbsoluteTime是机器相关的类型,通常是基于平台本身的时间。通常会提供从其他数据类型转换成AbsoluteTime值,比如纳秒。时间对象支持异步,drift-free notification、取消,并且提前警报。与时钟相比,它们效率更高,分辨率更高。