注册 登录
编程论坛 Linux系统管理

Linux 0.11

madfrogme 发布于 2012-09-28 23:24, 3373 次点击
新学期开始,读 linux 0.11 代码注释,

不定期更新自己觉得有必要注意的东西,希望自己可以坚持

对于linux 0.11内核,系统设置全局描述符表 GDT 中的段描述符项数最大为256,

其中 2 项空闲,2 项系统使用,

每个进程使用两项。因此,此时系统可以最多容纳 (256-4)/2 + 1=127 个任务,

并且虚拟地址范围是 ((256-4)/2)* 64MB约等于 8G。

但0.11内核中人工定义最大任务数 NR_TASKS = 64 ,

每个进程虚拟地址(或线性地址)范围是 64M(2^26 字节),

并且各个进程的虚拟地址起始位置是(任务号-1)*64MB。

因此所使用的虚拟地址空间范围是64MB*64=4G.


只有本站会员才能查看附件,请 登录


[ 本帖最后由 madfrogme 于 2012-10-3 23:06 编辑 ]
49 回复
#2
madfrogme2012-09-28 23:48
页目录和页表的结构是一样的, 表项结构也相同。

页目录表中的每个表项(简称页目录项)(4字节)用来寻址一个页表,

而每个页表(4字节)用来指定一页物理内存页。

因此,当指定了一个页目录和一个页表项,

我们就可以唯一地确定所对应的物理内存页。

页目录表占用一页内存,因此最多可以寻址1024个页表。

而每个页表也同样占用一页内存,因此一个页表可以寻址最多1024个物理内存页面。

这样在80386中,一个页目录表所寻址的所有页表共可以寻址

1024 * 1024 * 4096 = 4G 的内存空间。 在linux 0.11 内核中,

所有进程都使用一个页目录表, 而每个进程都有自己的页表

只有本站会员才能查看附件,请 登录


[ 本帖最后由 madfrogme 于 2012-10-3 23:07 编辑 ]
#3
madfrogme2012-09-28 23:58
线性地址的位31-22 共10个比特用来确定页目录中的目录项,

位21 -12 用来 寻址页目录项指定的页表中的页表项,

最的12 个比特正好用作页表项指定的一页物理内存的偏移地址(2 ^12 = 4096)

只有本站会员才能查看附件,请 登录
#4
有容就大2012-09-29 00:46
并且虚拟地址范围是 ((256-4)/2)* 64MB约等于 8G。
只用4G 还有4G拿来干什么?
#5
有容就大2012-09-29 00:49
好好  那几张图太强悍了 对页表映射机制有了进一步的认识。
#6
madfrogme2012-09-29 01:12
以下是引用有容就大在2012-9-29 01:46:51的发言:

并且虚拟地址范围是 ((256-4)/2)* 64MB约等于 8G。
只用4G 还有4G拿来干什么?


剩下4G拿来干什么我暂时没法清楚的解释,但我感觉到的问题是

现在的linux内核每一个进程都能寻址4G的空间,

但0.11 的每个进程都只有64MB的寻址空间,

用一个4G的虚拟空间就能容得下64个进程且不互相打扰,这和现在的虚拟空间明显不同,只是我也解释不清楚

很明显当时的物理内存不会有那么大,所以只是一个对虚拟地址理解的问题了,我感觉。

互相学习

这几张图应该是基础,等我慢慢深入理解下去,尽量把细节也表现出来


[ 本帖最后由 madfrogme 于 2012-9-29 02:17 编辑 ]
#7
madfrogme2012-09-29 09:51
以下内容应该不局限在0.11内核, 地址转换发生在两处, 实模式中和保护模式中

实模式中
逻辑地址(logical address) : segment : offset
计算方式: segment address * 16 + offset
比如:
FFFF : 0001 则结果是 FFFF * 16 + 0001 = FFFF1

保护模式中
段寄存器(segmentation registers)
逻辑地址: segment identifier : offset
其中segment identifier 就是16位的 segment selector
offset 为 32位

