面向对象的Amiga Exec(1991)

2021-02-21 07:35:39

那么,使Amiga变得如此小的操作系统缺少什么呢? Amiga如何在不到苹果公司和IBM计算机所需内存的四分之一的四分之一的时间内提供多任务和窗口服务?答案在于最大程度的裁员。如果检查许多流行的操作系统的内部结构,就会发现它是OS与它们的比较。就是说,您拥有一块有点单片的东西,它是核心操作系统,一些充当设备驱动程序的伏都教等等,以及应用程序代码-只有不轻松的停战才能让他们见面。即使操作系统本身可能由许多组件组成,但对于应用程序程序员来说,它的外观本质上仍然是一个相当神秘的建筑,超出该范围之后,只有应用程序软件的选定部分才可以使用。

Amiga操作系统不同。相对来说,很少部分是完全不透明的;实际上,随着操作系统的更高版本,趋势是进一步开放其内部供应用程序使用。考虑到与Macintosh不同,Amiga在非特权状态下运行其应用程序,这更加令人惊讶。

没有魔力当今大多数操作系统都以操作系统代码为" magic&#34 ;;的方式运行。也就是说,它花费了大部分时间在特权状态下运行,禁止中断,执行普通应用程序程序员难以理解的奥术指令,或者执行完全超出了应用程序编程范围的事情。这不是真的。

很少有操作系统代码是真正的魔术。它大部分处理表,列表,队列和其他此类普通任务的管理。但是,由于这些是操作系统表列表,队列等,因此通常使用在特权,不间断,内存管理或其他神秘环境中运行的特殊例程来管理它们。

往往被忽视的是,通常有可能把所有“魔法”都带走。部分代码,并将其与" nonmagical"分开部件,从而产生了一组用于切换模式的控制例程,以及许多看起来可疑的数据处理例程。

这种情况意味着,首先,存在这样的可能性:可以创建其中一些例程的通用版本来替换所有这些相似但不相同的功能;其次,由于这些例程不再是魔术,因此应用程序也可以访问它们,从而减小了它们的整体大小和复杂性,更不用说使用预先调试的代码节省的时间了。

对某些关键的操作系统功能使用通用代码似乎有些杂乱无章-毕竟,这不是通用的。和" efficiency"互斥?答案似乎是否定的,或更准确地说,如果它是通用的并且效率不高,那么也许它的通用性还不够。在通用代码中通常会浪费时间的是它为处理其处理的数据结构的变化而必须进行的所有测试和分支。

OOP会在哪里出现?答案在于继承原则。通过像我们在传统系统中对系统代码所做的那样安排系统数据结构,并将相关数据项放在一个公共序列中,我们获得了两个好处:减少了令人反感的特殊情况处理量,以及创建了数据对象类的层次结构。副作用是,系统变得更易于理解-独特功能更少。

如前所述,Inside View Exec是Amiga操作系统的核心(或内核),可以通过这种方式实现。 Exec由越来越复杂的对象类组成,如图1所示。当基于一个更简单的类定义一个新的Exec对象类时,它包含该更简单类的所有数据对象,并且(通常)有效不仅适用于为该类定义的操作,还适用于与较简单类有关的所有操作。这称为函数继承。

图1:Amiga操作系统的核心由一系列日益复杂的对象类组成。 Exec对继承的高度依赖使得继承如此紧凑。它不包含用于处理任务,I / O设备,任务间消息等的单独例程集;相反,它包含处理对象集合的基本例程(无论是任务对象,设备对象还是其他对象),并且仅在需要其他支持的情况下才添加功能。相反,许多操作系统包含任务例程的集合(包括管理任务表所需的例程)和设备例程的集合(包括管理设备表所需的例程),等等。

有很多内部表示数据收集的方法,每种方法都有其优点和缺点。 Exec基于双向链表。列表元素是从RAM中任何方便的地方动态分配的(请参阅内存管理部分);因此,没有表格可以填写。另一方面,访问速度高度依赖于列表中的节点数,但是有减少该问题的方法,这将在后面解释。

Amiga操作系统与众不同之处在于,操作系统本身为双链表提供了支持。 Exec支持两个级别的列表:列表和MinLists。 (Lattice C ++实现增加了两个与标准Exec列表类似的功能,但没有自动初始化。)它们用于定义Amiga系统软件初始化的项目。

MinList是MinNodes双向链接列表的锚点。 MinNode包含下一个和上一个MinNode指针。 MinList结构包含一对伪MinNode,以通过减少处理列表末尾项目所需的特殊情况逻辑来简化处理。空的MinList始终由两个MinNode组成-MinList的前端和末尾的虚拟节点-这两个都包含在MinList数据结构本身中。虚拟前节点的下一个节点指针指向列表的实际第一个节点。它的上一个节点指针始终为NULL。

关于虚拟端节点,存在类似情况。由于虚拟前端节点的上一个节点指针始终为NULL,而虚拟末端节点的下一个节点指针也始终为NULL,因此可以通过共享使它们重叠来节省少量内存相同的NULL指针。虚拟MinNode确实增加了一个复杂性:列表的最后一个实际项不是具有NULL下一节点指针的项:该荣誉属于虚拟节点。

