You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
16 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 64 | 知识串讲:用一个创业故事串起操作系统原理(三)
上一节我们说到周瑜和张昭商定了调用schedule的时机。尽管项目越来越多但是也井井有条。可是我们也说了不管你的事情做得有多好项目保密问题都是要解决的重要问题。怎么解决呢今天我们就来看一看。
## 保密需封闭开发,空间小巧妙安排
慢慢地小马发现项目接的多了之后CPU小伙伴的任务调度问题解决了之后会议室的使用经常陷入混乱。不同的项目使用会议室的时候经常冲突一个项目组没用完另一个项目组就在那里等着十分耽误开发效率。
小马说:“要不咱们的项目别用会议室封闭开发了,原来总是说封闭开发,就是为了隔离,保密。这对于公司声誉来说很重要,但是能不能通过签订保密协议的方式来,干嘛非得封闭开发呢?”
周瑜说:“马哥,以我在大公司管理项目的经验来看,您还是想简单了。”
“你看每次你接一个项目总要写成项目执行计划书CPU小伙伴们才能执行吧项目计划书中的一行一行指令运行过程中免不了要产生一些数据。这些数据要保存在一个地方这个地方就是会议室内存。会议室内存被分成一块一块儿的都编好了号。例如3F-10就是三楼十号会议室。这个地址是实实在在的地址通过这个地址我们就能够定位到物理内存的位置。”
“现在问题来了,写项目执行计划书的时候,里面的指令使用的地址是否可以使用物理地址呢?当然不行了,项目执行计划书,都是事先写好的,可以多次运行的。如果里面有个指令是,要把用户输入的数字保存在内存中,那就会有问题。”
“会产生什么问题呢我举个例子你就明白了。如果我们使用那个实实在在的地址3F-10打开三个相同的程序都执行到某一步。比方说打开了三个计算器用户在这三个程序的界面上分别输入了10、100、1000。如果内存中的这个位置只能保存一个数那应该保存哪个呢这不就冲突了吗
“如果不用这个实实在在的地址,那应该怎么办呢?那就必须用封闭开发的办法。
每个项目的物理地址对于进程不可见谁也不能直接访问这个物理地址。操作系统会给进程分配一个虚拟地址。所有进程看到的这个地址都是一样的里面的内存都是从0开始编号。
在程序里面指令写入的地址是虚拟地址。例如位置为10M的内存区域操作系统会提供一种机制将不同进程的虚拟地址和内存的物理地址映射起来。
当程序要访问虚拟地址的时候,由内核的数据结构进行转换,转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,就不会冲突了。”
小马想想,对啊,这是个好办法,咱们得规划一套会议室管理系统(内存管理)。根据刚才的分析,这个系统应该包含以下三个部分:
第一,物理内存的管理,相当于会议室管理员管理会议室;
第二,虚拟地址的管理,也即在项目组的视角,会议室的虚拟地址应该如何组织;
第三,虚拟地址和物理地址如何映射的问题,也即会议室管理员如果管理映射表。
我们先来盘点一下物理内存的情况。
![](https://static001.geekbang.org/resource/image/8f/49/8f158f58dda94ec04b26200073e15449.jpeg)
不同的园区工位的安排和会议室的布局各不相同。
第一种情况是CPU小伙伴们坐在一起会议室在楼层的另一面大家到会议室里面去都要通过统一的过道优点简单缺点是通道会成为瓶颈。
第二种情况是会议室分成多个节点离散地分布在CPU小伙伴周围。有的小伙伴离这个会议室近一些有的小伙伴离另外一些会议室近一些。这样做的优点是如果CPU小伙伴干活总是能够去离他最近的会议室则速度非常快但是一旦离他最近的会议室被占用了他只能去其他会议室这样就比较远了。
现在的园区基本都设计成第二种样子也即会议室内存要分节点每个节点用struct pglist\_data表示。
每个节点里面再分区域用于区分内存不同部分的不同用法。ZONE\_NORMAL是最常用的区域。ZONE\_MOVABLE是可移动区域。我们通过将物理内存划分为可移动分配区域和不可移动分配区域来避免内存碎片。每个区域用struct zone表示也放在一个数组里面。
每个区域里面再分页。默认的大小为4KB。这就相当于每个会议室的最小单位。
如果有项目要使用会议室应该如何分配呢不能任何项目来了咱都给他整个会议室。会议室也是可以再分割的例如在中间拼起一堵墙这样一个会议室就可以分成两个继续分可以再分成四个1/4大小的会议室直到不能再分我们就能得到一页的大小。
物理页面分配的时候,也可以采取这样的思路,我们称为伙伴系统。
空闲页放在struct free\_area里面每一页用struct page表示。
把所有的空闲页分组为11个页块链表每个块链表分别包含很多个大小的页块有1、2、4、8、16、32、64、128、256、512和1024个连续页的页块。最大可以申请1024个连续页对应4MB大小的连续内存。每个页块的第一个页的物理地址是该页块大小的整数倍。
![](https://static001.geekbang.org/resource/image/3f/4f/3fa8123990e5ae2c86859f70a8351f4f.jpeg)
例如要请求一个128个页的页块时我们要先检查128个页的页块链表是否有空闲块。如果没有则查256个页的页块链表如果有空闲块的话则将256个页的页块分成两份一份使用一份插入128个页的页块链表中。如果还是没有就查512个页的页块链表如果有的话就分裂为128、128、256三个页块一个128的使用剩余两个插入对应页块链表。
把物理页面分成一块一块大小相同的页这样带来的另一个好处是当有的内存页面长时间不用了可以暂时写到硬盘上我们称为换出。一旦需要的时候再加载进来就叫作换入。这样可以扩大可用物理内存的大小提高物理内存的利用率。在内核里面也即张昭的管理下有一个进程kswapd可以根据物理页面的使用情况对页面进行换入换出。
小马觉得这种方式太好了,如此高效地使用会议室,公司不用租用多少会议室,就能解决当前的项目问题了。
## 会议室排列有序,分视角各有洞天
周瑜说,“你先别急,这还仅仅是会议室物理地址的管理,每一个项目组能够看到的虚拟地址,咱还没规划呢!这个规划不好,执行项目还是会有问题的。”
每个项目组能看到的虚拟地址怎么规划呢?我们要给项目组这样一种感觉,从项目组的角度,也即从虚的角度来看,这一大片连续的内存空间都是他们的了。
如果是32位有2^32 = 4G的内存空间都是他们的不管内存是不是真的有4G。如果是64位在x86\_64下面其实只使用了48位那也挺恐怖的。48位地址长度也就是对应了256TB的地址空间。
小马说“我都没怎么见过256T的硬盘别说是内存了。”
周瑜接着说:“现在,一个项目组觉得,会议室可比世界首富房子还大。虽然是虚拟的,下面尽情地去排列咱们要放的东西吧!请记住,现在我们是站在一个进程的角度,去看这个虚拟的空间,不用管其他进程。”
首先这么大的虚拟空间一切二一部分用来放内核的东西称为内核空间一部分用来放进程的东西称为用户空间。用户空间在下在低地址我们假设是0号到29号会议室内核空间在上在高地址我们假设是30号到39号会议室。这两部分空间的分界线因为32位和64位的不同而不同我们这里不深究。
对于普通进程来说内核空间的那部分虽然虚拟地址在那里但是不能访问。这就像作为普通员工你明明知道财务办公室在这个30号会议室门里面但是门上挂着“闲人免进”你只能在自己的用户空间里面折腾。
![](https://static001.geekbang.org/resource/image/af/83/afa4beefd380effefb0e54a8d9345c83.jpeg)
我们从最低位开始排起先是Text Segment、Data Segment和BSS Segment。Text Segment是存放二进制可执行代码的位置Data Segment存放静态常量BSS Segment存放未初始化的静态变量。这些都是在项目执行计划书里面有的。
接下来是堆段。堆是往高地址增长的是用来动态分配内存的区域malloc就是在这里面分配的。
接下来的区域是Memory Mapping Segment。这块地址可以用来把文件映射进内存用的如果二进制的执行文件依赖于某个动态链接库就是在这个区域里面将so文件映射到了内存中。
再下面就是栈地址段了,主线程的函数调用的函数栈就是用这里的。
如果普通进程还想进一步访问内核空间,是没办法的,只能眼巴巴地看着。如果需要进行更高权限的工作,就需要调用系统调用,进入内核。
一旦进入了内核就换了一副视角。刚才是普通进程的视角觉着整个空间是它独占的没有其他进程存在。当然另一个进程也这样认为因为它们互相看不到对方。这也就是说不同进程的0号到29号会议室放的东西都不一样。
但是到了内核里面无论是从哪个进程进来的看到的是同一个内核空间看到的是同一个进程列表。虽然内核栈是各用各的但是如果想知道的话还是能够知道每个进程的内核栈在哪里的。所以如果要访问一些公共的数据结构需要进行锁保护。也就是说不同的进程进入到内核后进入的30号到39号会议室是同一批会议室。
![](https://static001.geekbang.org/resource/image/4e/9d/4ed91c744220d8b4298237d2ab2eda9d.jpeg)
内核的代码访问内核的数据结构大部分的情况下都是使用虚拟地址的。虽然内核代码权限很大但是能够使用的虚拟地址范围也只能在内核空间也即内核代码访问内核数据结构只能用30号到39号这些编号不能用0到29号因为这些是被进程空间占用的。而且进程有很多个。你现在在内核但是你不知道当前指的0号是哪个进程的0号。
在内核里面也会有内核的代码同样有Text Segment、Data Segment和BSS Segment内核代码也是ELF格式的。
不过有了这个规定以后,项目执行计划书要写入数据的时候,就需要符合里面的规定了,数据不能随便乱放了。
小马说,“没问题,这个作为项目章程,每一个新员工来了都培训。”
## 管理系统全搞定,至此生存无问题
周瑜接着说:“物理会议室和虚拟空间都分成大小相同的页,我们还得有一个会议室管理系统,将两者关联起来,这样项目组申请会议室的时候,也有个系统可以统一的管理,要不然会议室还不得老冲突呀。”
对于虚拟内存的访问,也是有一个地址的,我们需要找到一种策略,实现从虚拟地址到物理地址的转换。
为了能够定位和访问每个页,需要有个页表,保存每个页的起始地址,再加上在页内的偏移量,组成线性地址,就能对于内存中的每个位置进行访问了。
![](https://static001.geekbang.org/resource/image/ab/40/abbcafe962d93fac976aa26b7fcb7440.jpg)
虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址。这个基地址与页内偏移的组合就形成了物理内存地址。
下面的图,举了一个简单的页表的例子,虚拟内存中的页通过页表映射对应到物理内存中的页。
![](https://static001.geekbang.org/resource/image/83/c3/83a5de160088a2e23e7c1a76c013efc3.jpg)
32位环境下虚拟地址空间共4GB。如果分成4KB一个页那就是1M个页。每个页表项需要4个字节来存储那么整个4GB空间的映射就需要4MB的内存来存储映射表。如果每个进程都有自己的映射表100个进程就需要400MB的内存。对于内核来讲有点大了 。
页表中所有页表项必须提前建好,并且要求是连续的。如果不连续,就没有办法通过虚拟地址里面的页号找到对应的页表项了。
那怎么办呢我们可以试着将页表再分页4G的空间需要4M的页表来存储映射。我们把这4M分成1K1024个4K每个4K又能放在一页里面这样1K个4K就是1K个页这1K个页也需要一个表进行管理我们称为页目录表这个页目录表里面有1K项每项4个字节页目录表大小也是4K。
页目录有1K项用10位就可以表示访问页目录的哪一项。这一项其实对应的是一整页的页表项也即4K的页表项。每个页表项也是4个字节因而一整页的页表项是1k个。再用10位就可以表示访问页表项的哪一项页表项中的一项对应的就是一个页是存放数据的页这个页的大小是4K用12位可以定位这个页内的任何一个位置。
这样加起来正好32位也就是用前10位定位到页目录表中的一项。将这一项对应的页表取出来共1k项再用中间10位定位到页表中的一项将这一项对应的存放数据的页取出来再用最后12位定位到页中的具体位置访问数据。
![](https://static001.geekbang.org/resource/image/b6/b8/b6960eb0a7eea008d33f8e0c4facc8b8.jpg)
你可能会问如果这样的话映射4GB地址空间就需要4MB+4KB的内存这样不是更大了吗 当然如果页是满的,当时是更大了,但是,我们往往不会为一个进程分配那么多内存。
比如说上面图中我们假设只给这个进程分配了一个数据页。如果只使用页表也需要完整的1M个页表项共4M的内存但是如果使用了页目录页目录需要1K个全部分配占用内存4K但是里面只有一项使用了。到了页表项只需要分配能够管理那个数据页的页表项页就可以了也就是说最多4K这样内存就节省多了。
当然对于64位的系统两级肯定不够了就变成了四级目录分别是全局页目录项PGDPage Global Directory、上层页目录项PUDPage Upper Directory、中间页目录项PMDPage Middle Directory和页表项PTEPage Table Entry
![](https://static001.geekbang.org/resource/image/42/0b/42eff3e7574ac8ce2501210e25cd2c0b.jpg)
设计完毕会议室管理系统再加上前面的项目管理系统对于一家外包公司来讲无论接什么样的项目都能轻松搞定了。我们常把CPU和内存合称为计算。至此计算的问题就算搞定了。解决了这两大问题一家外包公司的生存问题就算解决了。
小马总算是可以松一口气了,他和周瑜、张昭好好地搓了一顿,喝得昏天黑地。周瑜和张昭纷纷感慨,幸亏当年跟了马哥,今日才有出头之日。
生存问题虽然解决了,马哥可非池中之物,接下来要解决的就是发展问题,马哥能想出什么办法进一步壮大企业呢?欲知后事,且听下回分解。