可以从处理器的段寄存器(segment registers)中快速获得segment selector
总共有六个这样的寄存器
CS:code segment
DS : data segment
SS : stack segment
FS :
GS :
其中CS 寄存器有2 位 用来指定CPU 的 CPL (Current Privilege level),0到3
linux中使用0 表示内核模式, 3表示用户模式

只有本站会员才能查看附件,请 登录

其中13个位的index 是GDT/LDT 的入口(entry pointer)
所以说每个段寄存器(segment register)都会被映射到处理器中一个不可编程的寄存器(non programmable register )
从而把你引向段描述符(Segment descriptor)


段描述符( segment descriptor )
每个段(segment)都会通过8个字节的段描述符(segment descriptor)来表示
他们被存在GDT或LTD 中(Global descriptor table 和 Local descriptor table)
一般一个CPU有一个GDT, 但每个进程都会有自己的LDT


[ 本帖最后由 madfrogme 于 2012-9-29 11:08 编辑 ]
#8
madfrogme2012-09-29 10:28
迟迟不敢下手GDT,继续巩固周边概念
Segment
a logically contiguous chunk of memory with consistent properties (CPU's speaking)

Segment Register
a register of your CPU that refers to a segment for a special use (e.g. SS, CS, DS ...)

Selector
a reference to a descriptor you can load into a segment register;
the selector is an offset of a descriptor table entry.
These entries are 8 bytes long, therefore selectors can have values 0x00, 0x08, 0x10, 0x18, ...


Descriptor
a memory structure (part of a table) that tells the CPU the attributes of a given segment


[ 本帖最后由 madfrogme 于 2012-9-29 11:32 编辑 ]
#9
有容就大2012-09-29 11:13
现在才知道原来一个段寄存器有16 bit的可见部分 还有64 Bit的不可见部分
这些和segment selector / segment discription 有很大关系啊。。。
#10
有容就大2012-09-29 11:16
  全局描述表(GDT Global Descriptor Table)是一个数据结构?
是个数组吗?他的下标索引就是segment selector 的前13位吧 我这个理解有没错?


[ 本帖最后由 有容就大 于 2012-9-29 11:18 编辑 ]
#11
madfrogme2012-09-29 16:25
下面的内容不是对0.11内核的讨论。是对更一般的linux的讨论

在保护模式中,逻辑地址由一个16位的段选择子(Segment Selector) 和一个32位的偏移量(Offset)构成,

而段寄存器(Segmentation registers)只能存储16位的段选择子(Segment Selector).

所以为了加快从逻辑地址到线性地址的转换,80x86处理器提供了额外的不可编程(nonprogrammable)寄存器,

每个不可编程寄存器(nonprogrammable register)里面都是一个8字节的段描述符(Segment Descriptor),

这8字节的段描述符是由相应段寄存器(segmentation register)中的段选择子(Segment Selector)决定的。

每次当一个段选择子(Segment Selector)被 加载到一个段寄存器(segmentation register)中时,

相应的段描述符(Segment Descriptor)就会从内存中被加载到相应的不可编程(nonprogrammable)CPU寄存器中


之后,对段(Segment)的逻辑地址的转换工作就不需要依靠再去访问主内存中的GDT或是LDT了。

因为段描述符(Segment Descriptor)为8字节,

所以它在GDT或者LDT中的相当地址可以通过用 8 乘以 段选择子(Segment Selector)中的那13位来获得。

比如GDT的地址为0x00020000(GDT的地址存储在gdtr寄存器中)并且段选择子(Segment Selector)的那index 的13位 指定的值为2,

于是相应的段描述符的地址就是 0x00020000 + (2 * 8 ) , 结果是0x00020000。

GDT的第一个入口(first entry)永远都被设为0,这就保证了一个空的段选择子(null Segment Selector)会被认为是无效的,

并且引起 processor exception。 GDT中可以存储的最多 8191个 段描述符(Segment descriptors)因为, 2^13 -1 为 8191


只有本站会员才能查看附件,请 登录


[ 本帖最后由 madfrogme 于 2012-9-29 17:27 编辑 ]
#12
madfrogme2012-09-29 16:32
以下是引用有容就大在2012-9-29 12:16:09的发言:

  全局描述表(GDT Global Descriptor Table)是一个数据结构?
是个数组吗?他的下标索引就是segment selector 的前13位吧 我这个理解有没错?

对的,目有错,段描述符位置的计算方法 是 下标(比如2)* 8 再加上 gdtr 中的值,就可以算出来了,

至于具体是什么样的数据结构,我看晚上能不能把它写出来,我也是在一点一点摸索
#13
zklhp2012-09-29 16:35
还用看这个么 有专门的讲描述符表的书啊。。。
#14
madfrogme2012-09-29 17:30
以下是引用zklhp在2012-9-29 17:35:25的发言:

还用看这个么 有专门的讲描述符表的书啊。。。

我在这里写的都是从四面八方搜集来的我觉得有用的东西,总体说来也算是归纳起来,给自己理清思路用的。

这几天我会主要看地址转换,过几天也就换主题了,就是人们说的好记性不如烂笔头

[ 本帖最后由 madfrogme 于 2012-9-29 19:02 编辑 ]
#15
信箱有效2012-09-29 17:47
[ 本帖最后由 madfrogme 于 2012-9-29 18:32 编辑 ]

我这里怎么才17点50呢
#16
madfrogme2012-09-29 18:00
以下是引用信箱有效在2012-9-29 18:47:37的发言:

[ 本帖最后由 madfrogme 于 2012-9-29 18:32 编辑 ]

我这里怎么才17点50呢

真细心啊,我确实快一个小时
#17
有容就大2012-09-29 18:17
以下是引用madfrogme在2012-9-29 18:00:30的发言:

 
真细心啊,我确实快一个小时

还在日本岛吧
#18
有容就大2012-09-29 18:18
以下是引用madfrogme在2012-9-29 17:30:09的发言:

 
我在这里写的都是从四面八方搜集来的我觉得有用的东西,总体说来也算是归纳起来,给自己理清思路用的。
 
这几天我会主要看地址转换,过几天也就换主题了,就是人们说的好记性不如烂笔头

好记性不如烂笔头!
确实
发个帖子在论坛上很有用啊 像我现在做东西经常去翻自己的老贴 里面有很多东西讨论过 比较重要 但是记不住。。。
#19
信箱有效2012-09-29 18:49
勇敢接分
#20
madfrogme2012-09-29 20:18
以下是引用有容就大在2012-9-29 19:17:31的发言:


还在日本岛吧

暑假回国呆了40天,这个星期刚回来,苦逼了
#21
zklhp2012-09-29 20:37
以下是引用madfrogme在2012-9-29 20:18:10的发言:

 
暑假回国呆了40天,这个星期刚回来,苦逼了

日本は良い場所です
#22
madfrogme2012-09-29 20:57
以下是引用zklhp在2012-9-29 21:37:39的发言:


日本は良い場所です

Z版,なかなかやるんじゃないですか、^^
あしたひとり韓国の友達と一緒にあるおばあさんの家を訪ねに行くから、楽しみにしてる~
#23
madfrogme2012-09-29 21:05
只有本站会员才能查看附件,请 登录

一点一点向前进,先说23 位上的G,G是0还是1 和Segment Limit有很大关系

Segment Limit 加在一起有20位 (2^20 就是 1MB)

如果G为0,且Segment Limit 为 0xFFFFF, 则段(Segment)的大小为 1MB(这时的单位为字节

如果G为1, 且Segment Limit 为 0xFFFFF, 则段(Segment)的大小为 4GB(因为这时单位为4K
所以2^20 * 4K = 1MB * 1024 * 4 = 4GB

// This structure contains the value of one GDT entry.
// We use the attribute 'packed' to tell GCC not to change
// any of the alignment in the structure.
struct gdt_entry_struct
{
   u16int limit_low;           // The lower 16 bits of the limit.
   u16int base_low;            // The lower 16 bits of the base.
   u8int  base_middle;         // The next 8 bits of the base.
   u8int  access;              // Access flags, determine what ring this segment can be used in.
   u8int  granularity;
   u8int  base_high;           // The last 8 bits of the base.
} __attribute__((packed));
typedef struct gdt_entry_struct gdt_entry_t;


[ 本帖最后由 madfrogme 于 2012-10-10 17:22 编辑 ]
#24
有容就大2012-09-29 21:06
以下是引用zklhp在2012-9-29 21:37:39的发言:


日本は良い場所です

Z版,なかなかやるんじゃないですか、^^
あしたひとり韓国の友達と一緒にあるおばあさんの家を訪ねに行くから、楽しみにしてる~

学了一天日语的我 表示大汗淋漓  
#25
zklhp2012-09-29 21:10
以下是引用有容就大在2012-9-29 21:06:54的发言:


学了一天日语的我 表示大汗淋漓  

m大神说的是

z版 好好努力搬砖哦

明天我介绍你去韩国的朋友那里搬砖 好处大大的
#26
有容就大2012-09-29 21:10
以下是引用madfrogme在2012-9-29 21:05:07的发言:

 
一点一点向前进,先说23 位上的G,G是0还是1 和Segment Limit有很大关系  
 
Segment Limit 加在一起有20位 (2^20 就是 1MB)
 
如果G为0,且Segment Limit 为 0xFFFFF, 则段(Segment)的大小为 1MB(这时的单位为字节)
 
如果G为1, 且Segment Limit 为 0xFFFFF, 则段(Segment)的大小为 4GB(因为这时单位为4K)
所以2^20 * 4K = 1MB * 1024 * 4 = 4GB

我有一个问题 为什么segment discription 要把32位 base address拆分放在三个不同的地方?
#27
有容就大2012-09-29 21:11
以下是引用zklhp在2012-9-29 21:10:19的发言:


m大神说的是

z版 好好努力搬砖哦

明天我介绍你去韩国的朋友那里搬砖 好处大大的

gf ? bf?
#28
zklhp2012-09-29 21:13
以下是引用有容就大在2012-9-29 21:10:28的发言:


我有一个问题 为什么segment discription 要把32位 base address拆分放在三个不同的地方?

问的好。。

我觉得有历史的原因 286的时候实现了保护模式 但386的时候才实现了32位的保护模式罢 好像是这样具体我也忘了。。
#29
madfrogme2012-09-29 21:15
以下是引用有容就大在2012-9-29 22:11:33的发言:


gf ? bf?

我勒个去,你表太着急,慢慢来,我说明天和一个韩国人去一个老奶奶家拜访了!
晕了
#30
madfrogme2012-09-29 21:17
以下是引用zklhp在2012-9-29 22:13:48的发言:


问的好。。

我觉得有历史的原因 286的时候实现了保护模式 但386的时候才实现了32位的保护模式罢 好像是这样具体我也忘了。。

既然是历史原因,看来那就把它们看成是连续的,也不妨碍理解了
#31
zklhp2012-09-29 21:22
以下是引用madfrogme在2012-9-29 21:15:50的发言:

 
我勒个去,你表太着急,慢慢来,我说明天和一个韩国人去一个老奶奶家拜访了!
晕了

这就要去拜见高堂了 恭喜恭喜 记得回来散个分哦
#32
有容就大2012-09-29 21:32
好像286的时候 是24位起始物理地址 + 16位偏移量来寻址 还受限在 64KB中 应该还没有粒度这一说吧。
#33
有容就大2012-09-29 21:34
以下是引用madfrogme在2012-9-29 21:15:50的发言:


我勒个去,你表太着急,慢慢来,我说明天和一个韩国人去一个老奶奶家拜访了!
晕了

这就要去拜见高堂了 恭喜恭喜 记得回来散个分哦
记得一定要说:岳母大人在上 请受小婿一拜
#34
madfrogme2012-09-29 21:41
这里说的应该是80386吧 ,顺便问一下谁有没有 M.J.Bach 的《UNIX操作系统设计》这本书。

虽然很老,但想读一下

好吧,我在新浪的iask上找到了,不过质量堪忧


[ 本帖最后由 madfrogme 于 2012-9-29 22:44 编辑 ]
#35
zklhp2012-09-29 21:46
以下是引用madfrogme在2012-9-29 21:41:22的发言:

这里说的应该是80386吧 ,顺便问一下谁有没有 M.J.Bach 的《UNIX操作系统设计》这本书。
 
虽然很老,但想读一下
 
好吧,我在新浪的iask上找到了,不过质量堪忧

我只有《搬砖大全》。。

睡觉去了 明天早起搬砖 各位继续研究技术啊
#36
有容就大2012-09-29 21:50
回复 34楼 madfrogme
俺现在还玩不转OS 不怎么收集资料。。。
#37
有容就大2012-09-29 21:51
以下是引用zklhp在2012-9-29 21:46:26的发言:


我只有《搬砖大全》。。

睡觉去了 明天早起搬砖 各位继续研究技术啊
早睡早起 搬砖有劲儿
#38
pangding2012-10-01 09:14
没一直看完所有帖子。不过讨论内存模型的时候,有时得分清哪些是 linux 对内存模型的抽象。哪些是底层硬件对系统编程的要求。

我的概念是,GDT 之类的这些寻址机制,理解成 x86 架构的知识比较好。它不属于 linux 的范畴。在其它架构下运行的 Linux 可能完全不是这个样子的。书一般会就 x86 这个架构讲 Linux,讲的寻址都是硬件上的东西。换句话说,windows 要想用 x86 的处理器也得这么写,和 linux 没什么关系。
由于我学得也是 x86 架构下的 Linux,所以我也很难界定 Linux 和硬件之间的界线。但 linux 为了能在各种硬件平台上运行,这两者之间的界线应该是特别清晰的。linux 内核对内存有个很明确的模型抽象,然后底层就是在所有硬件上实现了内核要求的这个内存模型。Linux 的模块化做得很好,内存模块和其它模块的耦合性有限,所以这块的知识一般也非常容易拎出来单讲。一般就 x86 这个架构讲的书,往往也有模糊这个界线的倾向,把内核模型和硬件模型讲的水乳交融。不过我感觉应该不是这样的。
#39
madfrogme2012-10-01 19:55
以下是引用pangding在2012-10-1 10:14:10的发言:

没一直看完所有帖子。不过讨论内存模型的时候,有时得分清哪些是 linux 对内存模型的抽象。哪些是底层硬件对系统编程的要求。

我的概念是,GDT 之类的这些寻址机制,理解成 x86 架构的知识比较好。它不属于 linux 的范畴。在其它架构下运行的 Linux 可能完全不是这个样子的。书一般会就 x86 这个架构讲 Linux,讲的寻址都是硬件上的东西。换句话说,windows 要想用 x86 的处理器也得这么写,和 linux 没什么关系。
由于我学得也是 x86 架构下的 Linux,所以我也很难界定 Linux 和硬件之间的界线。但 linux 为了能在各种硬件平台上运行,这两者之间的界线应该是特别清晰的。linux 内核对内存有个很明确的模型抽象,然后底层就是在所有硬件上实现了内核要求的这个内存模型。Linux 的模块化做得很好,内存模块和其它模块的耦合性有限,所以这块的知识一般也非常容易拎出来单讲。一般就 x86 这个架构讲的书,往往也有模糊这个界线的倾向,把内核模型和硬件模型讲的水乳交融。不过我感觉应该不是这样的。

版主所言极是, 0.11内核好像无法在80286上运行,至少要是80386,不知道GDT这个东西,随着intel CPU ,比如 386 ,486, 奔腾,这样升级,是不是GDT也会有一些变化
#40
信箱有效2012-10-01 20:05
胖丁所言好象很有道理,我前天看了这个帖子,也有这个感觉。

前段时间看深入解析windows操作系统,在内存管理这块也基本上是这样实现的。
这部分感觉是linux比win先进。 win64上才和linux一样。
#41
madfrogme2012-10-01 20:30
以下是引用信箱有效在2012-10-1 21:05:03的发言:

胖丁所言好象很有道理,我前天看了这个帖子,也有这个感觉。

前段时间看深入解析windows操作系统,在内存管理这块也基本上是这样实现的。
这部分感觉是linux比win先进。 win64上才和linux一样。

欢迎提供具体细节
#42
madfrogme2012-10-01 23:32
如果不开启分页,下面一图让人豁然开朗,物理地址是从segment descriptor中可以定位的
只有本站会员才能查看附件,请 登录
#43
pangding2012-10-02 11:11
以下是引用madfrogme在2012-10-1 19:55:49的发言:

 
版主所言极是, 0.11内核好像无法在80286上运行,至少要是80386,不知道GDT这个东西,随着intel CPU ,比如 386 ,486, 奔腾,这样升级,是不是GDT也会有一些变化
没变化,随着升级顶多多加一点功能(升级的都是矢量运算,多媒硬解码之类的功能吧,我也不是很懂这方面的)。没点向下兼容能力 intel 还作什么芯片。

最大的变化,应该就是除了实模式以外还有一个保护模式(是这么叫吧,术语我也记不太清了)。主要区别就是以前 dos 用的内存是平坦内存模型。后来就可以分页了,内存寻址访问的机制也更加现代化。
那个所谓 dos 模式只有加载系统内核之前有用吧。现在的技术一般只在那个模式下加载一个系统引导器,那个引导器好像就已经开始启用保护模式了来加载系统内核了。
#44
madfrogme2012-10-02 22:24
回复 43楼 pangding
嗯,我也来看看从实模式转换到保护的的工作流程
#45
madfrogme2012-10-02 22:31
只有本站会员才能查看附件,请 登录

notice the "I/O Devices". They are connected to the system bus.

All I/O Ports are mapped to a given memory location.

This allows us to use the IN and OUT instructions.


所有的I/O设备都连接到系统总线上。 

所有的I/O 端口也都被映射到某个内存地址上,

所以这使我们能够使用IN 和OUT 命令

 if we write a byte to a memory location for a hardware device controller to read,

the processor can signal the device that there are data at that address.

It does this through the Control Bus part of the entire System Bus.

如果我们向内存的某个位置写一个字节让硬件设备控制器来读,

这时处理器向设备发出信号:有数据在那个地址上。

处理器就是通过控制总线来完成的


[ 本帖最后由 madfrogme 于 2012-10-3 23:08 编辑 ]
#46
有容就大2012-10-02 22:46
表示强烈 顶这个 中英文对照的发帖方式!
#47
madfrogme2012-10-02 22:50
关于端口地址
A "Port Address" is a special number that each Controller listens to.

When booting, the ROM BIOS assigns different numbers to these controller devices.

端口地址是一个特别的数字,控制器都会监听这个数字。

机器启动时,ROM BIOS为不同的控制器分配了不同的数字

so controllers have a way to identify themselves.

This allows the BIOS to set up the Interrupt Vector Table,

which communicates to the hardware using this special number.

所以控制器也可以辨认自己了,这使BIOS 可以建立中断向量表。

中断向量表使用这些特别的数字和硬件交流


[ 本帖最后由 madfrogme 于 2012-10-3 23:09 编辑 ]
#48
madfrogme2012-10-02 22:54
以下是引用有容就大在2012-10-2 23:46:55的发言:

表示强烈 顶这个 中英文对照的发帖方式!

谢了啊, 小段的我还能翻译,大段引用我就见机行事了
#49
madfrogme2012-10-03 22:19
To make starting an OS easy, we will be using a lot of existing parts,

GRUB will be the bootloader, and the kernel will be in ELF format.

GRUB (a Multiboot compliant boot loader) puts the system in to the correct state

for your kernel to start executing.

This includes enabling the A20 line

(to give you access to all available memory addresses)

and putting the system in to 32-bit Protected Mode, giving you access to a theoretical 4GiB of memory.

We will not use a flat binary but a kernel in ELF format,

so that we have a lot of control to tell GRUB where to load which part in memory.

为了能容易的启动OS, 我们使用很多已经存在的部件,

包括使用GRUB做为引导程序, 内核为ELF格式。

GRUB使系统进入正确的状态,从而使内核能够运行。

这包括开启A20线(使能够访问所有可用内存);

让系统进入32位保护模式,使能多访问理论上4G的内存;

我们还使用ELF内核而不是平坦的二进制格式内核,

这样我们就可以有更多的控制力来告诉GRUB将内核的哪个部分加载到哪里


[ 本帖最后由 madfrogme 于 2012-10-3 23:48 编辑 ]
#50
fufa10292013-03-15 09:55
看着学习一下!
1