当玩到“虚拟内存”时,“簇”的概念有冲突,前面2章又需修改,这没办法。休息了一个星期(8月23--29日)、再来,修修补补需要一段时间。
这章介绍AOS中最为复杂的“虚拟设备”进程v节点。先将主框架、流程,大致画出来、再慢慢仔细修改。
分而治之、将SMP多处理器抽象分层:
⚫ AOS内核容器层:管理大部分的设备(包含CPU的分配),五行CPU或内核线程,1号init进程,大部分的系统调用,等等。将系统资源划分为“木、火、土、金、水”五类,以减少竞争和并发;每个进程都运行在自己的虚拟“时空”中、各不相干,进程间通过IPC消息包中断来通信,AOS的0号进程为内核进程(有五类基本线程、管理较多的外设)。中断机制也部分归并到IPC消息包结构中,不再需要中断优先级概念,也没必要为每个进程线程建立独立的内核栈。
⚫ 单CPU内核层:管理一个用户进程队列。每个CPU拥有私有的一级Cache(高速缓存L1-I、L1-D)、二级Cache高速缓存L2,CPU对L1和L2的读写操作是最快的;每个CPU在L1或L2中建立一个内核栈(4kb),是最为合理和快速的。所以,一个用户进程队列共用其所属CPU的4kb内核栈;当CPU节点在用户进程空间接收到IPC消息包而中断时,CPU保存用户进程的线程之上下文到相应的用户进程之线程栈(用户进程最少有一个主线程,每个用户线程栈通常是8Mb虚拟内存空间),切换栈指针为CPU内核栈从而进入CPU内核空间处理IPC消息包;CPU在内核空间处理IPC消息包时,同样可以自动接收更多的IPC消息包、但不会是可重入中断处理,CPU的L2有4kb的IPC消息包循环接收缓冲区。中断上半步快速处理所有消息,在处理完所有中断消息后、用户CPU处理中断下半部(32个为用户CPU所属的内核线程、软中断、工作队列、tasklet),最后调度(抢占式调度)最高的动态优先级之进程的线程,从而回到相应用户进程的线程空间运行、等等。当用户进程的线程阻塞(通常是中断发送IPC消息包)、让步另外的进程线程时,或者滴答时间(通常是10ms)到的广播中断,用户CPU都要重新运行调度程序。CPU进入内核空间处理所有IPC消息包完成后,才会清中断,在准备进入用户进程空间时、还需再看一下是否有新的IPC消息包中断。描述单CPU的、是“单CPU虚拟设备”v节点,主要功能有:用户进程线程的调度、线程动态优先级计算,中断消息包上半部处理,单CPU的32个内核线程(软中断、工作队列、tasklet、调度)、处理中断下半部。
⚫ 单用户进程层:AOS中最为复杂的“虚拟设备”进程v节点结构struct pcb_inode,直接关联的v节点有:虚拟进程设备v节点pcb_inode、虚拟内存设备v节点mm_inode、用户环境文件v节点、进程所属可执行程序procedure的v节点、日志文件系统JFFS的v节点。间接关联的打开文件之v节点可以有64k个。
⚫ 单用户进程的线程层:进程v节点结构的v节点信息结构32字节字段、描述线程小组(线程池),最大可以有64k个线程。
物理内存地址48位,内存缓冲区(1页8扇区、4kb,物理页)号、32位4G个,往往是给出内存缓冲区号或说物理内存页号、就可算出其真实物理地址;物理内存一半用于内核分配,如内存缓冲区、v节点、等等,另一半用于用户进程虚拟内存的物理内存页映射分配;进程虚拟内存空间线性地址48位,每一个进程都视为有256TB的虚拟内存空间,高128TB为内核物理内存映射的共享虚拟空间、通常是只读。内存读取速度:私有高速缓存L1最快、类比于从“冰箱”读写(购与送货),其次是二级Cache高速缓存L2、类比于从“小超市”读写,之后是三级Cache高速共享缓存L3、类比于从“大超市”读写;最后是通过总线桥的64位DDR主物理内存、类比于从“网购”读写,如果还要经过虚拟内存空间到物理内存的映射环节(映射、缺页、分配、锁判断、等等手续)、那就更慢了。设备的读写是最慢的,即使是应用DMA(挪用外设64位或32位总线矩阵周期)和中断通知、也只是将设备数据读写到DDR主存。
用户进程虚拟内存空间:
页全局目录PGD(page global directory)(9位、占据一页、512GB/项,共512项),高256项(高128TB)为内核映射的共享虚拟空间;
页上级目录PUD(page upper directory)(9位、共占据512页2MB内存,共512*512 = 256k项、1GB/项),高128K项为内核映射的共享虚拟空间;
中间页目录PMD(page middle directory)(9位、共占据512*512页1GB内存,共512*512*512 = 128M项、2MB/项),高64M项共享虚拟空间;
页表条目PTE(page table entry)(9位、共需占据512*512*512页512GB内存,共512*512*512*512 = 64G项、4KB/项),高32G项共享虚拟空间;
页内地址12位,4KB。
用户进程页全局目录PGD页中,用户进程占据的是0---255项,第255项虚拟内存地址区域512GB、是最大64K个的用户线程栈。每个用户线程栈为8MB,实际运行中、每个用户线程栈动态分配、只是得到一个物理内存页映射(为线程运行时其栈指针所指虚拟区域页之映射);根据线程号tid就可以得知其用户线程栈虚拟地址范围,理论上、用户进程有128TB的虚拟内存空间、可以编写巨型程序,但现今的大部分电脑物理内存有16GB、就算高档的。一个程序、装入到物理内存页的是极少部分,往往是动态缺页时,才分配物理内存页。通常,AOS内核操作的是48位物理地址、用户进程操作的是48位虚拟内存线性地址。AOS的设计是尽可能将用户进程必须的页、直接放入物理内存页中,以提升速度和效能。为了速度,一个CPU一个内核栈、而不是一个进程一个内核栈;一个进程内的所有线程都有自己的用户线程栈,一个进程最少有一个主线程。一切以简洁、清晰、少代码、为主。如果一个低优先级线程已经获得资源锁,在线程调度前需考虑高优先级线程是否需要对共享资源做操作;不轻易做调度,等待低优先级线程释放共享资源锁后、才做线程调度,又或者做优先级提升操作方案。进程线程的描述在进程虚拟设备v节点,线程的详细描述在v节点信息结构;当进程只是一个主线程时、pid = tid,用户进程的线程组之2个页缓冲区无需使用。假如有最大64K个不同动态优先级字节值(数值越大优先级越高)的线程调度,可按16位线程号分为256个小组,每小组256个线程;当一个线程就绪或挂起等等情形时,必须对该线程所对应的小组线程描述符之就绪动态优先级执行1条指令VMAX256,从而输出该组的动态优先级最大字节值到用户进程第二缓冲区之线程组描述符;之后对256个线程组描述符再一次VMAX256得到MMAX,如果当前线程的动态优先级字节值小于MMAX,则置“需调度”标志,这样编写线程调度程序就非常简洁了。
进程号和其mm对应的v节点号在同一个64k范围内,只需16位的相对v节点号,前面32k个v节点号分给进程、后面的32k个为相应mm的v节点号。
提示:以下是本篇文章正文内容,下面案例可供参考
⚫内存管理:内核内存与用户内存(虚拟内存)比例1:1。逻辑地址经段机制转化成线性地址,线性地址又经过页机制转化为物理地址。
⚫AOS用户进程空间:256TB,高128TB为内核映射的共享虚拟内存空间;通常,低128TB才是用户进程管理的虚拟内存空间。
⚫虚拟内存空间线性地址:PGD(9位) + PUD(9位) + PMD(9位) + PTE(9位) + 页内12位偏移地址offset。PGD.PUD.PMD.PTE.offset。
⚫用户进程空间虚拟内存线性地址:64位,最高位为0、高15位32K的进程号,PID.PGD.PUD.PMD.PTE.offset。
⚫AOS物理内存地址48位:LDZONE(4位、暂时为0)+ PBUF(32位页缓冲区、物理页号)+ 页内12位偏移地址offset。LDZONE.PBUF.offset。
⚫LZONE大型分区通常不用,32位PBUF(页号)就是物理内存页框、也称为页缓冲区,当前AOS最大物理内存4G页 = 16TB(以后会扩展到256TB)。
⚫linux:当用户进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存,获得的仅仅是对一个新的线性地址区间的使用权。实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请求页机制”产生“缺页”异常,从而进入分配实际页面的例程。该异常是虚拟内存机制赖以存在的基本保证——它会告诉内核去真正为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了系统的物理内存上。(当然,如果页被换出到磁盘,也会产生缺页异常,不过这时不用再建立页表了)。这种请求页机制把页面的分配推迟到不能再推迟为止,并不急于把所有的事情都一次做完。之所以这么做是利用了内存访问的“局部性原理”,请求页带来的好处是节约了空闲内存,提高了系统的吞吐率。在内存区域结构上的nopage操作。当访问的进程虚拟内存并未真正分配页面时,该操作便被调用来分配实际的物理页,并为该页建立页表项。
⚫虽然应用程序操作的对象是映射到物理内存之上的虚拟内存,但是处理器CPU直接操作的却是物理内存。所以当应用程序访问一个虚拟地址时,首先必须将虚拟地址转化成物理地址,然后处理器CPU才能解析地址访问请求。地址的转换工作需要通过查询页表才能完成,概括地讲,地址转换需要将虚拟地址分段,使每段虚地址都作为一个索引指向页表,而页表项则指向下一级别的页表或者指向最终的物理页面。因为在虚拟地址映射到页之前必须先分配物理页——也就是说必须先从内核中获取空闲页,并建立页表。但是实际上系统使用内存时还是倾向于分配连续的内存块,因为分配连续内存时,页表不需要更改,因此能降低TLB的刷新率(频繁刷新会在很大程度上降低访问速度)。
⚫AOS内存管理功能跟linux大同小异,AOS更为简洁和更多的行为是直接操作物理内存而似乎忽略映射、从而更为高速和高效。
⚫用户程序分段:
代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以是只读。
AOS的用户程序代码段通常会小于64kb,AOS本身(包含系统调用库等等)而言就会是小于32kb;用户程序再“牛鬼”,也不可能远远复杂于编写操作系统OS的代码量;当然,这里是谈论用汇编或C语言作为编写工具,我们不说使用C++类工具、那些“变态”,用它们编写出臃肿的GB级代码量、一点都不奇怪。
数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。
BSS段:BSS段包含了程序中未初始化的全局变量,在内存中 bss段全部置零。
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
栈:用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
数据段、BSS和堆通常是被连续存储的——内存位置上是连续的,而代码段和栈往往会被独立存放。堆和栈两个区域一个向下“长”(i386体系结构中栈向下、堆向上),一个向上“长”,相对而生。堆、bss、数据段(初始化过的)都在进程空间中由数据段内存区域表示。
⚫物理页(PP, Physical Page)(也叫页框、页帧、page frame)4kb,物理内存中的页PP;
虚拟页( VP, Virtual Page ),虚拟空间中的页VP;
磁盘页( DP, Disk Page ),磁盘空间中的页DP。
⚫从Virtual Address到Physical Address的映射,通过一个叫MMU( Memory Mangement Unit )单元来完成。
⚫每一个进程有它自己的页全局目录PGD(Page Global Directory、一个物理页,也称为PGD页表、PGD描述符)和自己的页表集。当发生进程切换时,Linux把cr3控制寄存器的内容(页全局目录基址)保存在前一个执行进程的页全局目录PGD描述符(一个48位页全局目录物理基址的指针、或者32位物理页号)中,然后把下一个要执行进程的页全局目录PGD描述符的值装入cr3寄存器中。因此,当新进程重新开始在CPU上执行时,分页单元指向一组正确的页表。分页单元( paging unit )把线性地址转换成物理地址。其中的一个关键任务就是把所请求的访问类型与线性地址的访问权限相比较,如果这次内存访问是无效的,就产生一个缺页异常。
⚫页表:把线性地址映射到物理地址的数据结构称为页表(page table)。页表存放在主存中,并在启用分页单元之前必须由内核对页表进行适当的初始化。
正在使用的页目录的物理地址存放在控制寄存器CR3中。
⚫例子:以线程栈为例。
用户进程页全局目录PGD页表中,用户进程占据的是0---255项,第255项为虚拟内存地址线性段512GB、是最大64K个的用户线程栈。我们假设有128个线程,每个用户线程栈最大为8MB(2k页);运行的某一时刻,128个线程栈为从1页---2k页不等。
对于一个栈线性地址:PGD.PUD.PMD.PTE.offset
首先PGD页表的物理页基址存放于CR3中,栈线性地址的PGD页表项为255号、项中条目有指向一个PUD页表(上级目录页表)的物理页号基址;
其次512项的PUD页表的每个条目项有指向一个PMD(中间目录页表)的物理页号基址,因只有128个线程,除0号条目有内容外、PUD页表的其余511项为0无效,或说栈线性地址的PUD页表项为0号;
之后512项的PMD页表的每个条目项有指向一个PTE页表的物理页号基址,128个线程;PMD页表的每4项为一个线程栈,每项指向一个有512条目的PTE页表的物理页号基址,而PTE页表的每个条目则指向一个页面( Page )的物理首地址;栈线性地址的PMD页表项对应线程号,如果一个线程栈只是一页时,栈线性地址的PTE页表项为0。
这样,PGD页表的物理页需要1页、线程栈PUD页表的物理页需要1页、线程栈PMD页表的物理页需要512页、线程栈PTE页表的物理页需要的页数量为128个线程栈的动态页数量(若平均一个线程栈1k页、则需128K个物理页)。
如果一个进程的代码段不到1页(4kb),那代码段的虚拟线性地址PGD.PUD.PMD.PTE.offset = 0.0.0.0.offset。
如果一个进程只有一个主线程、其栈不到1页(4kb),那主线程栈的虚拟线性地址PGD.PUD.PMD.PTE.offset = 255.0.0.0.offset。
⚫注意:AOS为了高速高效,不可能在调度切换线程时、操纵虚拟线性地址(那要一大堆麻烦手续),而是直接操纵物理地址!实际运行中、每个用户线程栈动态分配、只是得到一个物理内存页映射(为线程运行时其栈指针所指虚拟区域页之映射)。AOS1.0版本、不使用页交换机制!另一种的极低优先级进程移除模式。
⚫有n个运行的进程(n小于等于32k),就必须分配n个装配PGD页表的物理页。PUD页表、PMD页表、PTE页表的数量是跟用户进程相关,或说编写用户程序相关;通常PUD页表会被分配物理内存,PMD页表、PTE页表、则会是部分被分配物理页而驻留内存。PGD页表、PUD页表、PMD页表、PTE页表、统称用户进程页表,这4种页表都有512个条目项,PGD页表项指向一个PUD页表、PUD页表项指向一个PMD页表、PMD页表项指向一个PTE页表、PTE页表项指向一个物理页,offset是页内偏移。一张表需要一个物理页框来装配,或说一张表大小是4kb、512个表项、表项大小为8b = 2w(2字),其中36位为物理页号、另12位+16位为属性标志。
表项 = 高16位属性状态标志 + 36位为物理页号 + 12位页属性标志。
1、物理页、页表项结构
内核必须记录每个物理页的状态,是属于用户进程的页、还是内核的,空闲还是使用、等等;linux将物理页信息保存到struct page的页描述符中,占据32字节、约为一页的1%,浪费啊、4G物理页就要额外占去128GB物理内存。mem_map是一个struct page的数组,管理着系统中所有的物理内存页面。在系统启动的过程中,创建和分配mem_map的内存区域。
AOS追求小代码量,任何不符合的“苗头”都会想法扑灭,是故、去除linux的struct page页描述符。linux页描述符结构struct page中字段:
virtual 对于需要使用高端内存区域的页,即无法直接映射到内核的虚拟地址空间,因此需要用virtual保存该页的虚拟地址。这是老CPU错误,不必要!
_refcount 引用计数字段。没有关联实际对象、太细,去除!在VMA对象虚拟内存段中实现。当对象释放时,是连续多个页回收。
_mapcount 被页表映射次数。没有关联实际对象、太细,去除!在VMA对象虚拟内存段中实现。
index 在映射的虚拟空间(vma_area)内的偏移。没有关联实际对象、太细,去除!在VMA对象虚拟内存段中实现。
private 私有数据指针,应用场景定其具体的含义。太细,去除!在VMA对象虚拟内存段中实现。
lru 链表头。太细,去除!
mapping 指向与该页相关的address_space对象(类似file_vma对象)。太细,去除!在文件v节点才有指向file_vma对象的mapping。
AOS的页描述符结构struct page相比linux要简单很多,页在内存中为物理页时、大部分标志位才有意义;物理页简单分类为页表的页、非页表的页,而页表有4种:PGD页表、PUD页表、PMD页表、PTE页表。页为页表时,它的大部分标志对其所映射之所有页面起作用。
struct page { // AOS的页描述符结构。
u36 p_pno; // 物理页号。
u12 p_flags; // 页属性标志。
};
t_pflags.bit0 PRESENT; // 1、物理页号(页面)有效,或说页在内存中。0、否,缺页异常。
t_pflags.bit1 RW; // 1、页面可以读写Read/Write,0、为只读。当CPU在特权级时,此位不起作用。
t_pflags.bit2 USER;// 1、页面所有级别的程序都可访问,0、只允许Supervisor特权程序访问。
t_pflags.bit3 PWT; // 1、使用Write-Through的Cache类型,0、使用Write-Back的Cache类型。当CR0.CD=1(Cache被Disable),标志忽略。
t_pflags.bit4 PCD; // 1、页面是不能被Cache,0、允许Cache。当CR0.CD=1时,此标志被忽略。
t_pflags.bit5 ACCESSED; // 访问位。1、页面已被访问(读或写),AOS用来监视页的使用频率。
t_pflags.bit6 DIRTY; // 脏位。1、页面内容已经改变,AOS用来稍后回写磁盘。
t_pflags.bit7 PS; // Page Size标志。1、(少用一级、PTE页表),0、4KB。
t_pflags.bit8 GLOBAL; // 全局位。1、表项所指页是全局Page,在CR3被更新时,TLB内的全局Page不会被刷新。
// 在CR4的页全局启用PGE(Page Global Enable, )标志置位时、这个标志才起作用。
// t_pflags.[11:9] AVL; // CPU忽略,软件可以使用。
t_pflags.bit9 FVO; // 1、该page属于页缓存或文件映射、FVO对象,0、该page为匿名映射、anon_vma对象。
t_pflags.bit10 NOEXEC; // 1、该page不可执行,0、否。
t_pflags.bit11 FILE; // 1、该page为非线性映射,0、否。
struct table_entry { // PGD页表、PUD页表、PMD页表、PTE页表、的页表项结构。
u16 t_flags; // 页表项所指页的属性状态标志。
struct page t_page; // 页表项所指的页描述符结构。
};
t_flags.bit48 PG_unevictable; // 1、表项所指页不可回收,0、否。
t_flags.bit49 PG_mlocked; // 1、表项所指页对应的VMA处于locked状态,0、否。
t_flags.bit50 PG_locked; // 1、表项所指页被锁定,0、否。
t_flags.bit51 PG_error; // 1、表项所指页的I/O操作发生了错误,0、否。
t_flags.bit52 PG_uptodate; // 1、表项所指页的数据已经刷新,0、否。
t_flags.bit53 PG_slab; // 1、表项所指页属于slab分配器,0、否。
t_flags.bit54 PG_writeback;// 1、表项所指页中的数据正在被回写到后备存储器,0、否。
t_flags.bit55 PG_private; // 1、表项所指页为私有,0、否。
t_flags.bit56 PG_arch_1; // 1、表项所指页与体系结构相关,0、否。XXX、AOS不用。
t_flags.bit57 PG_swapbacked;// 1、表项所指页的页面具有swap缓存功能,通常匿名页面才可以写回swap分区,0、否。XXX、AOS不用。
t_flags.bit58 PG_reclaim; // 1、表项所指页马上要被回收,0、否。XXX、AOS不用。
t_flags.bit59 PG_lru; // 1、表项所指页加入了LRU链表,0、否。内核使用LRU链表管理活跃和不活跃页面。XXX、AOS不用。
t_flags.bit60 PG_active; // 1、表项所指页活跃程度,0、否。在kswapd页面回收中使用。XXX、AOS不用。
t_flags.bit61 PG_referenced;// 1、表项所指页刚刚被访问过,0、否。在kswapd页面回收中使用。XXX、AOS不用。
t_flags.bit62 PG_swapcache; // 1、表项所指页处于swap cache中,0、否。XXX、AOS不用。
t_flags.bit63 PG_reserved; // 1、预留页、表项所指页不可交换到swap,0、否。XXX、AOS不用。
AOS或许会用LRU类型的算法置换页面,但总觉得置换页面显得“太细”,不如置换具有连续页面“对象”;是故,t_flags标志字段还在思考中,标志太多也意味着代码量增大。
2、流容器对象Flow vessel object
一个页面、我感觉“太细”,尽管有“死板”的2MB大页面存在;是故,用户程序编写有必要增加一种可变的连续页结构、虚拟内存空间流容器对象。页描述符结构、页表项结构,描述的是虚拟内存空间页到物理内存页的映射关系和物理页的状态标志。256TB的用户进程虚拟内存空间,以页为单位有64G页;AOS的流容器对象FVO最大为4k个连续页,每个线程栈可视为2k个连续页(8MB)的FVO。为了速度,有时候FVO在虚拟内存空间是连续页、映射到物理内存空间时、也是不可交换出去的连续页;AOS在用户进程虚拟内存空间与内核物理内存空间交换数据时,通常是FVO指针提交、而非拷贝!如果一个4MB的FVO使用拷贝要多花费近千万条指令周期,即使你有几百万个CPU并行、也是无法补偿其亏损。那些一个进程的多个线程可以并发运行在多个CPU上的各种锁、淫荡技巧、等等,在我看只是“花拳绣腿”、引起代码量巨增。FVO不一定在物理空间也是连续页,如线程栈、如果只是一个页时,也要分配2k个连续物理页,就很浪费了,应该是动态按需分配物理页(非线性映射)。
struct fvo { // 虚拟内存空间流容器对象Flow vessel object结构。
u36 fvo_start; // FVO对象的虚拟线性地址开始页号,PGD.PUD.PMD.PTE.0。地址空间(address_space)
u12 fvo_len; // FVO对象的连续页数、1---4k页,流容器最大16MB或8GB(页面是2MB的大页)。
};
3、进程虚拟内存空间VMA对象vm_area_struct
用户进程的内存描述符mm_struct(mm的v节点)指向全部虚拟空间,而vm_area_struct对象(简称VMA对象)只是指向了虚拟空间的一段内存区域。
VMA对象有点类似分段内存,AOS规定:VMA对象最大为4G页,最小为一页;VMA对象是将“访问属性一致的虚拟地址空间(FVO流容器对象)存放在一起”,所谓访问属性在这里无非指的是“可读、可写、可执行、锁、偏移、等”。256TB用户进程空间:高128TB为内核映射的共享虚拟内存空间,低128TB是用户进程管理的虚拟内存空间。
struct vm_area_struct { // VMA对象结构、32字节。
u32 vm_vno; // 关联文件的v节点号。
u32 vm_flags; // 标志字段。
u16 vm_refcount; // VMA对象的引用计数。
struct fvo vm_start;// VMA对象的FVO流容器对象虚拟线性地址开始页号,PGD.PUD.PMD.PTE.0、u48。
u32 vm_len; // VMA对象的连续FVO流容器对象数量、或FVO流容器对象的记录数、或在FVO流容器内的偏移index。
u32 vm_ops; // VMA对象方法表号。
u64 vm_foff; // 关联文件的偏移量、单位字节。
};
支持最大文件为16 Eb(64位),
64位文件磁盘地址空间的“点地址” =
db(16位).c(16位).ps(16位).(bdb + offset)(16位) = db.c.ps.bo。
式中:bo16位(bdb大数据块7位 + offset扇区内偏移9位),db数据块16位,
c章(chapters)16位,ps16位(page页13位 + sector扇区3位)。
页内偏移offset:12位 = sector扇区3位 + offset扇区内偏移9位。
VMA对象结构是一个通用描述结构,不同的VMA对象其结构成员的多少是不一样;比如进程的64k个线程栈VMA对象,只有vm_flags、vm_start、vm_ops这3个成员必须,难道要做一个线程栈VMA对象的关联文件?vm_start = 255.0.0.0.2K,进程的64k个线程栈VMA对象瓜分了PGD页表项255、虚拟内存线性地址段512GB;每个用户线程栈最大为8MB(2k页)、或说其FVO流容器对象大小为连续2k页,有64K个用户线程栈FVO流容器对象;虽然64K个用户线程栈占据虚拟内存空间512GB,但如果某时刻只是有1个主线程,那映射物理页只是:PGD页表1、PUD页表1、PMD页表1、PTE页表项n(n = 1--2k、动态),最多占据2055物理页、8.027MB。
VMA对象也可以是文件打开表fot的项(AOS文件VMA对象file_vma、类同linux的struct file),其VMA对象只有一个FVO流容器对象等效为1个流缓冲区吧。AOS的VMA对象归并了许多虚拟内存空间的各种对象(数据库表、静态动态方法库、线程栈虚拟空间、进程虚拟空间、文件目录流、显卡内存空间、缓冲区、等等),关联了文件磁盘空间、虚拟内存空间、内核物理内存空间、用户进程线程、系统调用、等等。
AOS文件VMA对象file_vma是文件打开表fot的项,类同于linux的struct file;一个具体的文件在打开后,内核会在内存中为之建立一个v节点结构,其中的v节点信息结构32字节字段之file_vma_no域指向一个file_vma对象(类似linux的address_space结构)。这样,一个文件就对应一个file_vma对象结构,一个file_vma对象与其内的偏移量vm_foff就能够最终确定一个物理页面(虚拟内存通过页表映射到物理内存)。 v节点的信息结构的方法集v_ops用于管理文件磁盘空间或设备空间到FVO流容器对象file_vma的窗口映射,与之对应、流容器对象file_vma内的vm_ops方法集就是用来操作该文件映射到内存的页面,比如把内存中的修改写回文件、从文件中读入数据到页面缓冲等等。内核操作的是直接物理页面,文件IO操作直接和页缓存交互。内核的FVO流容器对象file_vma的“点地址”是物理页面地址,如设备等内核对象的操作无需经过页表映射、缺页、等等,从而为高速,内核的FVO流容器对象file_vma通常是连续物理页。用户进程的FVO流容器对象file_vma通常是非线性映射、是非连续的物理页组,但在虚拟内存空间是连续的页,最终是通
过用户进程空间的“页表”非线性映射到物理内存;如果也是线性映射,只是起始页经过“页表”映射到物理页,速度就会有很大的提高。
用户进程读一个文件的某内容时,首先是据文件描述符fd找到文件VMA对象file_vma(文件打开表fot的项)做权限检查,通过后、再计算读取的文件内容的偏移量vm_foff、从而相应要读取的页;然后在对象file_vma的流容器FVO中访问该文件的页缓存,如果页缓存命中,那么直接返回文件内容;如果页缓存缺失,那么产生一个页缺失异常、创建一个新缓存页,然后从磁盘中读取相应文件的页填充该缓存页;之后从页缺失异常中恢复,继续往下读。也可以从文件的v节点的v节点信息结构中找到文件VMA对象file_vma。写文件时,首先通过所写内容在文件中的偏移量计算出相应的页,然后还是通过找到文件VMA对象file_vma、从而找到流容器FVO的页;如果页缓存命中,直接把文件内容修改更新在页缓存的页中,写文件就结束了。这时候文件修改处于流容器FVO的页缓存中,并没有写回writeback到磁盘文件中去。一个页缓存中的页如果被修改,那么会被标记成脏页。脏页需要写回到磁盘中的文件块。有两种方式可以把脏页写回磁盘,也就是flush。
a. 手动调用sync()或者fsync()系统调用把脏页写回,
b. pdflush进程会定时把脏页写回到磁盘。
如果脏页正在被写回,那么会被设置写回标记;这时候该页就被上锁,其他写请求被阻塞直到锁释放。系统调用与磁盘操作并不同步。
struct file_vma { // file_vma对象结构、32字节。功能类同linux的struct file、结构类同address_space对象。
u32 vm_vno; // 关联文件的v节点号。
u32 vm_flags; // 标志字段。
u16 vm_refcount;// file_vma对象的引用计数。
struct fvo vm_start;// file_vma对象的FVO流容器对象虚拟线性地址开始页号,PGD.PUD.PMD.PTE.0、u48。
u32 vm_len; // FVO流容器对象的记录数、或在FVO流容器内的偏移index。
u32 vm_ops; // file_vma对象方法表号。
u64 vm_foff; // 关联文件的偏移量、单位字节。注:修改AOS最大文件为16EB。
};
vm_flags.bit0 VM_READ; // 进程对VMA对象、从而流容器对象、从而页的读权限。1、可读,0、 否。
vm_flags.bit1 VM_WRITE;// 进程对VMA对象、从而流容器对象、从而页的写权限。1、可写,0、 否。
vm_flags.bit2 VM_EXEC; // 进程对VMA对象、从而流容器对象、从而页的执行权限。1、可执行,0、 否。
vm_flags.bit3 VM_SHARED; // 进程对VMA对象、从而流容器对象、从而页的多个进程共享权限。1、可多个进程共享,0、 否。
vm_flags.bit4 VM_MAYREAD; // 进程对关联文件的读权限。1、可读,0、 否。
vm_flags.bit5 VM_MAYWRITE; // 进程对关联文件的写权限。1、可写,0、 否。
vm_flags.bit6 VM_MAYEXEC; // 进程对关联文件的执行权限。1、可执行,0、 否。
vm_flags.bit7 VM_MAYSHARED;// 进程对关联文件的多个进程共享权限。1、可多个进程共享,0、 否。
vm_flags.bit8 VM_GROWSDOWN;// 1、 VMA对象可以向低地址扩展,0、 否。
vm_flags.bit9 VM_LOCKED; // 1、 VMA对象锁着的、也不能交换出去,0、 否。
vm_flags.bit10 VM_IO; // 1、 VMA对象映射设备的I/O地址空间或类似,0、 否。
vm_flags.bit11 VM_FVO; // 1、 VMA对象只是一个关联着文件磁盘空间映射的FVO流容器对象,vm_len为FVO流容器对象的记录数、或index在FVO流容器内的偏移,0、 否。
vm_flags.bit12 VM_KERNEL; // 1、 VMA对象是内核对象、连续物理页内存、直接映射,0、 否。
vm_flags.bit13 VM_DONTCOPY; // 1、 fork创建新进程时、不复制VMA对象,0、 否。
vm_flags.bit14 VM_DONTEXPAND; // 1、 通过系统调用mremap()禁止VMA对象扩展,0、 否。
vm_flags.bit15 VM_PAGE_FILE; // 1、 VMA对象的FVO流容器对象的页是非线性映射,0、 否。
// 进程对关联文件的状态标志:
vm_flags.[17:16] VM_O_ACCMODE; // 00、要求读权限,01、要求写权限,10、要求读写权限,11、要求特殊权限。
vm_flags.bit18 VM_O_CREAT; // 1、 当该文件不存在时创建并打开此文件,0、 否。
vm_flags.bit19 VM_O_EXCL; // 1、 O_CREAT同时为1,当指定的文件已经存在时open失败,保证不破坏已存在的文件。0、 否。
vm_flags.bit20 VM_O_NOCTTY; // 1、 pathname所指不是终端,0、 否。
vm_flags.bit21 VM_O_TRUNC; // 1、 截断文件为零长度,这一选项只对普通文件有用,对诸如目录或FIFO之类的特殊文件无用。0、 否。
vm_flags.bit22 VM_O_APPEND; // 1、 所有write()操作写数据至文件尾而不管文件位置在何处,0、 否。
vm_flags.bit23 VM_O_NONBLOCK; // 1、 非阻塞I/O方式打开文件,0、 否。
vm_flags.bit24 VM_O_SYNC; // 1、 同步I/O方式写文件,导致任何写该文件的操作都阻塞、直至相关保存操作完成。0、 否。
vm_flags.bit25 VM_O_RSYNC; // 1、 提供同步的I/O数据完整性,等待写完成后才读。0、 否。
vm_flags.bit26 VM_O_DIRECT; // 1、 无缓冲I/O方式,0、 否。
vm_flags.bit27 VM_O_DIRECTORY; // 1、 pathname必须是目录、否则失败,0、 否。
vm_flags.bit28 VM_O_EOF; // 1、 报告文件结束,0、 否。
vm_flags.[31:29] VM_O_PROLE; // 进程对关联文件的角色:000、拥有者,001、root,010、root组管理员,011、组长,100、组管理员,101、组成员,110、指定者,111、游客。
4、用户进程的内存描述符mm的v节点(功能类同mm_struct)
用户进程的内存描述符mm虚拟设备v索引节点mm_inode结构管理进程的全部虚拟线性地址空间。mm_inode.mm_vnode(v节点信息)成员结构包含了对进程所有VMA对象数组的构造,AOS不用链表和红黑树;与文件关联的VMA对象(AOS文件VMA对象file_vma、类同linux的struct file),已经是分离到文件打开表fot的项,这里余下的只是其“不管状态”下的大约虚拟地址空间。
PGD页表共512项、每项512GB,用户进程的虚拟内存空间参考布局:
0--255项、 用户进程虚拟地址空间128TB:
0--127项: code段、data数据段、BSS、堆brk、用户堆栈、共64TB,
128--159项、用户进程虚拟内存地址空洞16TB,
160--191项、用户进程虚拟地址PVMA对象区域16TB,进程私有;
192--207项、用户进程虚拟内存地址空洞8TB,
208--223项、用户进程虚拟地址file_vma对象区域8TB,固定区域,进程私有;
224--239项、用户进程虚拟地址静态动态方法库区域8TB,固定区域,进程私有;
240--254项、用户进程虚拟内存地址空洞7.5TB,
255项、 用户进程的线程栈VMA对象512GB,固定区域。
256--511项、共享内核虚拟内存地址空间128TB:
256--271项、内核虚拟内存地址空洞8TB,
272--303项、内核虚拟内存地址空间16TB与物理内存的直接映射(4G页),固定区域;
304--319项、内核虚拟内存地址空洞8TB,
320--383项、内核虚拟内存地址KVMA对象区域32TB,进程共享;
384--399项、内核虚拟内存地址空洞8TB,
400--463项、内核虚拟地址file_vma对象区域32TB,固定区域,进程共享;
464--479项、内核虚拟内存地址空洞8TB,
480--511项、内核映射、静态动态公共方法库区域共16TB,固定区域。
struct mm_vnode { // mm的v节点信息结构32字节字段。
u16 mm_no; // 本mm的v节点号。
u16 mm_pno; // 所属父parent用户进程的mm的v节点号。
u32 mm_pgdno; // 用户进程全局目录PGD页表的物理页号。
u16 mm_users; // mm的v节点引用记数。
u16 mm_count; // mm的v节点“匿名使用者”引用记数。
u32 mm_nr_ptes; // 用户进程的页表数,PGD、PUD、PMD、PTE、的页表物理页总数。
u32 mm_pbuf; // VMA对象第二缓冲区2页,32k个VMA对象位图、物理页号虚拟页号和标志及空闲数(4w),用户和内核各半。
u32 mm_pfbuf; // VMA对象第三缓冲区1页--256页,最大32k个VMA对象项。用户和内核各半。
u16 vma_count; // VMA对象个数。
u16 file_vma_count; // file_vma文件映射VMA对象个数(文件描述符fd数量)。
u32 mm_ops; // mm的方法表号。
};
struct mm_inode { // 用户进程mm虚拟设备v索引节点结构。
u16 mm_ono; // 16位兄(左)older sibling进程v节点号之mm的v节点。在64K范围内。
u16 mm_yno; // 16位弟younger sibling(右)进程v节点号之mm的v节点。
u16 p_pid; // 线程组TGID号或说进程号pid或说线程组的主线程号。
u16 p_tid; // 当前运行的线程TID号。
u32 mm_flags; // mm的访问标志。
u32 mm_state; // 用户进程mm状态。
u64 start_pvma; // 用户进程虚拟地址PVMA私有对象区域开始。
u64 end_pvma; // 用户进程虚拟地址PVMA私有对象区域结束。
struct mm_vnode m_vnode; // v节点信息结构、32字节。
u64 start_kvma; // 内核虚拟内存地址共享KVMA对象区域开始。
u64 end_kvma; // 内核虚拟内存地址共享KVMA对象区域结束。
u64 task_size; // size of task vm space,用户空间与内核空间的分界线。
u64 hiwater_rss;// 进程所拥有的最大物理页数。
u64 hiwater_vm; // 进程线性区中最大页数。
u64 total_vm; // 进程虚拟地址空间的大小(页数)、总共映射的页数。
u32 mm_pbuf; // 用户进程mm的第一缓存页,saved_auxv[AT_VECTOR_SIZE]、等等。
u32 def_flags; // 进程虚拟地址空间线性区vm默认的访问标志。
u64 locked_vm; // 进程虚拟地址空间锁住而不能换出的页数。
u64 shared_vm; // 进程虚拟地址空间共享文件内存映射中的页数。
u64 reserved_vm;// 进程虚拟地址空间在保留区中的页数或者在特殊线性区中的页数。
u64 exec_vm; // VM_EXEC & ~VM_WRITE & ~VM_STACK,存放可执行文件的页数。
u64 pinned_vm; // 进程虚拟地址空间既不能被换出也不能被移动的固定页数。
u64 start_code; // 可执行代码的起始地址。
u64 end_code; // 可执行代码的最后地址。
u64 start_data; // 已初始化数据的起始地址。
u64 end_data; // 已初始化数据的最后地址。
u64 start_brk; // 堆的起始位置。
u64 brk; // 堆的当前的最后地址。
u64 start_stack;// 用户堆栈的起始地址。
u32 stack_vm; // 用户堆栈中的有效页数。
u32 stack_ptrs; // 用户堆栈中的实际分配物理页数。
u64 arg_start; // 命令行参数的起始地址。
u64 arg_end; // 命令行参数的最后地址。
u64 env_start; // 环境变量的起始地址。
u64 env_end; // 环境变量的最后地址。
};
二、tadm时间管理员之CPU调度
内核容器管理大部分系统资源,最好是有5个CPU核来参与管理;划分为木火土金水五类系统资源、相应有5个AOS内核线程CPU来管理,或说划分为:
⚫ 生发管理员Birth administrator(节点管理、树状结构、分配行为、等等动态管理),Badm、肝木血藏魂,主生发,为创建、“生血”、节点树;
⚫ 时间管理员Time Administrator(管理CPU资源),Tadm、心火藏神,为调度(运)、为“动力”,为定时器、时间线;
⚫ 空间管理员Space Administrator(物理内存空间、磁盘空间、虚拟内存空间、等等静态资源),Sadm、脾土肉藏意,为内存、闪存、磁盘、等等空间及其所藏之血,文件、数据、代码(方法库)、为之“血”,血肉相连啊,血的依靠为肉、肉为静阴、血动为阳,一切之“意”不就都在血肉之中;
⚫ 界面管理员Interface administrator(包含输入输出设备、释放行为等等),Iadm、肺金气藏魄,主气息出入界面。文件数据流、输入输出消息流、外部控制消息流,三者为外内交换的“气息”,在内部类比为“血”的流动;
⚫ 通信管理员Communications administrator(进程间通信IPC、通信设备、人机和机机交流),Cadm、肾水精藏志,为进程间的中断消息通信IPC(Internal Process Communication)。指令即是“精”、是“水”,气血的流动终是消息通信从而指令执行的结果。志既是“七情六欲”,
七情:喜、怒、忧、思、悲、恐、惊,
六欲:眼、耳、鼻、舌、身、意、的生理需求或愿望,意欲:爱、恨、恶、伤。
万物的一生:魂气生、而神主运、而意动,气血流而志显现,情迷荡漾终而金气杀。
木火属阳而金水土属阴。
七情六欲是无法压制的,天生与本能、谁能完全禁止?古今中外,谁能压制?越是压制、反弹越大,往往“堂皇之下、多奸诈”,引出更大的“恶”。几千年来、有许多人想法通过修炼来“理性压制”,也就是“禁欲”;而最终结果不外是“跳出社会”中,“无所事事”一生、也就是所谓的“成仙”;呵呵,“你我”大多是“凡人”啊。法律可以约束和部分压制“欲念”,但更多的是依靠“道德”,而最本源的“基石”一定是“你情我愿”。因为“这样”,然后要“那样”,之后“热血”、要“秋后算账”、要“不顾后果”,但这是建立在“自己聪明、别人愚蠢”的基础上,还是理性的多想想“各种可能性的结果”吧。现在信息化的社会,“蠢人”会越来越少,会被“当枪使”的又有多少?那些自以为聪明的、喜欢“博懵、算计、城府”的渣人啊,“衰气冲天”、在消耗自己及其下一代的“福运”,终会是受到“反噬”;要知道,人在做、天在看。
五行CPU在二进制体系AOS中不会担任太多的工作,其主要是应用于三进制体系BOS、以及BOS之上的虚拟生命体系COS。
tadm时间管理员,是一个由独立CPU运行的五行内核线程;它管理CPU资源,试图让所有用户进程公平地使用CPU。想均衡每个用户CPU的负载,需要知道每个用户进程的线程数量,从而知道每个CPU管理的用户进程之总线程数量;新建一个进程,安排到哪一个CPU下,就需要依据“总线程数量”参数。tadm不做用户进程线程迁移的功能实现,一个用户进程线程的开始终结总是在为其分配的CPU上实现。一个对称多处理SMP的CPU节点最多不到256个,一个大系统允许tadm管理最大64K个CPU节点(可有最多256个SMP大节点、视硬件定,也许每SMP有16个用户CPU、可以有4K个SMP)。
struct tadm_inode { // tadm时间管理员CPU虚拟设备v节点树结构。
u32 t_cno; // 待定,BOS/COS使用。
u32 t_version; // 待定,BOS/COS使用。
u32 t_flags; // tadm时间管理员标志。待定,大部分标志是BOS/COS使用。
u32 t_state; // tadm时间管理员状态。待定,大部分标志是BOS/COS使用。
u32 t_start; // 用户CPU队列cql(cpu queue list、每项半字为用户cpu的v节点号)的连续物理内存页开始地址,1--32页。
u16 t_len; // 用户CPU队列cql项数,最大管理64K个用户CPU。
u16 t_mode; // 用户CPU类型和和访问控制模式。
u32 t_ktbm; // tadm内核线程位图Kernel thread bitmap(最大32个tadm下属的内核子线程,为软中断、工作队列、tasklet)。
u32 t_fno; // tadm相关联的磁盘描述文件v节点号。
struct cpu_vnode t_vnode; // tadm的cpu设备v节点信息结构、32字节。
struct dtimestamps t_times; // tadm设备时间戳(Timestamps)字段结构、2*12字节。
struct tdev t_pdata; // tadm的特殊私有数据区、2*12字节。待定,BOS/COS使用。
u32 t_pbuf; // 设备第一缓冲区页开始地址(页数看t_len而定),用户CPU队列cql的总线程数1字值数组,1--64页。
u48 *u_stack; // 待定,BOS/COS使用。
u48 *u_cstack; // tadm时间管理员CPU内核栈。
u16 t_pdata[64];// 最大64用户CPU队列cql项,超过64项时,t_start指针才有效;t_len决定了t_start的页数,2k项/页。
};
struct cpu_vnode { // cpu虚拟设备v节点信息结构32字节段。
u16 v_no; // 本CPU的v节点号。
u16 v_pno; // 所属父parent用户CPU的v节点号。
u16 v_rmc; // cpu虚拟设备消息接收数(receiving message count)。
u16 v_msc; // cpu虚拟设备消息发送数(messages sent count)。
u16 v_count; // 引用记数。
u16 v_wcount; // 写者记数或其它参数。
u32 v_name; // v节点的FNT表文件名字索引或PNVT表设备目录名字索引,从FNIT表中寻找到的文件名字是安装到FNT文件目录名字表。
u32 v_rmbuf; // cpu虚拟设备消息接收(Receiving message)缓冲页 ,或设备第二缓冲区起始页号。
u32 v_msbuf; // cpu虚拟设备消息发送(messages sent)缓冲页 ,或设备第三缓冲区起始页号。
u32 file_vma_no; // 待定,BOS/COS使用。
u32 v_ops; // cpu虚拟设备的消息处理方法表号。
};
单CPU内核层:管理一个用户进程队列。每个CPU拥有私有的一级Cache(高速缓存L1-I、L1-D)、二级Cache高速缓存L2,CPU对L1和L2的读写操作是最快的;每个CPU在L1或L2中建立一个内核栈(4kb),是最为合理和快速的。所以,一个用户进程队列共用其所属CPU的4kb内核栈;当CPU节点在用户进程空间接收到IPC消息包而中断时,CPU保存用户进程的线程之上下文到相应的用户进程之线程栈(用户进程最少有一个主线程,每个用户线程栈通常是8Mb虚拟内存空间),切换栈指针为CPU内核栈从而进入CPU内核空间处理IPC消息包;CPU在内核空间处理IPC消息包时,同样可以自动接收更多的IPC消息包、但不会是可重入中断处理,CPU的L2有4kb的IPC消息包循环接收缓冲区。中断上半步快速处理所有消息,在处理完所有中断消息后、用户CPU处理中断下半部(32个为用户CPU所属的内核线程、软中断、工作队列、tasklet),最后调度(抢占式调度)最高的动态优先级之进程的线程,从而回到相应用户进程的线程空间运行、等等。当用户进程的线程阻塞(通常是中断发送IPC消息包)、让步另外的进程线程时,或者滴答时间(通常是10ms)到的广播中断,用户CPU都要重新运行调度程序。CPU进入内核空间处理所有IPC消息包完成后,才会清中断,在准备进入用户进程空间时、还需再看一下是否有新的IPC消息包中断。描述单CPU的、是“单CPU虚拟设备”v节点,主要功能有:用户进程线程的调度、进程线程动态优先级计算,中断消息包上半部处理,单CPU的32个内核线程(软中断、工作队列、tasklet、调度)、处理中断下半部,调度是根据进程队列中的最大动态优先级值的进程来进行。
struct cpu_inode { // 用户CPU虚拟设备v节点树结构。
u16 u_ono; // 16位兄(左)older sibling用户CPU的v节点号。兄弟brothers用户CPU虚拟设备的v节点号,在256范围内。
u16 u_yno; // 16位弟younger sibling(右)用户CPU的v节点号。
u16 u_pid; // 当前运行的用户进程号pid或说线程组的主线程号。
u16 u_tid; // 当前运行的用户进程之线程TID号。
u32 u_flags; // 用户CPU运行标志。
u32 u_state; // 用户CPU运行状态。
u32 u_start; // 用户进程队列pql(process queue list、每项1字为进程pcb的v节点号)缓冲页开始地址,1--32页。
u16 u_len; // 用户进程队列项数,最大管理32K个进程。
u16 u_mode; // cpu类型和和访问控制模式。
u32 u_ktbm; // 用户CPU内核线程位图Kernel thread bitmap(32个为用户CPU所属的内核线程、软中断、工作队列、tasklet)。
u32 u_dfno; // 用户CPU描述文件description file的v节点号标识。
struct cpu_vnode u_vnode; // 用户CPU的 v节点信息结构、32字节。内核线程组描述。
struct dtimestamps u_times; // 用户CPU设备时间戳(Timestamps)字段结构、2*12字节。
u32 u_tcnt; // 用户进程队列的总线程数Thread count。
u32 u_rtcnt;// 用户进程队列的总就绪线程数Ready thread count。
u32 p_jffs_no; // 日志文件系统JFFS的v节点。
u64 ? // 待定。
u32 ? // 待定。
u32 u_pbuf; // 用户CPU第一缓冲区(1页4扇区4kb、到8页,看u_len定)开始地址,用户进程队列的线程最大就绪动态优先级数组。
u48 *u_stack; // 当前运行的用户进程(主线程)栈(当前缓冲页)。
u48 *u_cstack; // 用户CPU内核栈。
u32 d_pdata[32];// 最大32用户CPU进程队列pql项,超过32项时,u_start指针才有效;u_len决定了u_start的页数,1k项/页。
};
struct cpu_vnode { // cpu虚拟设备v节点信息结构32字节字段。
u16 v_no; // 本用户CPU的v节点号。当它终止或暂停时,必须向它的父进程发送IPC消息包信号。和cpu号1b
u16 v_pno; // 所属父parent用户CPU的v节点号(时间管理员)。
u16 v_rmc; // cpu虚拟设备消息接收数(receiving message count)。
u16 v_msc; // cpu虚拟设备消息发送数(messages sent count)。
u16 v_count;// 用户CPU的引用记数。
u16 v_wcount; // 用户CPU的写者记数。?待定。
u32 v_name; // 用户CPU的v节点FNT表文件名字索引,从FNIT表中寻找到的文件名字是安装到FNT文件目录名字表。
u32 v_rmbuf;// 用户cpu虚拟设备消息接收(Receiving message)缓冲页 ,或设备第二缓冲区起始页号。
u32 v_msbuf;// 用户cpu虚拟设备消息发送(messages sent)缓冲页 ,或设备第三缓冲区起始页号。
u32 *v_security; // 用户CPU的安全模块。?待定。
u32 v_ops; // 用户cpu虚拟设备的消息处理方法表号。
};
PCB(Process Control Block)进程控制块结构、linux为task_struct。
struct pcb_inode { // processes virtual device进程虚拟设备v节点树结构。
u16 p_ono; // 16位兄(左)older sibling进程v节点号。兄弟brothers进程虚拟设备的v节点号,在64K范围内。
u16 p_yno; // 16位弟younger sibling(右)进程v节点号。
u16 p_pid; // 线程组TGID号或说进程号pid或说线程组的主线程号。
u16 p_tid; // 当前运行的线程TID号。
u32 p_flags; // 进程标志。
u32 p_state; // 进程状态。
u32 p_start; // 进程文件描述符表fdt(每项1字指向文件打开表项fot)缓冲区(通常是1页)开始地址页号,1kw、最大64kw。
u16 p_len; // 进程文件描述符表项数。
u16 p_mode; // 进程文件类型和和访问权限控制。?
u32 p_envv; // 用户环境文件v节点。用户ID、名字、所属组ID数组、工作和根目录、主组(会话SID、会话进程组PGID)、用户磁盘限额、等
u32 p_pvno; // 进程所属可执行程序procedure文件的v节点号标识。
struct pcb_vnode p_vnode; // v节点信息结构、32字节。线程组描述。
struct dtimestamps p_times; // 进程设备时间戳(Timestamps)字段结构、2*12字节。
u16 p_tcnt; // 进程的线程数Thread count。
u16 p_rtcnt; // 进程的就绪线程数Ready thread count。
u32 p_kcnt; // 进程的内核空间运行次数。
u32 p_ucnt; // 进程的用户空间运行次数。
u16 mma_no; // 指向上一个用户进程虚拟内存空间mm的v节点。
u16 mm_no; // 指向当前用户进程虚拟内存空间mm的v节点。
u32 p_ptrace_flags; // 进程追踪标志。
u32?; // 待定。
u32 p_pbuf; // 指向用户进程第一缓冲区(1页4扇区4kb、到2页,看p_len定)开始地址页号,进程文件描述符表fdt项的位图。
u48 *p_stack; // 用户进程(主线程)栈(分配缓冲区1页)。
u48 *p_cstack; // 用户CPU内核栈。
u32 d_pdata[32];// 最大32个进程文件描述符表fdt项,超过32项时为1k位图变量、最大表示1k个进程文件描述符表fdt项,p_start指针有效。超过1k项时、p_pbuf指针有效所指为1页,超过33k项时、p_pbuf所指为2页,p_start所指大于33页。
};
struct pcb_vnode { // 进程虚拟设备v节点信息结构32字节字段。
u16 v_no; // 本用户进程v节点号。当它终止或暂停时,必须向它的父进程发送IPC消息包信号。
u16 v_pno; // 所属父parent用户进程的v节点号。如果创建它的父进程不再存在,则指向pid为1的init进程v节点号。
u32 v_pgdno; // 用户进程页全局目录PGD页。
u16 v_count; // 用户进程的引用记数。
u16 v_wcount; // 用户进程的写者记数。
u32 v_name; // 用户进程v节点FNT表文件名字索引,从FNIT表中寻找到的文件名字是安装到FNT文件目录名字表。
u32 v_pbuf; // 用户进程第二缓冲区1页,128*4b共128线程小组描述符的页缓冲区号、小组最大就绪动态优先级和栈基址,其它待规划。
u32 v_pfbuf; // 用户进程第三缓冲区1页,512个线程描述符的小组:线程栈指针512*3b(8MB(连续2k页)/每线程),512*5b:线程标志1b,线程状态1b、静态优先级1b,动态优先级1b、就绪动态优先级1b。主组线程池。
u32 file_vma_no; // 待定。
u32 v_ops; // 进程虚拟设备的消息处理方法表号。
};
待续...