Sunday, March 15, 2009

劝君更尽一杯酒 ---- 我的兄弟龙的婚宴印象

2009年3月14日,我的兄弟龙结婚了! 在此祝福他和他的妻子在新的人生旅途中开心幸福!

在KTV唱最后一首歌的时候我突然莫名其妙的眼泪哗哗的流了,回想起《非诚勿扰》中在秦奋的北海道的老友在他们离开后失声的哭起来,当时我怎么也看不懂,现在我大概懂了。

兄弟,永远祝福你。

Monday, March 2, 2009

learning kernel: 预备知识

=======
预备知识
=======

Intel X86 CPU系列的寻址方式
========================

实地址模式
----------------

Intel在8086 CPU中设置了四个“段寄存器”:CS, DS, SS和ES, 分别用于可执行代码,数据,堆栈和其他。每个段寄存器都是16位,对应于地址总线的高16位。这样和某个段寄存器中的内容相加就得到20位实际地址的转化。对于这种类型的断式内存管理,一个进程可以随心所欲的访问从基地址开始的连续的64K字节空间,没有任何的保护机制,所以称这种模式为“实地址模式”。由于缺乏保护机制,所以一个现代的操作系统是无法在此建造起来的。

保护地址模式
-------------------

为了增加保护机制,80386 CPU增设了两个寄存器:GDTR(global descriptor table register)和LDTR(local descritor table register), 分别可以用来指向内存中的一个段描述结构数组,或者称为段表述表。段描述表的数据结构可以用如下伪代码表述:

typedef struct {
unsigned int base24_31 : 8; /* 基地址最高8位 */
unsigned int g : 1; /* 表段的长度单位,0表示字节, 1表示 */
unsigned int d_b :1; /* 存取方式: 0=16位, 1=32位 */
unsigned int unused :1; /* 未使用,设置成0 */
unsigned int avl :1; /* */
unsigned int seg_limit 16_19 :4; /* 段长度的最高4位 */
unsigned int p :1; /* segment present, 为0时表示该段的内容不在内存中 */
unsigned int dpl :2; /* Descriptor privilege level, 访问本段所需权限 */
unsigned int s :1; /* 描述项类型 1表示系统 0表示代码或数据 */
unsigned int type : 4; /* 段的类型 */
unsigned int base_0_23 :24; /* 基地址的低24位 */
unsigned int seg_limmit_0_15: 16; /* 段长度的低16位 */
} 段描述项;

可以看出每个段描述项的大小是8个字节,含有段的基地址和段的大小,再加上一些其他的信息。

16位段寄存器中的高13位用作下标访问段描述表,低3位含有其他信息:

typedef struct {
unsigned short seg_idx : 13; /* 13位的段描述项下标 */
unsigned short ti :1; /* 段描述表指示位, 0表示GDT, 1表示LDT */
unsigned short rpl :2; /* Requested Privilege level, 要求的优先级别 */
} 段寄存器;

页式内存管理机制
--------------------------

在段式内存管理中逻辑地址映射成物理地址,但在页式管理中不再是物理地址,intel称之为线性地址,再通过页式管理转变成实际的物理地址。32位的线性地址的伪代码可以表示如下:

typedef struct {
unsigned short dir :10; /* 用作页面表目录中的下标, 该目录项指向一个页表项 */
unsigned short page :10; /* 用作具体页面表中的下标,该表项指向一个物理页面 */
unsigned short offset :12; /* 在4K字节物理页面内的偏移量 */
} 线性地址;

由于页面表和页面的起始地址总是在4K字节的边界上,这些指针的低12位都永远是0。可以利用这12位控制。目录项的结构的伪代码为:

typedef struct {
unsigned int ptba : 20; /* 页面基地址的高20位 */
unsigned int avail :3; /* 供系统程序员使用 */
unsigned int g :1; /* global,全局性页面 */
unsigned int ps :1; /* 页面大小, 0表示4K字节 */
unsigned int reserved :1; /* 保留, 默认为0*/
unsigned int a :1; /* accessed, 表示已访问过 */
unsigned int pcd; /*关闭缓存存储器 */
unsigned int pwt; /* Write-Through,用于缓冲存储区 */
unsigned int u_s; /* 为0时表示系统权限, 为1时表示用户权限 */
unsigned int r_w : 1; /* 只读或可写 */
unsigned int p :1; /* 为0时表示相应的页面不在内存中 */
} 目录项;

页表项的结构基本和目录项相同。

linux内核源代码的汇编语言代码
-----------------------------------------------
参见:http://asm.sourceforge.net/articles/linasm.html