存在一套完整的功能来支持在MinList的两端(或两端)之间插入和删除MinNode。因此,通过使用适当的功能,MinList可以用作先进先出(FIFO)(也称为队列)或先进先出(LIFO)(也称为后进先出)。堆栈),以及通用列表。

再一次,请注意,MinList,MinNode或作用于它们的任何功能都没有神奇之处。尽管它们被Amiga操作系统广泛使用,但它们可以在任何应用程序中自由使用。

朋友和成员关于C ++朋友和成员函数的说明。当用C ++定义一个类时,(除非另外指定)它的内部组件受到保护,以防止随意访问。这是一个很强的卖点。它使对象的内部损坏变得更加困难,并且更容易找到负责的功能。因此,为了实际使用(和更改)类对象中的信息,需要某种访问机制。 C ++提供了两种:朋友函数,类似于传统的C函数,不同之处在于,通过被声明为一个或多个类的朋友,可以直接访问存储在该一个或多个类中的数据,以及成员函数实际上是由特定类别拥有的,因此具有" invisible"传递给它的额外参数:" this,"这是作用于该类对象的指针。

Exec设计用于非OOP语言;因此,Exec函数实际上是朋友函数。由MTS Associates组成的,用于在Amiga上支持C ++的#include文件通常是这样定义的。但是,为了更好地支持Exec作为面向对象系统,它还定义了许多成员函数。例如,实际上Amiga中的每个对象都在某种列表中,因此大多数对象都有一个名为next()的成员函数。不管它是什么,无论它是如何链接的,以及该对象的下一个项目指针的名称或相对位置如何,都始终保证可以得到指向该对象的指针。使用next()函数在列表中的下一个。

列表:MinList,然后是Some列表是扩展的MinList,由节点组成。节点是MinNodes,外加一个1字节的类型字段,一个1字节的优先级和一个指向节点名称的指针,该名称是C格式的字符串。图2显示了节点的结构。节点合并了MinNode的结构,因此自动继承了MinList的属性以形成列表。

随后,列表可以使用所有MinList函数。就像MinList一样,列表可以维护为FIFO或LIFO。但是,也可以通过列表好友函数Enqueue()优先维护列表。如果为列表中的节点指定了名称,则还可以在列表中搜索该名称的第一个/下一个节点。这可能非常有用,因为这是Exec定位多个公共对象的方式。

信号信号由包含信号标记模式的32位字表示。用户可以分配16个,操作系统保留16个。当一个任务发出信号通知另一个任务并且另一个任务处于信号等待状态时,接收任务的传入信号信息将传入信号位进行逻辑或运算。然后,将其与接收者任务的模式进行“与”运算表示它正在等待。非零结果导致任务变得可调度。

这是激活睡眠任务的一种非常有效的方法,可以在任何系统级别(包括在中断例程中完成)执行,许多较复杂的系统服务都无法使用中断例程。

消息图3显示了如何从节点构造消息。信息在Amiga的操作中极为重要。它们用于将信息从一个任务传递到另一个任务,作为I / O请求的基础,并作为Intuition鼠标和窗口事件的传输媒介。与信号不同的是,信号只能在此处给出“!”!指示,一条消息可能带有复杂的信息。

图2:可以按名称定位节点,并按优先级排列。节点由列表锚定。由于节点包含一个MinNode,因此它将继承所有列表属性和功能。

图3:Amiga操作系统使用" messages"以将信息从一个任务传递到另一个任务,以此作为1/0请求的基础,并作为Intuition鼠标和窗口事件的传输媒介。消息包含一个节点,因此将继承其所有属性和功能。

图4:一条消息按优先级顺序传输到消息端口,该端口包含要服务的传入消息的列表。像消息一样,消息端口包含一个节点,因此继承了节点的所有属性和功能。

消息是扩展节点,通常以优先级顺序传输到消息端口(MsgPort),这是另一类节点,其中包含要服务的传入消息的列表(请参见图4)。 MsgPorts可以是私有的也可以是匿名的,也可以将它们添加到系统消息端口列表中。通常,它们成对出现(每对一个),因为在处理完一条消息后,通常将其转发到MsgPort答复,在此通常将其回收或丢弃-尽管可以将消息整体退回系列端口。有几种不同的方法来实现MsgPort,但是最常见的方法由名为StdPort的特殊C ++类(或标准消息端口)支持,可以通过编码来创建和初始化

StdPort *侦听器=新的StdPort("我听到你了"); StdPort构造函数负责标准MsgPort初始化的所有详细信息。内存被分配和初始化,并获取一个信号,监听任务可以等待。使用AddPort函数,可以将MsgPort放置在系统的公共MsgPort列表中,任何希望向其发送消息的任务都可以在该列表中找到该MsgPort。因为Exec是按面向对象的方式设计的,所以新的操作系统功能非常简单。 C ++重构显示以下内容:

