AOS与阿里的名字发生冲突,现在改为OACS;汇编对象语言OASM(Object assembly language),又带一点点C风格,改名为OACS、即是带一点C风格的汇编对象语言系统(Object assembly language C system)。相比之下,一切都是对象unix/linux一切都是文件,会显得更广;许多核心对象,如内存对象,CPU对象、IPC没有文件i节点的对象、线程对象等。c语言功能也很强大,但我认为使用好并不容易;在此期间,我阅读了许多嵌入式操作系统和相关源代码;给我的印象是,英语单词满天飞,对像我这样的小白不友好。许多缩写E文本没有给出E文本源单词,也很难查阅翻译软件;总是有一万多个函数,没有那么多时间仔细阅读。stm32f7xx_hal_rcc.c stm32f7xx_hal_rcc_ex.h stm32f7xx_hal_rcc.h三份文件近6000行,只是复位和时钟控制RCC一个小单片机30多个功能部件之一,必须大大简化!我相信用汇编语言编写单片机程序的老手不会同意使用HAL库的;估计十几行汇编代码的东东,却啰嗦地写成千行,你能忍受吗?反正我宁愿推倒重来。很多年前,我也用汇编写了单片机程序;编译后相当复杂的项目只有4个KB你现在看:W601、w800,等等,平头哥CDK或MDK5上编译,功能不多,总是1MB、2MB代码量,上万函数。你可能会说,人们包含多线程复杂的操作系统,而汇编主要使用前后操作系统;但如果你真的开发了一台单片机,你自然会明白操作系统的核心并不多,即使是多过程、多线程和复杂的内存管理(仅1k内存或更小,到EB即使增加了虚拟内存管理,也要增加级以上的巨大动态内存管理)和文件管理。那为何linux、windows、等等,花费几十年、不断改进,却整出几千万行、上亿行的代码量?如果考虑的相关因素太多,代码量自然会大幅增加,而且没有很好地使用C语言,10多行代码只是编写成千上万行代码;万行代码;呵呵,有人会说我是个小白人,还没用过C语言编程,所以我在这里胡说八道;事实上,这与语言编程与是否面向对象无关,linux即使是面向对象编程,本质上也是面向对象编程;代码量也与你的编程思想和方法有关。编程理念是最重要的。有必要减少代码量和面向对象的编程;此外,我们需要知道如何选择。如果我们认为小白更容易理解和阅读代码,并将10多行代码添加到数千行代码中,就像HAL你认为选择应该是什么? c语言指针的概念太难听了。我得模拟软件,进去main即便如此,函数中的反复学习和测试也会让人头晕3天。OAC语言必定是取消“指针”概念的,如何更简洁的表达才是OAC语言重心! C语言表达方法的改进,OAC语言必须另写编译器。例如,C语言:
/* Reset PLLCFGR register 复位PLLCFGR寄存器 */ CLEAR_REG(RCC->PLLCFGR); SET_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM_4 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_7 |RCC_PLLCFGR_PLLQ_2 | ((uint32_t)0x20000000U));
1、(uint32_t)0x20000000U表示0x20000000是无符号(U)的32位数值,(uint32_t)强制为32位无符号。uint32是 某些编译器定义的别名,以方便使用,uint32_t是C/C 标准中定义的类型。OAC语言:(u32)0x20000000即可。 2、OAC语言没有指针,类属性对象cao类似C语言的结构;RCC其操作成员的变量是结构指针PLLCFGR只能以这种形式写:RCC->PLLCFGR。在OAC将语言转化为对象(结构),即表示为RCC.PLLCFGR;而函数CLEAR_REG(RCC->PLLCFGR),在OAC语言只是一个简单的汇编指令:RCC.PLLCFGR = 0; 是故,OAC语言有对象的声明和定义,但没有结构和指针。OAC语言类属性对象cao本质上是C语言的结构。 3、查到:#define RCC ((RCC_TypeDef *) RCC_BASE) 就是说RCC是指向RCC_TypeDef结构指针,即将RCC_BASE强制指向基地址RCC_TypeDef结构,而 #define RCC_BASE (AHB1PERIPH_BASE 0x3800U),或说地址 RCC_BASE = (AHB1PERIPH_BASE 0x3800U)。又 #define AHB1PERIPH_BASE (PERIPH_BASE 0x00020000U) 或外设总线AHB1地址AHB1PERIPH_BASE = (PERIPH_BASE 0x00020000U),又 #define PERIPH_BASE 0x40000000U /*!< Base address of : AHB/ABP Peripherals AHB/ABP外设总线的基地址为0x40000000U */。 这样,RCC就是结构RCC_TypeDef的相对PERIPH_BASE偏移地址,为 0x40000000 0x00020000 0x3800 = 0x40023800 。如果将AHB/ABP外设总线被视为类属性对象cao,RCC只不过是其中一个成员对象;如果AHB/ABP外设总线视为根属性对象,AHB1总线被视为根属性对象下的子节点属性对象,RCC则是AHB1总线节点属性对象下的成员对象。 4、查结构和RCC的PLLCFGR定义相关位置
typedef struct { __IO uint32_t CR; /*!< RCC clock control register 时钟控制寄存器,相对地址偏移Address offset: 0x00 */ __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register 锁相环PLL配置寄存器,Address offset: 0x04 */ __IO uint32_t CFGR; /*!< RCC clock configuration register时钟配置寄存器,Address offset: 0x08 */ __IO uint32_t CIR; /*!< RCC clock interrupt register,时钟中断寄存器,Address offset: 0x0C */ __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ uint32_t RESERVED0; /*!< Reserved, 0x1C */ __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ uint32_t RESERVED2; /*!< Reserved, 0x3C */ __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ __I uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */
__IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */
__IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */
uint32_t RESERVED4; /*!< Reserved, 0x5C */
__IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */
__IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */
uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */
__IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */
__IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */
uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */
__IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */
__IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */
__IO uint32_t PLLSAICFGR; /*!< RCC PLLSAI configuration register, Address offset: 0x88 */
__IO uint32_t DCKCFGR1; /*!< RCC Dedicated Clocks configuration register1, Address offset: 0x8C */
__IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x90 */
} RCC_TypeDef;
/******************** Bit definition for RCC_PLLCFGR register ***************/
#define RCC_PLLCFGR_PLLM_4 0x00000010U
#define RCC_PLLCFGR_PLLN_6 0x00001000U
#define RCC_PLLCFGR_PLLN_7 0x00002000U
#define RCC_PLLCFGR_PLLQ_2 0x04000000U
#define RCC_PLLCFGR_PLLR_1 0x20000000U
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
#define CLEAR_REG(REG) ((REG) = (0x0))
可以看出SET_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLM_4 | ...)实际上等效就是RCC->PLLCFGR = 0x24003010; 写那一长串,我感觉很难看(只是耍花腔而已),资料已经说明PLLCFGR寄存器的复位值就是0x24003010了。例子改为以下写法就简单明了,不外是对象RCC的成员寄存器赋值吧: RCC.PLLCFGR = 0; // PLLCFGR寄存器清0。 RCC.PLLCFGR = 0x24003010; // PLLCFGR寄存器恢复复位值。 5、OAC语言的类属性对象可看为u32或u64的数组,也不需要说什么对齐了。几个位段变量(1--n位、n<32 or 64),最好是可以拼接成一个u32或u64的大小,这样就不需要无效的保留位段。位或位段的操作方法(不外是汇编指令的语句吧),例如对PLLCFGR寄存器吧: RCC.PLLCFGR.bit25 = 0 or 1; // PLLCFGR寄存器的第25位置1或清0。 RCC.PLLCFGR.PLLQ = 4; // PLLCFGR寄存器的第27位--24位是定义位段PLLQ = PLLQ[3:0],主PLL 分频系数,适用于 USB OTG FS、SDIO 和随机数发生器时钟 (Main PLL division factor for USB OTG FS, SDIO and random number generator clocks)。 对位段声明,制作一个位段变量名称,再操作就好了。也可以对某一位、声明一个位变量名称,操作赋值该变量为1或0就好了。怎么简单怎么来!对象的成员变量是多个位段组合时,比如: u32 xxx.; 需要在变量名称后加“.”,然后在.h头文件另处的声明块u32 xxx. {...};内部对各个位段变量作声明。可以用公共的bitx的x位声明一个位变量,b[x1:x2]声明一个位段变量。OAC语言的编译器会对每个变量(或者常量、方法、对象、位段、数组、等等)的名称、属性、类型、长度、等保存到相应符号表中。OAC语言的类名称一定是唯一的,但属性对象名称或对象成员名称可以相同(不同类情形下)、会在“.”前加上类名称限定词;如果认为对象名称是唯一的(如内核对象),就可以不加,如上面的RCC对象。
一、类和对象
❏尽管我不喜欢E文,但ASCI符号才128个,还可以有压缩单词的简写,这点是满优越的;OAC语言的类名称最多8个ASCI符号,已经是够用,没必要“鸡鸭肠子”样子的长度吧、好难看的,对于成员变量名字可以是加“_”分隔,略为长点没关系,比如上面的RCC_PLLCFGR_PLLN_6。文件目录名字除了有8个ASCI字符的唯一短名称,还有可以在一个系统内同名的文件或目录的可变长度名称、最大可到256个UTF-16(Unicode字符16位编码)字符。一切皆对象、皆文件,那么看作为文件的类或对象的名称、自然是统一到“文件节点名字哈希映射表FNHT(file_name_hash_table)”中去。当我们编写代码时的对象成员变量(或者常量、方法、位段、数组、等等)名称也只能是包含在头文件(.h)或方法代码文件(.oac)或线程文件(.thr)的文件内容中(键(名称)/值(相对地址、类型、属性、长度、等)、对),而这名字最大只有28个ASCI字符;既然都编程序了,简单的E文自然是要懂得的(百度翻译软件辅助)。可以用缩写,又大小写区分,不同对象下可以同名;使用最大28个ASCI字符,来命名对象成员变量已是够用;多于28字符的会被自动截断,对于短名字,尾部会存在没有用到的ASCI字符字节、这里为0xFF(对于flash,增长名字时、容易),而没有使用/0作为字符串结束。
❏为何要面向对象编程?对我而言、最重要的是减少了E文单词量!不至于英文单词满天飞。一切皆对象、皆文件,那么不管什么类型的文件对象,其i节点成员通常就那十多个E文对象成员名称而已。每个类的方法组、对应同类的n个对象也只是一份,不同类的方法名称也大多数是相同的;如open();、close()、等等,加上xxx类.来区分而已。如果说有1亿个类,岂不是要1亿个函数、xxx类.open(); 是故,或许会统一为公共的open()方法,无非是init初始化、最终也就是memcopy()方法、不到10条汇编指令而已。是故,OAC语言会随时对方法数量作优化设计(由此对比,应该可以明白linux等的代码量巨大原因了)。当面向对象编程时,我们或许面对数以万计的各类对象;至于对象管理、并不一定要求极为高速,而是更多地需考虑代码的精简量和简明性;也就不得不引入双向和单向链表环概念,就会有“对象钩子、单向节点环、双向节点环”等说法。OACS通常规定一个节点环最大64K个子节点,当在链表节点环上漫游处理时,比如删除一个目录下的所有子节点(子目录)和叶子节点(文件);假设处理一个子节点的时间1us,若64K个子节点、则需要64ms,还可以接受;若是4G个子节点、则需要1个多小时,显然会不耐烦;是故,一个目录节点下的子节点数最多64k个。也有例外情形,比如内核的一个进程打开文件对象数量就可以设置到达4G个,但其处理情形和方式是不一样的。无论是用OAC语言写编译器、或编写OACS操作系统内核、或编写应用程序,都是简单的面向对象之“节点树”描述模式。抽象说就是“道生一、一生二(动分阴(空间的类对象静态数据)阳(时空的指令流、从而变动的数据流))、二生三(阴阳和合生三才)、三生万物”,遵从“简单到复杂”的自然规律过程;简单地说就是:先编写根类节点、再编写子节点、之后编写孙节点...,从“一到二、到三、到多”而已。即先定义顶层模块类功能,进而分析要构成顶层模块类的必要子模块类;然后进一步对各个模块类进行分解、设计,直到无法进一步分解的底层功能块类。既然一切皆对象、文件,其编写过程无非是相应“文件目录体系”的节点树半自动构建过程。道生一,动分阴阳,阴阳和合成三才,三才生四象、之后有:五行,六合,七音,八卦,九宫。也可以理解三才为“阴、阳、中”,三才是无处不在的。
❏类:类可以定义为描述对象行为/属性/状态的模板/蓝图。OAC语言将“类”划分为类属性和类方法2部分,将类比拟为“目录节点”;旗下的类属性对象cao(class attribute object)比拟为“.h”头文件,类方法对象cmo(class method object)比拟为“.oac”文件。这样划分,会使得编程更为简单明了;类目录节点下、通常还有一个扩展名为.thr的线程(thread)对象文件,根目录下就是包含main()方法的主线程文件了。.h文件反映了类属性对象cao的规划模板,还包含用这模板声明和定义的具体cao对象或cao对象数组;而cao对象的初始化元组是常量,在cmo中定义、也就是说其放置在.oac文件内容中。常量和类方法(函数)都是在.oac文件中编写,指令构成方法(函数)、指令段(伪指令、宏指令、内联函数);宏指令是汇编语言程序中的一种伪指令,是代表某功能的一段指令代码;在宏指令出现的地方,编译器总是自动的把他们替换成相应定义的指令代码块。宏指令与子程序(方法、函数)都可以用一个名字定义一段指令代码,以简化源程序的结构和设计。宏指令的参数传送简单,执行效率高;使用宏指令不会缩短目标程序,适用于子功能代码较短、传参较多的情况。子程序代码(如类的方法)在目标程序中只出现一次,调用子程序是执行同一程序段,因此,目标程序也得到相应的简化;模块化、方法类库、节省内存,可被多次调用、编程效率高;但有额外开销(压入将使用的寄存器组和返回地址,计算转向地址、传递参数,返回时还需弹出恢复等),增加了执行时间;适用于子功能代码较长,调用比较频繁的情况。
❏对象:现状的对象定义为具有状态和行为。但OAC语言定义为一切皆对象(Object)!而类(Class)的一个实例(Instance)包含一个具体的cao对象(属性数据)和一个具体的cmo对象(方法、指令码流数据),一阴(cao)一阳(cmo)、相对而言。
❏方法:从基本上说,一个方法表示一种行为,一个类可以包含多个方法。可在方法中写入逻辑、操作数据以及执行所有的动作。方法还有很多叫法,比如函数、子例程或子程序、等等,其本质就是一段指令;OAC语言一条机器指令码用半字u16表示、可以和对应的汇编指令语句相互转换(编译器功用),方法就是一个指令码数组,可以和汇编指令语句组或字符串数组相互转换。
❏标识符:OAC语言标识符(键)是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。OAC语言是区分大小写的编程语言,标识符内不允许出现标点字符,比如 @、& 和 %。
❏节点树(生长发育):节点描述文档从根节点(0层、root node)开始,以树状一层层节点展开,每个标签行是一个节点或叶子(属性)终端节点,一个文档最大有4G个节点;根节点外的每个节点都有一个父节点(parent_node ),节点可以有最多64k(下层)子节点(child_node)、第一个子节点(first_child_node)、最后一个子节点(last_child_node);同级节点(兄弟节点sibling_node)是拥有相同父节点的节点(注意:同层不一定是同级),前一个兄弟节点(prev_sibling_node)、下一个兄弟节点(next_sibling_node);叶子节点(简称叶子、leaf _node)是没有子节点或说节点描述块内的标签行都是叶子标签行、的节点(即末端节点、又称为终端节点),层数(layers_number,也称深度)最大256层(0层(根)---255层),通常是不到16层(文件目录体系)。
❏空间:磁盘空间,虚拟内存空间(通常每个进程有独占的2GB虚拟内存空间(堆空间),共享128TB的内核虚拟内存空间(包含内核虚拟空间、进程的线程堆栈虚拟空间)),物理内存空间,编译器空间,flash空间,等等;一个线程(内核或用户)的栈空间最大为一页4KB(还包含64B的cao线程对象在内),考虑到一些单片机的有限资源、线程对象及其栈(属于内核对象)的总大小也可以设为128B、256B、512B、1KB、2KB;局部变量通常只是CPU内部的有限寄存器u32或u64,需要局部大数据空间、如大数组等,只能是动态new或在cao对象中定义。将来64位体系、可考虑到每个进程有128TB虚拟线性内存空间。 PGD页表共512项、每项管理512GB,最大64k个用户进程的虚拟内存空间参考布局: 0--255项、 用户进程虚拟地址空间128TB,每个进程有2GB独立线性虚拟内存空间,每1项512GB包含256个用户进程。用户进程包括:code段、data数据段、BSS段、堆brk(动态对象),用户进程的线程堆栈页在内核共享区域(每进程64K*4K = 256MB,共4G*4KB = 16TB)。 编译器符号表:根类节点、类节点、子类节点、...,节点描述树排列。有三种符号表:cao对象节点描述树表(data数据段、BSS段),cmo对象节点描述树表(类方法集、对象常量集,code段后部),thr线程对象节点描述树表(线程方法集、线程常量集,code段前部(程序))。 用户进程(应用程序)文件:兼容ELF 文件格式规范。 用户进程2GB布局:虚拟地址PVMA对象区域,进程私有;虚拟地址静态或动态方法库区域,进程线程区域,进程私有。
256--511项、共享内核虚拟内存地址空间128TB参考布局:(间隔页在项内) 256--287项: 内核虚拟内存空间16TB与物理内存的直接映射(4G页),固定区域; 288--415项: 内核虚拟内存区域64TB,内核v节点打开文件表,固定区域; 416--447项: 内核虚拟内存用户进程线程栈VMA对象4G*4KB = 16TB(线程cao对象64B包含在线程堆栈页内),固定区域; 448--479项: 内核虚拟地址file_vma对象区域16TB(进程打开文件描述符对象32B、最大512G个,平均每进程8M个),固定区域,进程共享; 480--495项: 共享内核虚拟内存地址空间8TB(磁盘缓冲页空间、最大2G个页),进程共享; 496--503项: 共享内核虚拟内存地址空间4TB(磁盘双缓冲页空间、最大0.5G个),进程共享; 504--511项: 内核映射、静态动态公共方法库区域共4TB,固定区域。
物理内存空间管理、磁盘空间管理、虚拟内存空间管理、等等是大同小异,都属于空间管理类,自然是希望统一到简单的2种方法:分配和释放。这样,可以大幅度减少代码量;还应考虑到有些单片机的内存小于1KB的情形,OACS应该具有非常大的自动变化“弹性”,或说支持小于1KB的空间管理、和最大直到16ES级(4G*4G*512B = 8K EB)的空间管理。虚拟内存空间,下一步计划扩展到64位。
❏i节点(inode):文件索引节点(index node)、也称i节点inode,通常:在内存中打开的vfs_inode文件cao对象称为v节点vnode、在磁盘空间中的称为文件i节点inode。内核有一张v节点打开文件表(位图+链表环、管理)。一个内核文件v节点 = 32B的v节点管理对象vmo (V node management object)+ 224B的文件i节点(目录、链接文件、等无文件内容的,是96B),部分内核文件v节点是只有v节点管理对象vmo、而无磁盘空间i节点;v节点的大小、通常有:32B、64B、128B、256B、512B、1KB、2KB、4KB(页)、8KB(双页)。OACS的i节点数可到256T个(64K*4G、16位i节点号所属区域,32位区域的i节点号),磁盘文件种类繁多,通常划分为4类: 无文件内容的微型文件(如目录、链接文件、设备文件、等),i_fnna = 1; 文件内容小于等于116B的小型文件,i_fnna = 2; 文件内容小于等于112B + 4KB(带磁盘缓冲页)的中型文件,i_fnna = 3; 大型文件(4KB以上、不需要带磁盘缓冲页),i_fnna > 3;。 这4类是处于不同的i节点号所属区域。前三类文件、其内容或在i节点号所处区域或磁盘缓冲页空间,通过i节点号,就可以快速定位到文件的磁盘空间位置,一次I/O就可以拉入内存。与linux不同,内核v节点、其f_sflags(描述了file文件对象状态标志),f_pos(描述了文件的偏移位置),等等、是在进程打开文件描述符对象file中,而并非在v节点打开文件表中;用户进程cao对象有file_head双向节点环表头项,可将其打开的所有打开文件描述符对象钩挂在环上,最大可达到4G个、或说一个用户进程最大可打开4G个文件,取代了linux进程打开文件描述符表。进程open打开一个文件,返回当前可用的最小打开文件描述符对象号fd(u32)。 v节点管理对象vmo的v_fcount项会记录通过该文件表项(v节点打开文件表的表项)打开文件的次数,当v_count计数归0时这个文件表项才会被删除,因此,对于指向同一文件表项的两个不同文件描述符对象file(如、同一进程中dup的两个文件描述符fd指向同一个文件表项),即使其中一个文件描述符fd关闭了,只要仍然有文件描述符指向这个文件表项,那么就依然能通过这个文件表项访问文件,直到所有指向该文件表项的文件描述符都关闭了才不能再进行访问。 文件打开表fot的file的流容器对象,文件磁盘空间或设备空间到FVO流容器对象的窗口映射,大型文件。
u8w file. { // 进程打开文件描述符对象file,32B。功能类同linux的struct file、结构类同address_space对象。
u64 f_pos; // 关联文件的偏移量、单位字节。
u32 f_next; // file对象节点钩子,下一个对象号。钩挂到相应进程file_head头,最大可达到4G个。
u32 f_prev; // file对象节点钩子,上一个对象号。
u48 f_start; // file对象的fvo流容器对象(窗口)虚拟内存地址开始页号,大型文件才需要(相当于文件窗口缓冲区)。
u48 f_onn; // 相应打开文件表的文件v节点对象号。
u32 f_sflags.; // file对象状态标志。
};
性能超越linux和windows的OACS操作系统,支持最大64K个进程对象、每个进程支持最大64K个线程对象;我希望系统内核编译后不超过16KB代码量,也就是不超过3千行的实际OAC语言语句(宏指令语句、方法,包含有多条汇编指令);这对编程思想、OAC语言、OACS操作系统规划、OCC编译器,都有很高大上的要求。
二、文件目录体系(节点树),VFS(Virtual File System)虚拟文件目录系统实现
❏OACS操作系统,是一切皆对象;对象若在磁盘空间有表现、有i节点的,才视为磁盘文件;通常90%以上的对象都是文件对象,只是少数无“磁盘文件包装”的内核对象才不看作磁盘文件。微型文件只是有i节点、而通常是无文件内容,或说其在磁盘空间中不占用文件内容空间;如目录、链接文件、等等,设备文件的“寄存器空间描述”是硬件固定分配,在内核8W的v节点管理对象vmo中描述、没有在i节点表现,也看作微型磁盘文件。微型文件的i节点大小只有128B,为32B的vmo + 96B的i节点大小。有不少文件只有很少的文件内容,若文件内容小于等于116B的,称之为小型文件;其文件内容直接包含在256B的i节点内,微型文件和小型文件都可以直接用i节点寻址。还有许多文件内容小于112B + 4KB(带磁盘缓冲页)的中型文件,也是可以直接用i节点寻址的,只是需要2次磁盘I/O(i节点1次,磁盘缓冲页1次),实际上、根据路径名称哈希表FNHT查找i节点寻址时,还需要另外2次的磁盘I/O。大型文件通常也只是需要2次磁盘I/O,i节点中有文件内容磁盘空间开始地址,file对象中有文件偏移位置(f_pos)、窗口缓冲区。
❏OACS的cao对象通常都有2个钩子,一个钩子钩挂在对象节点树上、另一个钩子钩挂到应用双向节点环上(不一定使用);比如内核线程对象的一个钩子钩挂在主线程上、从而钩挂到内核进程文件目录体系上,另一个钩子或许钩挂在“就绪”管理对象上、也或许是钩挂在各种IPC管理对象上、也或许钩挂在“睡眠”管理对象上、等等。文件的i节点cao对象有2---3个钩子,一个钩子钩挂在文件目录树单向节点环上,一个钩子作为源i节点头环、钩挂对其进行链接的所有链接文件,另一个双向环钩子可以钩挂到其它应用上(如进程文件钩挂到相应的用户进程组)。当文件对象在系统内存中展开为v节点时,其vmo还有2个钩子,反映文件对象在内存中的映射(内存打开的文件目录体系),进程文件file_head头钩子则会钩挂其所有的打开文件描述符对象file,内核cao对象钩子、是对象号或直接物理地址。呵呵,一堆钩子、就像“挂猪肉”样子, 系统编程的代码量就会大幅度减少。
❏磁盘空间文件名字哈希映射表FNHT(file_name_hash_table): 任一条路径是唯一的、任一条路径 + 文件名也是唯一的,同一个父目录路径下不能有相同名字的文件,这是规则;不同父目录路径下允许同名文件、同名目录,以及文件和目录同名。OACS系统:不同父目录路径下的同名文件(或目录)只允许最大1k个,对一个“名字”而言。大系统理论上支持48位的i节点数(256T个i节点、约为282万亿个i节点(或文件)),如果将文件(或目录)名字哈希映射到32位的分配器对象项(2W),那每项还需最大16位的不同文件名字映射相同哈希值节点数(实际上,因存在同名文件(或目录)的最大1k个(10位)情形、是不到16位的)。映射表FNHT在大系统时,需要4G*8B = 32GB的FLASH空间(不需要机械磁盘IO),如果只分别映射到256个24位哈希空间、而每个24位哈希空间只是16M*2W = 128MB的FLASH空间;如linux等的中系统只是32位的i节点数、只有一个24位或16位哈希空间,不同文件名字映射相同哈希值节点数只需最大8位。总之、打开一个文件等等操作,只是想2次磁盘IO、就可定位到相应i节点,而最终将文件拖入内存通常也是需要2 + 2 = 4次磁盘IO(微型和小型文件则为3次磁盘IO)。
用“空间换时间”或者“用时间换空间”这是没办法的事情,当按文件名字查找其相应i节点时,不可能在一个巨大的文件名字线性空间16PB(假设平均一个文件名字32字符64B、256T*64B = 16PB)中、一个一个比对(最大需要256T次比对,及4G次的磁盘IO、每次装入内存2MB进行对比),那会慢如蜗牛。利用硬件哈希计算:文件名字-->硬件哈希4字MD5-->二次哈希到32位线性相对地址哈希映射表FNHT,这表放在无需磁盘IO的FLASH空间中、有4G项,每项为一个8B的“分配器对象”指示、其中32位为分配器对象磁盘空间开始地址、16位为映射到相同哈希值的不同文件名字节点数、16位为状态标志。如果说需考虑:相同哈希值的不同文件名字节点数最大有64k个,每个节点允许对应有最大1k个同名项(每个同名项为8W),则需磁盘空间:4G项*64K*32K = 8EB;这显然不合理,即使未来的个人电脑磁盘空间可达到EB级; 在特例情形下、可以适当增加IO次数,用时间换空间,但我们也是希望特例情形下一次查询、可以控制在2秒时间内。
分配器对象中的查找分为2次: a、查找映射到相同哈希值的不同文件名字节点中(最大有64k个)的对应节点,按文件名字长度和类型的轻型比较,符合后再进一步比对文件名字。一个节点对应一个双扇区,最大需要64K*1KB = 64MB的磁盘空间;一次拖入内存64MB再进行比较、自然只是一次IO,但我现在用的电脑、文件数估计也就几十万个,就算系统有1万亿个文件,这些节点数估计也在1k个之内;使用单向链表、将分配器对象划分为2MB块的单向链表,极端情形下最大32次IO;这样,需要的初始磁盘空间大小从:4G*64MB = 256PB降为4G*2MB = 8PB;如果将分配器对象拆分为256个“256分配器对象”单向链表,还可降低为4G*256KB = 1PB, 但极端情形下循着链表需最大256次IO,那将是有上百万亿个文件的情形。256分配器对象充斥在OACS系统的每个角落,为简洁代码量起着很重要的作用。在此,一个256分配器对象管理256个双扇区(相同哈希值不同文件名字节点,256KB),动态增减节点数;一次IO读入一个256分配器对象到内存,再轻型比较、文件名字比较。系统文件数量小于1万亿个时,通常就是一次磁盘IO。 b、当定位到相应的相同哈希值不同文件名字节点,下一步是找出不同路径下的同名文件项8W:文件所属父目录节点路径名字哈希值(硬件4字MD5、唯一性),48位的i节点号(其中高16位i节点区域),16位的v节点号类型,32位的v节点号,32位的父v节点号。文件名字节点双扇区32*8W,如果文件名字为256字符(8字对齐),则只有14个文件项,或许需要文件项扩充双页;有时文件名字只是不到16字符(8W),则还可以装入29个同名文件项8W;通常多个同名文件的情形并不多的,256字符的长文件名也不多见。已知文件名字及所属父目录节点路径名字时,查找其对应i节点和v节点,通常就是2步、2次磁盘IO(如果文件项在节点双扇区、只需一次磁盘IO);实际上也不一定限制同名文件项只是1K个,但最大不超过64K个。
一个256双扇区分配器对象为256KB = 256*1KB = 256*256W = 64KW,有256个相同哈希值的不同文件名字节点,最大可扩充为256个256双扇区分配器对象;0号256双扇区分配器对象的地址由哈希映射表FNHT中相应的项所指示;或说256双扇区分配器对象的0号双扇区,才会包含有下一个扩充256双扇区分配器对象节点钩子,之后才是相应的节点名字、长度、等等,以及同名文件项8W。256分配器对象ao256(256 allocator object),双页dp(double pages), 有2种形式的256分配器对象ao256: a、256双扇区分配器对象256KB,对应、映射到相同哈希值的最大256个不同文件名字节点(对应一个双扇区);如果不止256个节点时,0号节点的相同哈希值不同文件名字节点的ao_next成员指向下一个扩充256双扇区分配器对象、动态增加256个节点;用横向单向链表来动态扩充节点、ao_next为0结束。非0号节点,ao_next = 0、不使用;每一个节点的双扇区(256W)分为32项,ao256对象使用了2项,文件名字字符串、以项8字对齐,最小占用一项(小于16字符),最大占用16项(256字符、16*8W = 128W);余下的就是x个同名文件项了,最大x = 32 -2 - 1 = 29项,最小x = 14项;同名文件项:文件所属父目录节点路径名字哈希值4W(硬件4字MD5、唯一性),16位i节点区域、16位的v节点号类型,32位的i节点号,32位的v节点号,32位的父v节点号。 b、256双页分配器对象8KB:如果同名文件项有更多时,需要增加同名文件项扩充双页(256双页分配器对象8KB);OACS系统允许一个文件名字,最大有64K个同名文件项,为纵向单向链表动态增加,ao_dpnext 下一个扩充同名文件项双页地址,为0结束,最大钩挂256个扩充同名文件项双页。每个扩充同名文件项双页共有256同名文件项,最初的2个同名文件项空间16W用于256双页分配器对象8KB的n_ao256.;是故,实际只有256 - 2 = 254个同名文件项,不够用、则纵向单向链表动态扩充。
增加或释放256分配器对象ao256,通常是拷贝原有的相应ao256对象,但“块”最终是由相应的块空间(256KB或双页8KB)管理对象进行。无论如何,都是要拖进内存才能进行相关操作,ao256对象必定有相应的v节点管理对象vmo(磁盘空间无意义)。cao对象的本质就是一个u32数组,数组的成员自然也可以是cao对象;表示成cao对象,是因其某些u32成员可以拆分为:位、位段、的组合表示;也可以写成u8w、u256w等等的整体数组结构模式表示,自然就好。cao对象或说u8w、u256w等等的整体数组结构的成员变量是多个成员位段组合时,比如:u32 xxx.; 需要在变量名称后加“.”,然后在.h头文件另处的声明块u32 xxx. {...};内部对各个位段变量作声明;可以用公共的bitx的x位声明一个位变量,b[x1:x2]声明一个位段变量。所有这些表示模式区别,都是编译器的任务。
u256w n_object.[256]; // 虚拟文件名字256双扇区对象数组,256KB/64KW。
u256w n_object. { // 虚拟文件名字256双扇区对象数组成员描述 。
cao n_ao256.; // 256分配器对象ao256,u16w。
u8w n_name[ao_ncount]; // 文件(目录)名字的字符串数组。
u8w n_snf_item.[ao_ficount]; // 同名文件项(same name file item)数组。
};
u8w n_snf_item. { // 同名文件项描述 。
u4w n_md5; // 文件所属父目录节点路径名字哈希值4W(硬件4字MD5、唯一性)。
u16 n_i_fnna; // inode号所属区域(area)。
u16 n_v_type; // 16位的v节点号类型。
u32 n_i_fnn; // 索引inode号(file inode number)。
u32 n_v_fnn; // 相应在内存打开的v节点号。
u32 n_v_pdnn; // 相应在内存打开的父v节点号。
};
cao n_ao256. { // 8W(含4W的vmo) + 8W位图 。
u32 v_next; // 相应内存空间节点钩子(0号节点),下一个扩充256双扇区分配器对象节点地址,0结束,最大钩挂256节点。
u32 v_dpnext; // 相应内存空间扩充同名文件项双页钩子,下一个双页地址,为0结束,最大钩挂256个扩充同名文件项双页。
u16 v_type; // 对象v节点类型。
u16 v_state; // 对象v节点状态。
u32 v_flags; // 对象v节点标志。
u32 ao_next; // 磁盘空间节点钩子(0号双扇区),下一个扩充256双扇区分配器对象节点号(地址),0结束,最大钩挂256节点。
u32 ao_dpnext; // 磁盘空间扩充同名文件项双页钩子,下一个双页地址,为0结束,最大钩挂256个扩充同名文件项双页。
u8 ao_ocount; // 本对象节点单向链表的实际子节点计数。非0号双扇区为0、不使用。
u8 ao_dpcount; // 扩充同名文件项双页的计数。
u8 ao_ficount; // 本对象的同名文件项计数(每项8字)。
u8 ao_ncount; // 本对象的文件名字项(8字对齐)计数(节点为:14---29,扩充同名文件项双页:1---254)。
u8 ao_namel; // 本对象节点的对应文件(目录)名字的实际长度,最大128字(256个UTF-16字符),0xff为字符串结束。
u8 ao_type; // 文件(目录)的类型。
u16 ao_tficount; // 总文件项计数(包含扩充文件项)。
u8w n_bitmap; // 256位图,对应256个双扇区或文件项。
};
1、目录文件cao对象i节点。
不同于linux,这里的目录(节点)看作是无文件内容的微型文件,被包含在相应的i节点内;目录节点最大有64K个目录项,目录项可以是子目录节点或叶子(文件)属性节点;目录inode号所属区域(area),固定为1,只需32位的目录索引i节点表示。目录节点单向环是48位,包含挂到目录下的48位子i节点,高16位是inode号所属区域(area),如果是子目录节点则为1。新建一个目录时,目录i节点head头钩子i_hnext = 自身i_fnn(初始目录节点单向环),每增加一个目录项、都是在目录i节点head头钩子插入;而删除一个目录项,则需从head头钩子循着单向链表环查找到相应目录项后、删除。目录可以被链接文件进行链接,并提供i_shead头钩子,钩挂对其进行链接的所有链接文件,方便管理,最大被链接数64K个。目录的双向环钩子可以钩挂到其它应用上,比如作为xxx组的成员。目录是无文件内容的微型文件(少了116B的i节点文件内容),其时间戳比普通文件的、少了一项文件内容时间戳3W,共少了116B + 12B = 128B。 // u16 i_fnna; // 目录inode号所属区域(area),固定为1。
cao directory_inode { // 目录i节点cao对象,只需32B + 96B = 128B = 32W。
u8w vmo.; // 内核使用,文件打开表v节点管理对象8字/8w(v node management object)。
u32 i_fnn; // 目录索引inode号(file inode number)。将本目录inode节点钩挂到所属父目录的i节点单向环上(头插入)。
u32 i_pdnn; // 所属父目录i节点号(parent directory inode number)。
u48 i_hnext; // 目录i节点head头钩子,目录节点单向环的下一个i节点号,本目录i节点单向环(挂到目录下的子i节点,头插入)。
u16 i_mode.; // 目录类型和访问权限控制。
u32 i_next; // 目录i节点钩子,下一个i节点号。自定义目录i节点双向环钩子,可以钩挂到其它应用。
u32 i_prev; // 目录i节点钩子,上一个i节点号
u16 i_fcount; // 本目录i节点单向环的实际子节点计数(目录项数)。
u16 i_nlink; // 目录被链接的计数。
u32 i_flags.; // 目录标志。
u32 i_uid; // 目录拥有者id。
u32 i_gid; // 目录用户组id。
u8 i_name[8]; // 目录短名字ASCI字符。
u8 i_type; // i节点对象所属类的类型。
u8 i_layers; // i节点对象的层数。
u48 i_nhash; // 名字32位哈希映射表FNHT项某256双扇区分配器对象256KB的某双扇区(16位相同哈希值不同文件名字节点)。
u32 i_shead; // 目录源i节点号的链接文件头(所有链接到本目录下的链接文件i节点头部,头插入)。
u9w i_timestampsd.; // 时间戳。
};
// 时间戳(Timestamps)字段结构、9*4字节。高64位表示正负秒数,它们以1970年1月1日(unix元年)表示为0,正数表示之后至今的秒数,最大表示
// 约2147.5亿年;负数表示之前的秒数,最小表示约负2147.5亿年。低32位的单位是纳秒(毫微秒(nanosecond),上一秒开始经过的ns数)。
u9w i_timestampsd.{
s64 i_atimes; // 文件的最后访问(access)时间,秒数。
u32 i_atimens; // 纳秒数。
s64 i_ctimes; // 文件inode属性的最后改变(change)时间,秒数。
u32 i_ctimens; // 纳秒数。
s64 i_crtimes; // 文件的最初创建(create)时间,秒数。
u32 i_crtimens; // 纳秒数。
};
u16 i_mode.{
u4 i_mode.[15:12]{ // f_types,文件类型File types。
F_UNKNOWN 0 // 不确定。
F_REG 1 // 普通(常规)文件(regular file)R
F_DIR 2 // 目录文件(微型文件)D(directory file)
F_DBT 3 // 数据库表文件T(data base table file)
F_LNK 4 // 链接Link(微型文件)L
F_EXECP 5 // 可执行程序executable programme文件E
F_CHR 6 // 字符设备(微型文件)C(character device file)
F_BLK 7 // 块设备(微型文件)B(block device file)
F_VDEV 8 // 虚拟设备virtual device(微型文件)V
F_SOCK 9 // Socket文件S
F_FIFO 10 // FIFO管道文件F
F_SMALL 11 // 小型文件(小于等于116B)
F_MSB 12 // 中型带缓冲区文件(medium-sized buffered file)(112B + 4KB)
F_LARGE 13 // 大型文件(不带缓冲页、大于4KB)
F_WHT 14 // 变换文件W
F_DEV 15 // 物理设备文件
};
u12 i_mode.[11:0]{
bit0 S_IXGADM // 组管理员执行权限
bit1 S_IWGADM // 组管理员写权限
bit2 S_IRGADM // 组管理员读权限
bit3 S_IXGRP // 同组用户执行权限
bit4 S_IWGRP // 同组用户写权限
bit5 S_IRGRP // 同组用户读权限
bit6 S_IXOTH // 其它用户执行权限
bit7 S_IWOTH // 其它用户写权限
bit8 S_IROTH // 其它用户读权限
bit9 S_IXAPP // 指定appoint用户执行权限。
bit10 S_IWAPP // 指定appoint用户写权限。
bit11 S_IRAPP // 指定appoint用户读权限。
// i_mode.bit9 S_ISVTX Sticky 位特殊权限,沾着位。
// i_mode.bit10 S_ISGID set-group-ID 位特殊权限。
// i_mode.bit11 S_ISUID set-user-ID 位特殊权限。
};
};
u32 i_flags.{
bit31 I_H; // 1、i节点为“.h”头文件,0、否。
bit30 I_OAC; // 1、i节点为“.oac”文件,0、否。
bit29 I_THR; // 1、i节点为.thr的线程(thread)对象文件,0、否。
bit28 I_DSBUF; // 1、i节点文件使用磁盘空间缓冲页(中型文件)Disk space buffer,0、否。
bit27 I_DG; // 1、i节点文件内容为动态增长Dynamic growth的(通常按大型文件处理),0、i节点文件内容相对固定。
bit26 I_SYS; // 1、i节点文件为系统文件,0、否。对于系统文件root也没权限,只能由机器程序执行。
bit25 I_FSR; // 1、i节点文件为文件系统根目录文件File system root file,0、否。
bit24 I_IF; // 1、i节点文件为不可变的文件Immutable file(不能修改、删除和更名),0、否。
bit23 I_AOF; // 1、i节点文件为仅追加(append-only)的文件(只能把数据追加在文件尾),0、否。
bit22 I_MICRO; // 1、i节点文件为微型文件micro file(没有文件内容),0、否。
bit21 I_OFST; // 1、i节点文件为其它文件系统类型other file system types,0、否。
bit20 I_MUTEX; // 1、i节点互斥锁定,0、否。
bit19 I_IBUF; // 1、i节点缓冲区锁定inode buffer(对应磁盘空间缓冲区在内存中的缓冲区),0、否。
bit18 I_IDM; // 1、i节点数据变动(已修改、脏标志,需磁盘I/O更新)inode data movement,0、否。
bit17 I_IBDM; // 1、i节点缓冲区页数据变动inode buffer data movement,0、否。
bit16 I_DBTP; // 1、i节点为数据表文件时的数据项需要细分rwx权限,0、否。
bit15 I_DIR; // 1、i节点为目录文件(微型文件)D(directory file),0、否。
... // 待续。
};
2、链接文件.lnk、文件叶子i节点cao对象。 内核为每一个新创建的文件分配一个唯一的inode(索引节点),我们可以将inode简单理解成一个指针,它永远指向本文件的具体存储位置。文件属性保存在索引节点里,在访问文件时,索引节点被复制到内存中,从而实现文件的快速访问。系统是通过索引节点、而不是文件名来定位每一个文件。快捷方式(shortcut)是一种功能上类似符号链接的文件对象,但与符号链接有本质的不同。快捷方式是普通的文件(拥有扩展名 .lnk),而非符号,因此,快捷方式可以被复制、移动、更改(某些特殊的快捷方式无法更改所有信息)或删除。快捷方式可以指向文件、文件夹或其他任何系统中合法的位置(包括控制面板、桌面等)。快捷方式如果指向可执行程序,则可以同时指定启动的命令行参数以及启动位置(对于非可执行程序的快捷方式也能指定这些信息,但无意义)。同时,可以为快捷方式单独选择图标(如果没有选择图标,则使用目标的图标),以方便用户个性化。总之,建立快捷方式就是建立了一个新链接文件。当访问链接文件时,系统就会发现他是个链接文件,它读取链接文件找到真正要访问的文件。每建立一个新链接文件,其对应的源i节点之链接引用计数都会加1,创建源i节点文件时、其引用计数 i_nlink = 1,当所有对其的链接文件都删除后,也即i_nlink = 1时、才可删除源i节点文件。可以用根权限强行删除源i节点文件,但相应的链接文件就成为无源而被遗弃(系统自动删除相应链接文件)。
cao link_inode { // 链接文件叶子i节点cao对象,需32B + 96B = 128B。
u8w vmo.; // 内核使用,文件打开表v节点管理对象8字/8w(v node management object)。
u32 i_fnn; // 链接文件索引 inode号(file inode number)。将本链接文件i节点钩挂到所属父目录的i节点单向环上(头插入)。
u32 i_pdnn; // 所属父目录i节点号(parent directory inode number)。
u32 i_hnext; // 没有使用。
u16 i_fnna; // 链接文件inode号所属区域,也是固定为1。
u16 i_mode; // 链接文件类型和访问权限控制。
u32 i_next; // 链接文件i节点钩子,下一个i节点号。自定义链接文件i节点双向环,或许是所有连接文件双向链表i节点钩子。
u32 i_prev; // 链接文件i节点钩子,上一个i节点号
u16 i_fcount; // 没有使用。
u16 i_nlink; // 没有使用。
u32 i_flags; // 链接文件标志。
u32 i_uid; // 链接文件拥有者id。
u32 i_gid; // 链接文件用户组id。
u8 i_name[8]; // 链接文件短名字ASCI字符。
u8 i_type; // i节点对象所属类的类型。
u8 i_layers; // i节点对象的层数。
u48 i_nhash; // 名字32位哈希映射表FNHT项某256双扇区分配器对象256KB的某双扇区(16位相同哈希值不同文件名字节点)。
u32 i_sfnn; // 链接文件的源i节点号。
u9w i_timestampsd.; // 时间戳。
};
u8w vmo. { // 文件打开表v节点管理对象8字/8w(v node management object),嵌入(被包含)到v节点中、为其成员。
// u32 v_fnn; 对象索引v节点号(file node number)。内核使用v节点内存地址管理,v_fnn = &v_pdnn变量的地址即是。
u32 v_pdnn; // 对象父目录v节点(parent directory node number), 将对象v节点钩挂到所属父目录v节点单向环上(头插入)。
u32 v_hnext; // 对象v节点head头钩子,本对象v节点单向环的下一个子v节点,钩挂对象的子节点(头插入),v节点树。
u16 v_type; // 对象v节点所属类的类型。
u16 v_fcount; // 本对象v节点单向环的实际子节点计数。
u32 v_count; // 对象v节点引用计数。
u32 v_next; // 对象v节点钩子,下一个v节点。自定义对象v节点双向环,可以钩挂到其它应用。
u32 v_prev; // 对象v节点钩子,上一个v节点。
u32 v_sflags; // 对象v节点状态标志。
u32 v_fpbuf; // 对象v节点物理内存缓冲区页起始地址,类同this指针 。
};
3、虚拟文件(普通文件,非、目录、链接文件、物理设备文件、等微型文件)cao对象: 一切皆对象,具有i节点的对象才称为磁盘文件;只有文件打开表v节点管理对象vmo、8字/8w(v node management object)的,称为内核文件;oacs系统有90%以上的对象为磁盘文件,余下的、基本上都是内核文件,是故、也有一切皆文件的说法。许多内核对象,如内存对象、CPU对象、IPC对象、线程对象、等等,是没有文件i节点的、但有vmo。链表头初始时是指向自己就构成单向环或双向环了,
cao vfs_inode { // 虚拟文件叶子i节点cao对象,224B、56W、28DW,总256B = 32DW = 64W。
u8w vmo.; // 内核使用,文件打开表v节点管理对象8字/8w(v node management object)。
u32 i_fnn; // 文件索引i_node号(file node number)。将本文件i节点钩挂到所属父目录的i节点单向环上(头插入)。
u32 i_pdnn; // 所属父目录i节点号(parent directory node number)。
u32 i_hnext; // 文件i节点head头钩子,本文件i节点单向环的下一个子节点号,可作为文件内容数据表记录分配器对象钩挂。
u16 i_fnna; // 文件inode号所属区域。
u16 i_mode; // 文件类型和访问权限控制。
u32 i_next; // 文件i节点钩子,下一个i节点号,自定义文件i节点双向环,可以钩挂到其它应用。
u32 i_prev; // 文件i节点钩子,上一个i节点号
u16 i_fcount; // 文件i节点单向环的实际子节点计数。
u16 i_nlink; // 文件被链接的计数。
u32 i_flags; // 文件标志。
u32 i_uid; // 文件拥有者id。
u32 i_gid; // 文件用户组id。
u8 i_name[8]; // 文件短名字ASCI字符。
u8 i_type; // i节点对象所属类的类型。
u8 i_layers; // i节点对象的层数。
u48 i_nhash; // 名字32位哈希映射表FNHT项某256双扇区分配器对象256KB的某双扇区(16位相同哈希值不同文件名字节点)。
u32 i_shead; // 文件源i节点号的链接文件头(所有链接到本文件i节点号下的链接文件i节点头部,头插入)。
u29w i_pdata.; // 文件私有private数据区、116字节。小型文件内容:116B,中型文件内容:112B + 4KB。
u12w i_timestamps.; // 时间戳。
};
u29w i_pdata.{ // 中型文件或小型文件
u32 i_dsbuf; // 中型文件磁盘空间数据区的缓冲页号,或小型文件内容4B数据。
u28w i_data; // 文件内容数据28W(字),112B。
};
u29w i_pdata.{ // 大型文件,100B + xxx。
u64 i_dssadd; // 大型文件磁盘地址空间的扇区开始地址(或许是文件内容数据表记录根分配器对象开始地址)。
u64 i_fsize; // 大型文件总大小,单位字节B。或者是数据表记录分配器对象数,实际的记录数、等等。
u25w i_data; // 大型文件的25W(100B)数据区作为分配器对象链表等。
};
// 时间戳(Timestamps)字段结构、4*12字节。高64位表示正负秒数,它们以1970年1月1日(unix元年)表示为0,正数表示之后至今的秒数,最大表示
// 约2147.5亿年;负数表示之前的秒数,最小表示约负2147.5亿年。低32位的单位是纳秒(毫微秒(nanosecond),上一秒开始经过的ns数)。
i_timestamps.{
s64 i_atimes; // 文件的最后访问(access)时间,秒数。
u32 i_atimens; // 纳秒数。
s64 i_ctimes; // 文件inode属性的最后改变(change)时间,秒数。
u32 i_ctimens; // 纳秒数。
s64 i_crtimes; // 文件的最初创建(create)时间,秒数。
u32 i_crtimens; // 纳秒数。
s64 i_mtimes; // 文件内容的最后修改(modify)时间,秒数。
u32 i_mtimens; // 纳秒数。
};
4、 数据库表文件T(data base table file)对象dbtf: 具有i节点的cao对象才能称为磁盘文件。dbtf通常是大型动态文件,通过数据表记录分配器对象可以动态增减文件的记录、可以1D、2D、3D、、、等动态扩展,前面的磁盘空间文件名字哈希映射表FNHT就是一个2D的256分配器对象ao256使用例子;我们还可以使用64K分配器对象ao64k,3D扩张就会有256T个记录,设每个记录平均占空间64KB,就会有16EB的巨型dbtf文件。数据库DB(data base)是一个目录,可以包含各种杂牌DB子目录,OACS_DB可链接64k个dbtf文件,扩张是没问题,问题是用户磁盘空间是否“够力”而已。我们应用的大多数文件都是dbtf:i节点表文件,各种空间文件,记事本文件text,word文件,网页文件,电子表格文件,资源管理器,编译空间的.h、.oac、.thr文件,等等。大型数据表文件通常是用64K分配器对象ao64k居多,为了更大的动态“弹性”、则是使用256分配器对象ao256居多。世界上大多数软件公司在玩的、通常就是DB节点树,各种应用程序的设计也是围绕dbtf。我一个“初级发烧音响业余玩家”,可没那么多精力去玩花样、只想“简化再简化、最好是一天就可以出结果”;设想2种分配器对象搞定80%的工作量以及规划总体框架,本打算闭关半年攻关、而这就耗费3个月时间了;还得经常通宵,感觉有点力不从心,最近还是决心晚上11点后抽1小时学唱歌,否则唱歌水平又得回到“解放前”;想想七月份,就可以出来“吃喝玩乐”了、还是坚持吧。错误是难免的,以后慢慢修改。
5、编译空间cso(compiling spatial objects)对象:
❏我们不管“先有鸡还是先有蛋”的问题,OACS系统包含了编译空间规则及相应的occ编译器,就如同linux包含gcc编译器一样;不过,“第一次的OACS系统编译”一定是在第三方平台进行。OAC语言本来就是面向对象汇编语言为主,occ编译器自然会比gcc要简单得多,但也是得重新构造和设计,我是选择windows平台来进行。编译器维护一个符号表,符号表中的每项记录一个符号相关信息;例如符号名字,符号类型,数据类型(据此可计算出占用内存大小),偏移起始地址等信息。当使用某个标识符a时,编译器就在符号表中找名为a的符号;找到了就知道其类型,地址等信息,没找到就会报“未定义的符号a”。符号表自然就是一个数据库表文件dbtf,符号表中的一个记录就是“键值对”项(item),键值对通常写法为 name : value(键与值以冒号分割),变量名作为键KEY,然后语义信息作为值。键值对类似于:PHP 中的关联数组,Python 中的字典,C 语言中的哈希表,Java 中的哈希映射,Ruby 和 Perl 中的哈希表。OAC语言的键值对32B/8W(项):最大28个ASCI字符(E文大小写,对于不到28字符的短名字,尾部会存在没有用到的ASCI字符字节、这里为0xFF(对于flash,增长名字时、容易),而没使用/0作为字符串结束。):u8项的类型(数值(Number)类型、或位段类型、或字符串(String)、或函数(Function)、或子节点类型(cao对象、集合对象object)、或数组类型(Array)、或未使用值来声明的变量 undefined、或空(Null)、或可变动态类型var、或唯一值(Symbol)、等等)、u8项的属性、u16项的长度(位段长度、或数组长度),等效为1字u32的键属性说明值。变量(或常量、方法、位段(0--31位)、等,可把变量看做存储数据的容器、大小写敏感)项的符号名字(Symbol Name),编译器在进程所在虚拟内存空间的起始地址开始,每次从头比对查准项名称时,自然会得到项的相应虚拟内存地址,从而用地址替代变量(或常量)标识符。通常标识符只用于链接或调试信息,所有实际运行时要用的信息都已经被转换为某种寻址方式了,内存地址的内容或是用户给出的初始化值、或是初始化为0、等等,还得看是全局变量或局部变量等情形。CPU 访问内存时需要的是地址(Address)或说指针(Pointer),而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。变量名和函数名为我们提供了方便,让我们在编写代码过程中可以使用易于阅读和理解的英文字符串,不用直接面对二进制地址。虽然变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符,但在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、对象名、字符串名和数组名表示的是代码块或数据块的首地址。
❏无论是用OAC语言写编译器、或编写OACS操作系统内核、或编写应用程序、或编写物理(或协议)设备节点树,都是简单的面向对象之数据库DB“节点树”描述模式。抽象说就是“道生一、一生二(动分阴(空间的类对象静态数据)阳(时空的指令流、从而变动的数据流))、二生三(阴阳和合生三才)、三生万物”,遵从“简单到复杂”的自然规律过程;简单地说就是:先编写根类节点、再编写子节点、之后编写孙节点...,从“一到二、到三、到多”、或说从“点到面、到体、到多”而已。即先定义顶层模块类功能,进而分析要构成顶层模块类的必要子模块类;然后进一步对各个模块类进行分解、设计,直到无法进一步分解的底层功能块类,不外是数据库DB节点树编写过程。
❏编译器符号表:根类,类节点,子类节点、...,排列,节点树描述符表。编译空间cso对象对应的简单说就是类文件目录、或说DB目录,终是一个cso目录节点树。OAC语言将“类”划分为类属性和类方法2部分,将类比拟为“cso目录节点”;旗下的类属性对象cao(class attribute object)比拟为“.h”头文件(cao符号表,全局变量、静态变量、堆空间,可以在设计时赋值),类方法对象cmo(class method object)比拟为“.oac”文件(cmo符号表,局部变量,全局类属性常量(u64对齐的cao对象初始化数组常量),方法u16代码数组(一个个方法数组、u64对齐),代码空间)。这样划分,会使得编程更为简单明了;cso类目录节点下、通常还有一个扩展名为.thr的线程(thread)对象文件(线程符号表,方法数组、常量数组,类似.oac,动态变量、堆空间,栈空间),cso根目录下就是包含main()方法的主线程文件了。.h文件反映了类属性对象cao的规划模板,还包含用这模板声明和定义的具体cao对象或cao对象数组;而cao对象的初始化元组是常量,在cmo中定义、也就是说其放置在.oac文件内容中。常量和类方法(函数)都是在.oac文件中编写,指令构成方法(函数)、指令段(伪指令、宏指令、内联函数);宏指令是汇编语言程序中的一种伪指令,是代表某功能的一段指令代码;在宏指令出现的地方,编译器总是自动的把他们替换成相应定义的指令代码块。宏指令与子程序(方法、函数)都可以用一个名字定义一段指令代码,以简化源程序的结构和设计。宏指令的参数传送简单,执行效率高;使用宏指令不会缩短目标程序,适用于子功能代码较短、传参较多的情况。子程序代码(如类的方法)在目标程序中只出现一次,调用子程序是执行同一程序段,因此,目标程序也得到相应的简化;模块化、方法类库、节省内存,可被多次调用、编程效率高;但有额外开销(压入将使用的寄存器组和返回地址,计算转向地址、传递参数,返回时还需弹出恢复等),增加了执行时间;适用于子功能代码较长,调用比较频繁的情况。cmo对象的项多为数组(u64对齐):cao对象的初始化常量元数组,方法的指令数组(oac汇编指令一条为半字,汇编语句和宏指令则要编译成多条汇编指令,而指令码会对应到oac_asm公共汇编语言数据表文件)。方法的语句代码块或元数组,对应的只是cmo对象符号表中一个数组变量项:方法名字,1字u32的数组变量说明值(数组变量属性、长度、等);而方法的语句代码块(包含注释)或元数组则被编译自动生成到.lst汇编列表文件,.thr的线程(thread)对象