AFS文件目录数据库系统初步规划
文章目录
- AFS文件目录数据库系统初步规划
- 前言
- 一、AFS是什么?
- 二、游戏关卡
- 三、文件名称及标识
- 四、与linux对比文件系统
- 五、linux文件系统简介(网上择抄)
- 总结
前言
linux文件系统 网络代码约100万行。要达到相同的效果,甚至更高的标准,但目标代码要求小于1000行;如何简化和实现将是一个有趣的挑战。Linux共有27852148行分布在66492个文件中,我不能去看linux源代码,没有时间和精力;一年后,我将逐渐回归学唱歌。今年,三款产品(飞鸡1-3)的软硬件同步学习。AOS包括简易微内核操作系统:AFS文件目录数据库系统,ANET基本设备驱动网络系统、内核(过程(线程)调度、内存管理、通信管理等)。AOS目标代码数量小于3000行,预计半年后进入调试阶段;更多的代码编写,如设备驱动、其他文件系统支持、用户应用层软件等,将来自互联网或其他公司或个人的支持。我只是给框架支持,C标准io库实现。我通常需要半个月的时间突破算法过程等游戏困难。我会尽可能多地从原则上实现和简化代码量。如果代码超过3000行,我会感到无能为力惫。预计开发时间将超过一年。假如24小时战斗K代码量、半年可以完成(单纯编写是不会花多少时间,而时间主要花在实现方法的思考上)。AOS主要目标是简化代码量、高可靠性、高速度和微内核。
一、AFS是什么?
AOS实现VFS(Virtual Filesystem)不同的方式linux。如果某个ext3文件系统再次挂靠AFS的某个目录AAA下面,那么目录AAA就是该ext文件系统的根目录文件包括与设备驱动相关的文件ext所有文件系统的操作方法和所谓的超级块等,AFS会对ext文件系统制作元数据(Metadata)相关外包装。是故,AFS卷根目录文件自然是/(0号i节点,隐藏,包含AOS等);AFS其他文件系统也可以参考如何在/卷根目录文件中实现(预计不到500行代码),无非是编写2组32种方法(函数)。所以,AOS核心不多Linux超级块对象(super_block)和VFS复杂实现。打开一个文件,必定是其对应文件系统根目录文件也被打开,因为哪里才会有对其统属文件操控的方法。还有一种实现VFS用户流程或内核线程模式,AFS实现通信。AFS最多支持32个不同的文件系统(插件和安装模块)AOS内),每个根目录最多有1K但普通目录文件不限于目录项(取决于i节点资源)。默认情况下,一个过程打开32个文件号,用户也可以定义为64K个;但socket连接号是另一种实现,最大可支持16M个连接号(ANET实现代码小于500行,用户流程的网络操作更简单,实现各种连接、自动读写文件等ANET所有这些都包含在用户发送。OK、不用学网络编程等东东,哈哈,累得狗喘气的是我)。简洁、速度和高性能贯穿整个过程AOS。简洁、速度和高性能贯穿整个过程AOS。设备驱动也是附加在一起的AFS并成为设备根目录的目录。希望未来的计算机硬件:内存大于256Gb,固态盘大于256Tb,一块磁盘有16Zb。当计算机达到人脑水平时,需要经历36层;即使世界上所有的计算机加起来都达不到18层的一半,离我想象的还有很长的路要走。网络被视为由孤岛矿山(网站)组成的海洋,各种网站也是粗矿石;计算机需要自动采矿,建立字典、联想数据库,然后学习、推理、思考、预测等,努力在5年内达到幼儿园水平。
二、游戏关卡
考虑到未来智能计算机的需要,AFS由文件目录系统管理的磁盘空间全球磁盘阵列数据区(16个全球磁盘)1 GDRPT = 64Krl,一个卷rl (roll)= 64K bdb,大数据块bdb(big data block)= 64Kdb,一个数据块db( data block ) = 64 Kp,1p(页page) = 16 s(扇区sector) = 256 r(行row)= 2K w(字word)= 4Kz(半字z)= 8K b(字节byte)= 64K bit(位bit)。是故,1db = 512Mb,1bdb = 32Tb,1rl = 2Eb,1 GDRPT = 128 Zb。是故,1db = 512Mb,1bdb = 32Tb,1rl = 2Eb,1 GDRPT = 128 Zb。最大支持文件2Eb,每卷最大文件数为4G支持最大分区2Eb(一个分区,一个卷,一个全球磁盘数据区 GDPT可有4K分为32段,每段128段)。游戏关卡主要有三个: 1)、inode数字映射和分配,SQL查询搜索数据库等。 二、文件权限管理。 三、空间分配算法。 花了半个月的时间,我终于想出了独狼空间连续粒度分配算法 linux采用的“Buddy(伙伴)算法”;适用于磁盘空间的以卷、或以大数据块、或以数据块、或以页为单位粒度的连续空间分配,也适用于连续内存空间或FLASH连续空间分配(也可以是行为单位粒度(32字节一行)等。);有效减少碎片化,算法代码简单易实现。整体空间分配算法为四方兽空间分配算法,青龙粒,白虎骨,朱雀来,玄武出。整体空间分配算法为四方兽空间分配算法、青龙粒、白虎骨、朱雀来、玄武出,包括单粒位图分配算法和独狼分配算法。 支持数据库表模式(或使用数据库显示层或浏览器作为用户相互操作数据库表)。内存空间和磁盘空间预配置,参数初始化(如设备驱动树初始化配置 等等,未来代码也会放入数据表,呵呵)。 根据用户的权限不能是无限的,必须考虑保护用户的隐私!文件的权限设置必须是文件的所有者!除非文件所有者在i_mode中、允许root用户有权读写文件!但是根用户可以删除所有非根用户的文件。
1 Kb ( Kilobyte 千字节 ) =1024b,1024 = 2^10 ( 2的10次方); 1 Mb ( Megabyte 兆字节,简称兆 = 1024 Kb, 1Gb ( Gigabyte 吉字节,又称千兆) = 1024 Mb, 1 Tb ( Trillionbyte 万亿字节,太字节 ) = 1024 Gb, 1 Pb(Petabyte 千亿字节,拍字节)= 1024 Tb, 1Eb(Exabyte 百亿字节,艾字节)= 1024 Pb; 1 Zb ( Zettabyte 十万亿字节, 泽字节 ) = 1024 Eb, 1 Yb (Yottabyte 1亿亿字节, 尧字节 ) = 1024 Zb, 1 Bb ( Brontobyte 一千亿字节 ) = 1024 Yb。
各种文件目录系统让我很累,太烦人了,只好一点一点的沟通简化。
三、文件名称及标识
程序(程序在磁盘中,程序在内存中打开),目录也是文件。文件是复杂数据容器集合的组织形式,通常由描述文件属性集的文件头结构和文件体元素数组成,文件也可以只有文件头。文件头 它由一些属性字段元素组成,通常包括:文件名称、标识符、类型、文件权限、标识、大小、位置、方向(节点)、时间戳等。字段元素可以是 C由一个或多个字段组成的基本类型或结构体、数组等的集合称为记录,因此文件头也可视为文件的属性记录。如果文件体元素数组的元素很简单 C的基本类型变量被视为字符流文件或二进制文件,也称为流式文件。相反,如果是记录类型的数组,则称为记录文件或表文件。记录文件或表文件的文件体可称为表,表中每个行为的记录也可分为字段和列;这样,每个表都由行和列组成。事实上,流式文件也可以看作是孤立字段表文件,只有一个简单的字段列。记录表文件可视为多个相关字段表文件的合成体。 因此,文件体可视为表,而文件与表文件等价,只看用户的视角。数据库DB(data base)是存储数据表(data list/sheet)集成仓库可以简单地看作是一个数据库目录,包括一堆数据表文件和相关文件。这样,文件目录系统就可以被视为数据库,反之亦然;设备驱动库,甚至系统核心(内存管理、过程和线程管理等)。也可以被视为数据库;这样,总编程代码将大大减少。
1)编码文件名称 AFS中文件名字的字符串编码为2字节Unicode编码变形称为UTFA16。如果用UTF-8编码(Unicode Transformation Format ): 7位ASCII–>0bbbbbbb,8-11位–>110bbbbb 10bbbbbb, 12-16位–>1110bbbb 10bbbbbb 10bbbbbb,中文需要3字节。 UTFA16: 如果两个字节的最高位是0,则分为两个ASCII如果两个字节的最高位之一是1,可以有15.5位(xy:01、10、11):-xbbbbbbb ybbbbbbb,可表示48k将中文等符号形象为48k个Unicode字符编码:xy=01时,14位b 0X0080,映射到0X0080—0X407F;x=1时,15位b 0X4080,映射到0X4080—0XABFF,还余0X分别映射1400个,D800–DFFF(0X0800个),F400–FFFF(0X0C00个)。 这种算法简单高速,优点是英语等ASCII代码符号只占一个字节。
2)文件属性记录FAR(File Attribute record。包含部分元数据(不包括文件名属性记录)的文件头,28b) 文件系统inode译成中文就是文件索引节点(information node 或index node)、也称i节点,在内存中打开的inode称为v_inode(v节点),其它文件系统的如ext2_inode等等。索引节点对象inode包含文件的相关信息(诸如文件的大小、磁盘位置、文件权限、时间戳、文件类型和标志、相关标识符、等等),其实质就是文件属性记录FAR(也就是对文件属性的描述,也称为文件索引节点对象inode object)。
AFS:文件 = 元数据(文件属性数据) + 数据(文件具体内容), 元数据 = 文件属性记录FAR(索引节点对象inode)+ 文件名字属性记录NAR(Name Attribute record), NAR = GUID(16b文件标识) + __u8 afs_name_len(1b文件名字长度) + __u8 afs_file_type(1b文件类型) + pinode(1w所属目录的i节点)。
将磁盘中的inode调进填充入内存中的v_inode,这样才是算打开了磁盘文件的inode。linux的内存中的inode(VFS)和具体文件系统的inode(如ext2_inode)有着很大的区别,而AFS的差别不大。每个inode节点的大小,AFS是128字节,inode节点的总数是4G个,一般AFS是每2Mb就设置一个inode。AFS有2个线性表和一个哈希表:文件属性记录FAR表(i节点对象表)、文件小数据区表(每项4Kb、半页),文件名字属性记录NAR哈希表。相关线性表是根据inode排序,或说是以inode为下标的记录数组。
四、与linux文件系统对比
通常最大打开文件数会是系统内存的10%,如果系统同时打开16M个文件,那需要16M个v节点、2Gb内存;linux的目录项对象(dentry)也是要占内存约2Gb(dentry包含有文件名字、就算平均占128b吧),是故、单是元数据就要占内存约4Gb;如果同时打开的是512M个文件时,岂非元数据就要占内存约128Gb?其实,内存占多大并非重点,对我而言、重点是代码清晰和简洁!内核操纵的是v节点,不应将包含文件名字的dentry(注意:文件名字长度是不等长的,最大可到256个符号)弄进内存中。如果说引入存放在内存中linux的目录项对象(dentry)概念主要是出于方便查找文件的目的,那么、耗费我不少想象力后,我看到的只是:复杂无比,代码量巨增,不够清晰;除了增加复杂度和增加内存使用量,我没发现有特别之处。查找方便?如果一个目录下面的文件很多时,还不是要用哈希查找?还不是要磁盘I/O?如果就为在如open一个文件时增加那么一点点速度,而增加复杂度、增加dentry cache保存最近访问过的目录项(如果要找的目录项在cache中没有,就要从磁盘读到内存中),增加目录文件数据块的目录项哈希索引排放、等等;那么,还不如放弃目录项对象dentry概念。
所以,AFS取消了内存中的Linux目录项对象(dentry)概念,且AFS的目录文件之目录项dir_entry只是1个字的文件索引节点描述符inode;也不需要生成额外的两个目录项:".“和”…",因为inode节点对象已经包含相关描述,也不需要增加代码量去做目录文件数据块的目录项dir_entry的索引排布。如果为了相对数量极少的一些硬件符号链接文件、节省下一些inode号,而使得inode号没能与路径文件名、一一对应;我认为,终是得不偿失。linux硬链接只能对文件、不能对目录,而又不得不划分为软硬链接、增加了实现复杂和代码量。是故,AFS没有软、硬链接之分,只有简单的可以跨文件系统的目录或文件链接,链接文件inode对象含有到源inode号的指针(指向自己、则为源文件或目录)和链接计数项。链接文件不能再对其链接,这没必要。文件inode号一一对应于路径+文件名,任一条路径是唯一的、任一条路径 + 文件名也是唯一的。同一个路径下不能有相同名字的文件,这是规则。AFS最大4G个inode号,非根目录下文件数不限制,只受限于inode号资源;允许同名目录,不同路径下也允许同名文件以及文件和目录同名,例如:路径1/abc.txt、路径2/abc.txt、路径i/abc.txt, /…abc/abc/abc/abc/abc。类似按通配符与部分文件名查询、或部分元数据属性查询时等等情形,使用B+树索引表和哈希表;但这是属于shell命令程序,不包含在AOS中。AOS支持数据库基本操作(数据库方法集)功能,AFS文件目录系统是数据库例子,支持SQL查询操作。AOS支持用户C标准库。
内存中打开的表有:PCB(process control block)进程表(进程描述符PID,Linux操作系统下的PCB是:task_struct,会被大改动),fd文件描述符表(file descriptor table),打开文件表(open file table),v_inode节点表(d_inode(目录i节点)、rd_inode(根目录i节点)、socket_inode(网络i节点)、f_inode(普通文件i节点)、p_inode(程序文件i节点)、等等在内存中打开之inode节点称为v节点)。内核中,对应于每个进程都有一个文件描述符表fdt,表示这个进程打开的所有文件。文件描述表fdt中每一项都是一个指针,指向一个用于描述打开的文件的数据块–file对象(打开文件表oft中的项),file对象中描述了文件的打开模式,读写位置等重要信息,当进程打开一个文件时,内核就会创建一个新的 file对象。file对象有引用计数,记录了引用这个对象的文件描述符个数,只有当引用计数为0时,内核才销毁file对象,因此某个进程关闭文件,不影响与之共享同 一个file对象的进程。file对象中包含一个指针,指向相应v_inode节点对象。v_inode节点对象包含了如文件系统类型、文件的权限、访问日期等文件属性,还包含到其父节点(所属路径)的指针(包括i节点和v节点指针)、从而回溯出整个路径,找出根目录v节点、自然就会得到最终对文件进行操作所需的所有信息、文件的操作方法、等等。打开文件后,进程得到的文件描述符实质上就是文件描述符表的下标,内核根据这个下标值去访问相应的文件对象,从而实现对文件的操作。注意,同一个进程多次打开同一个文件时,内核会创建多个file对象。PIDT–>fdt–>oft–>vnt。内核各种表的相互关系如何去表述?我就简单地放1个或2个指针。当我学习到“链表”知识时,对C语言表述的“双向链表”感冒;你可以用“圆环形带一圈夹子的衣架”来解释如何便于插入和删除等使用、或说linux系统大量使用简单的双向链表结构类(统一的、简洁的一堆C操作方法)来表示、等等,我就是“莫名的不喜欢”、那就是“感觉”,我感觉反而它是linux系统代码量庞大的原因之一(呵呵,与大众认知的双向链表结构类简化代码量相反)。也或许是我这小白错了,反正2张表关联就会有相互之指针,实现动态增长数据单元、就要一个数据单元有一个指针指向下一个数据单元,管它单向或双向的数据链表是否规范、只要清晰和代码量简单就ok。
五、linux文件系统简介(网上择抄)
1)、linux文件系统inode号、目录和链接
在Linux内部,访问文件都是通过inode号来进行的,所谓文件名仅仅是给用户容易使用的别称或者绰号。当我们打开一个文件的时候,首先,系统找到这个文件名对应的inode号;然后,通过inode号,得到inode信息,最后由inode找到文件数据所在的block,再进行文件数据块的相应处理。Linux系统允许,多个文件名指向同一个inode号码;这意味着,可以用不同的文件名访问相同inode号对应的实际文件内容。inode对象中有一项叫做"硬链接计数",记录指向该inode的文件名总数;删除一个文件名,就会使得inode节点中的"硬链接数"减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。创建目录时,默认会生成两个目录项:".“和”…"。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接(hard link)";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。除了硬链接以外,还有一种特殊情况。文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的"软链接"(soft link)或者"符号链接(symbolic link)”。这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:“No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名(含路径名),而不是文件B的inode号码,文件B的inode"链接数"不会因此发生变化。Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。目录文件的结构很简单,就是一系列目录项(Directory entry、简为dir_entry)的列表。每个目录项dir_entry(与内存中目录项对象dentry不一样),由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。硬链接:其实就是同一个文件具有多个别名,具有相同inode从而有相同的data block,但目录项dir_entry不同;只能对已存在的文件进行创建硬链接,不能对目录进行创建;可以不同交叉文件系统进行硬链接的创建,删除一个硬链接并不影响其他具有相同inode号的文件。软链接:软链接文件具有自己的inode(文件属性及权限等)和文件内容数据块(文件存放的内容是另一个文件的路径名),软链接可以对不存在的文件或目录创建;软链接可以交叉文件系统,可以对文件或目录创建;创建软链接时,硬链接计数i_nlink不会增加;删除软链接不会影响被指向的文件,但若指向的原文件被删除,则成死链接,但重新创建指向的路径即可恢复为正常的软链接,只是源文件的内容可能变了。 例子:普通文件的数据块里面保存的是文件数据,而目录文件的数据块里面保存的是目录里面一项一项的文件信息,这些信息称为ext4_dir_entry。
struct ext4_dir_entry { __le32 inode; /* Inode number / __le16 rec_len; / Directory entry length / __le16 name_len; / Name length / char name[EXT4_NAME_LEN]; / File name / }; struct ext4_dir_entry_2 { // 后来的改版 __le32 inode; / Inode number / __le16 rec_len; / Directory entry length / __u8 name_len; / Name length / __u8 file_type; char name[EXT4_NAME_LEN]; / File name */ };
简单的目录文件表述格式可以是:以ext4_dir_entry_2为项的线性表,第一项是“.”表示当前目录,第二项是“…”表示上一级目录,接下来就是一项一项的文件名和inode。通过查询比对相应的inode就能找到真正的文件。如果一个目录下面的文件太多时(比如10亿个),想在这个目录下找一个文件,按照列表一个个去找太慢了,于是就添加了索引的模式。如果在inode中设置EXT4_INDEX_FL标志,则目录文件的块的组织形式将发生变化,变成了下面定义的这个样子:
struct dx_root { struct fake_dirent dot; char dot_name[4]; struct fake_dirent dotdot; char dotdot_name[4]; struct dx_root_info { __le32 reserved_zero; u8 hash_version; u8 info_length; /* 8 */ u8 indirect_levels; u8 unused_flags; } info; struct dx_entry entries[0]; };
当然,首先出现的还是差不多的,第一项是“.”表示当前目录;第二项是“…”表示上一级目录,接下来就开始发生改变了,是一个dx_root_info的结构,其中最重要的成员变量是indirect_levels,表示间接索引的层数。接下来看索引项dx_entry,这个其实就是文件名的哈希值和数据块的一个映射关系,如下所示:
struct dx_entry { __le32 hash; __le32 block; };
如果要查找一个目录下面的文件名,可以通过名称取哈希。如果哈希能够匹配上,就说明这个文件的信息在相应的块里面。然后打开这个块,如果里面不再是索引,而是索引树的叶子节点的话,那里面还是ext4_dir_entry_2的列表,只是少量的一项一项找文件名就行。通过索引树可以将一个目录下面的N多文件分散到很多的块里面,可以很快地进行查找。
2)、Linux目录项对象(dentry)
它代表一个目录项(包括该目录对象对应的索引节点,子目录链表,父目录目录项对象,与它同级的目录的目录项对象链表,使用计数,缓存标志),是路径的一个组成部分(注:路径中的每个组成部分都由一个索引节点对象表示)。该对象只存放在内存中。引入目录项的概念主要是出于方便查找文件的目的。一个路径的各个组成部分,不管是目录还是普通的文件,都是一个目录项对象。如,在路径/home/source/test.c中,目录 /, home, source和文件 test.c都对应一个目录项对象。不同于前面的三个对象,目录项对象没有对应的磁盘数据结构,VFS在遍历路径名的过程中现场将它们逐个地解析成目录项对象,并使用缓存机制,以提高查找的速度(目录项对象与索引节点对象一一对应,即它也代表着一个文件,这里的文件可以是普通文件也可以是目录文件等)。目录项对象有三种状态:被使用、未被使用和负状态。 被使用:一个被使用的目录项对应一个有效的索引节点(d_inode指向相应的索引节点)并且表明该对象存在一个或多个使用者(d_count为正)。一个目录项处于被使用状态,意味着它正被VFS使用并且指向有效的索引节点,因此不能被释放。 未被使用:一个未被使用的目录项对应于一个有效的索引节点(d_inode指向相应的索引节点),但是应指明VFS当前并未使用它(d_count为0)。该目录项对象仍指向一个有效对象,而且被保留在缓存中以便需要时再使用它。由于该目录项不会过早地被销毁,所以在以后再需要用到它时,不必重新创建,从而使得路径查找更迅速。但如果要回收内存的话,可以销毁未使用的目录项。 负状态:没有对应的有效索引节点。因为索引节点已被删除了或路径不正确,但是目录项仍然保留,以便快速解析以后的路径查询。
以上三种类型的目录项都会被缓存到目录项缓存中,并且散列表也会被缓存。另外如果目录项被缓存了,并且是被使用状态,那么相应的索引节点也会被缓存。目录项是描述文件的逻辑属性,只存在于内存中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。例如:open一个文件/home/xxx/yyy.txt,那么/、 home、xxx、yyy.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。注意:目录也是一种文件(所以也存在对应的inode)。打开目录,实际上就是打开目录文件。
struct dentry { atomic_t d_count; // 目录项对象使用计数器 unsigned int d_flags; // 目录项标志 struct inode * d_inode; // 与文件名关联的索引节点 struct dentry * d_parent; // 父目录的目录项对象 struct list_head d_hash; // 散列表(哈希表)表项的指针 struct list_head d_lru; // 最近未使用的目录项的链表 struct list_head d_child; // 目录项通过这个加入到父目录的d_subdirs中 struct list_head d_subdirs;// 对目录而言,表示子目录目录项对象的链表 struct list_head d_alias; // 相关索引节点(别名)的链表 int d_mounted; // 表示被安装文件系统根项。一个目录下可有不同的文件系统! struct qstr d_name; // 目录项名称 unsigned long d_time; /* used by d_revalidate 重新变为有效的时间!注意只要操作成功这个dentry就是有效的,否则无效。*/ struct dentry_operations *d_op; // 目录项方法 struct super_block * d_sb; // 文件超级块对象,这目录项所属的文件系统的超级块 vunsigned long d_vfs_flags;// 一些标志 void * d_fsdata;// 与文件系统相关的私有数据 unsigned char d_iname [DNAME_INLINE_LEN]; // 存放短文件名 };
一个有效的dentry结构必定有一个inode结构,这是因为一个目录项要么代表着一个文件,要么代表着一个目录,而目录实际上也是文件。所以,只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。inode却可以对应多个dentry,整个结构其实就是一棵树。
总结
linux的知识网上一大堆,呵呵、我也不太熟悉。我只能按自己所想当然去用汇编做AOS,感觉也不是一件非常复杂的事情,难度系数也就3.5星。
数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。哈希则是一种寻址容易,插入删除也容易的数据结构。从速度性能看,数组和哈希是最好的,加入位图将解决数组“插入和删除困难”的缺点、性能会比链表更好;是故,AOS以位图+数组、哈希为主,当然、单向链表和表的指针互指情形也是有的。操作对象的最基本方法之中的2种是:增加或删除一个对象。从另一个角度看,增加、删除可以看作是对资源对象的分配、释放。我们可以用一个位图变量中的一个二进制位来表示一个资源对象,增加或删除一个对象不外就是位图相应位置1或清0。资源对象可能是一个内存行、或一个内存数据块、或数据表中一条记录、或一个文件、或一个进程、或一个进程中的一个线程、或一页、或一个大数据块、或一个i节点、或进程中的一个打开的文件号、或线性表中的一个字段、或进程中的一个对象号、或一个消息号、或一个过程号、或一个方法集合等等。AOS中的一页 = 8Kb(字节) = 16s(扇区)= 64Kbit(位),AOS使用一页的位图(64Kbit、位序号用16位变量表示)来表示有64K个方法组;位图的一位对应一组方法跳转字(有16个方法跳转字w,方法组字段),总的方法组跳转表需要64K*16w = 1Mw = 4Mb的空间,方法组跳转表的字初始化为跳转到方法(函数)printf“没有安装”。位图最初的256位是AOS使用,其实、AOS也就用20多组方法,最多300多种方法代码吧(256个中断入口表不外是16个连续方法组,平均一个方法也就约10条指令)。64K - 256个方法组(可视为数据库表或说动态方法库),可以满足千奇百怪的各种设备驱动所需了(一个设备用1组方法),还有各种文件系统(连续2组方法)、应用软件的动态方法库以及玩家的各种独特设备驱动。我想象:所谓“模块”的注册、安装、卸除等等,不外是方法组位图相应位的置1或清0、以及相应方法组字段安装或初始化,另有一些、比如模块说明书、导出符号表之类的少量工作。每个方法的代码段是一个字数组、长度不一,需要使用到的方法组才会安装到AOS中、相应具体方法代码段的内存首地址也才会安装到方法组跳转表相应位置。用汇编写也就位图搜索第一位为1的指令或根据位图序号操作相应位、以及表跳转指令,C语言就那个case语句吧。想象力表明,操作系统只能是用汇编、C语言来编写;不可能蠢得用C++来写,难道每个不同对象类都来一组增加或删除方法?呵呵、那样代码量会无比臃肿。
AFS的文件名字哈希表hash table:文件名字–>硬件哈希4字MD5–>二次哈希到24位线性表相对指针(二维单向链表首指针)–>二维单向链表(横向为不同文件名字哈希冲突单向链表相对指针(首字相对指针next、为0结束),纵向为相同文件名字不同路径单向链表相对指针(尾字相对指针next、为0结束))。对路径文件、/home/source/test.c,先哈希test.c和路径/home/source,就可以得知/home/source/test.c的inode号;之后,再判断其是否在内存打开、等等。