无效AddPort(MsgPort * mport){Forbid(); //禁用任务切换入队(AbsExecBase-> PortList,mport);使能够( ) ; //重新启用任务切换}这是另一个系统功能的重构:MsgPort * FindPort(const char * portname){返回(MsgPort *)AbsExecBase-> PortList.find(portname); } FindPort说明了另一个重要的设计功能。如果每次要访问列表中的元素时都搜索系统列表,则系统性能会受到影响。相反,约定是搜索并返回对象的地址。此后,可以直接使用对象的地址(Amiga不使用Macintosh风格的句柄,这会导致对象在内存中移动)。不利的一面是,您绝不能移动或移除其他任务可能正在使用的对象。库和设备通过维护用户数量来确保这一点。对于简单的消息端口,应用程序应强制执行登录/注销功能,或者要求所有消息必须一次性发送(即FindPort / PutMsg)。

每个任务最多只能有32个不同的信号,但可以有无限数量的MsgPort。同一信号可以被多个MsgPort使用,这使Intuition任务不受限于有限数量的打开窗口。

如果检查许多流行的操作系统的内部结构,您会发现它与它们相比是0S。

IORequests IORequests是扩展的消息,包括I / O控制和传送到设备的传输信息。所有IORequest都具有一组基本的命令(读,写,控制等)。对于给定的设备,可以根据需要添加其他扩展。派生了许多专用设备的IORequest类。任何设备实现者都可以根据需要自由派生自己的扩展。出现类似以下内容的情况很常见:

MyDeviceRequest是基于:StdIORequest是基于:IORequest是基于:消息是基于:Node是基于:MinNode在继承的每个级别上,您都可以获得其他属性和功能。唯一需要的新代码就是支持您自己独特的对象类的代码。

库节点的另一种重要类型是库。它由一个基本结构组成,后面是功能向量,然后是可选的私有存储。所有库都有一组通用的基本功能(例如,打开,关闭和删除)。除此之外,设计人员可以随意添加功能。

与大多数操作系统不同,Amiga操作系统不使用软件中断或非法指令陷阱来提供操作系统服务。相反,在ROM内核中有一个名为exec.library的主库。此处定义了所有基本系统功能-列表原语,内存管理,加载和打开库的功能(从库中调用内部拥有的内部初始化和打开例程)。操作系统唯一不变的部分是绝对内存位置4,它指向Exec库结构(ExecBase)。 ExecBase的数据部分包含基本的Exec结构,包括系统消息端口,库,设备和任务的列表定义。

将Exec库与OS / 2和Microsoft Windows使用的动态链接库进行比较很有趣。 DLL支持功能集,但它们也提供其他服务。英特尔286及后续芯片支持不同级别(环)的安全性概念。如果您不具备必需的最低安全级别,则请求将失败。 DLL函数分派可能导致安全级别切换。相当于Motorola 68000系列的模块调用功能。但是,它至少需要68020微处理器单元,最好是页面存储器管理单元。 AmigaDOS可在所有68000上运行,因此固有的唯一安全级别是由于AmigaDOS程序以默认用户状态运行,而操作系统根据需要以超级用户状态运行。

两种方法都各有利弊。由于Exec库本质上是简单的向量表,因此调用库函数的开销仅比驻留在调用程序中的函数(而不是软件中断)多,而不是驻留在共享系统代码中,这仅比后者高。另一方面,精心设计的DLL在造成较小的速度损失的同时,更不会受到已经运行的程序的破坏。

请注意,DLL是Microsoft操作系统的扩展。基本系统功能仍由软件中断驱动。因此,两种库接口都必须具有逻辑。但是,Amiga库不仅提供单个接口,而且不受所有软件中断固有的问题的影响-仅有有限数量的中断,对于实际目的而言似乎还远远不够。另一方面,图书馆不仅是“无限”的。可扩展,但是创建与内置文件没有区别的新文件或通过以更高优先级在系统库上插入同名新库甚至完全覆盖内置文件是一项简单的任务列表。

图书馆的概念本身得到了扩展。通过添加一些标准功能,它构成了I / O设备的基础。大多数设备通过称为IORequests的扩展消息工作。这些扩展也有扩展(例如StdIORequest),以及针对特定设备的自定义扩展。设备通常还具有一个或多个任务,因此I / O可以异步完成,尽管这不是强制性的。

该库还有另一个鲜为人知的扩展,称为资源。资源本质上充当共享资源(通常是硬件)(例如磁盘控制器上的不同驱动器)或串行和并行I / O端口(whicb在同一芯片上实现)的协调器。

任务任务结构是另一个节点。这其中包含使Exec成为功能齐全,可抢占,优先级驱动,多任务操作系统的所有定义。任务大致等效于OS / 2线程。扩展的任务(称为进程)提供了附加信息,以允许使用名为dos.library的库中定义的AmigaDOS函数-主要是诸如类Unix的I / O服务,程序加载功能等。

Exec的任务调度程序不如OS / 2复杂,据传OS / 2是从IBM的VM / 370大型机操作系统上物理提升的。 OS / 2调度程序根据“魔术”中的某些算法动态调整任务优先级。操作系统的一部分。虽然令人印象深刻,但值得怀疑

......