Zhaoyuan 的个人资料备忘纪事照片日志列表更多 工具 帮助

日志


4月24日

Linux Memory Management(0.11 release)<4>

以前写C code时经常会用malloc和free来动态申请和释放内存,但对操作系统是如何实现这个功能的却不知道,看了Linux 0.11的kernel code,总算知道是怎么回事了。
 
Linux 0.11中,使用桶描述符来管理动态内存,每个桶描述符管理一页内存,该页内存中分成指定大小的块,数据结构如下:
 
struct bucket_desc { /* 16 bytes */
 void   *page;                         //物理页内存的头指针
 struct bucket_desc *next;     //链向下一个桶
 void   *freeptr;                     //桶中空闲块的指针
 unsigned short  refcnt;         //引用counter
 unsigned short  bucket_size;  //目前这一个桶中每个块的大小
};
 
为了加速的分配和回收,定义一个桶目录,保存一组桶指针.
struct _bucket_dir { /* 8 bytes */
 int   size;
 struct bucket_desc *chain;
};
并如下初始化
struct _bucket_dir bucket_dir[] = {
 { 16, (struct bucket_desc *) 0},
 { 32, (struct bucket_desc *) 0},
 { 64, (struct bucket_desc *) 0},
 { 128, (struct bucket_desc *) 0},
 { 256, (struct bucket_desc *) 0},
 { 512, (struct bucket_desc *) 0},
 { 1024, (struct bucket_desc *) 0},
 { 2048, (struct bucket_desc *) 0},
 { 4096, (struct bucket_desc *) 0},
 { 0,    (struct bucket_desc *) 0}};   /* End of list marker */
 
 
下面就可以看内核是如何申请内存的了。
 
void *malloc(unsigned int len)
{
 struct _bucket_dir *bdir;
 struct bucket_desc *bdesc;
 void   *retval;
 /*
  * First we search the bucket_dir to find the right bucket change
  * for this request.
  */
 for (bdir = bucket_dir; bdir->size; bdir++)
  if (bdir->size >= len)
   break;      //找到一个块大小最合适的桶.
 if (!bdir->size) {
  printk("malloc called with impossibly large argument (%d)\n",
   len);
  panic("malloc: bad arg");
 }
 /*
  * Now we search for a bucket descriptor which has free space
  */
 cli(); /* Avoid race conditions */
 for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next)
  if (bdesc->freeptr)
   break;           //找到该桶中一个空闲的快的头指针
 /*
  * If we didn't find a bucket with free space, then we'll
  * allocate a new one.
  */
 if (!bdesc) {   //如果没有合适的桶. 
  char  *cp;
  int  i;
  if (!free_bucket_desc)  //如果桶空的话,或者一页的桶描述符已经用完.
  init_bucket_desc();  //该函数会申请一页内存,然后放入桶链表,所以这一页都是桶描述符.
  bdesc = free_bucket_desc;    //free_bucket_desc总是当前空闲的桶描述符,这里就是把空闲的桶拿来处理
  free_bucket_desc = bdesc->next; //free_bucket_desc则指向了下一个空闲的桶描述符
  bdesc->refcnt = 0;
  bdesc->bucket_size = bdir->size;  
  bdesc->page = bdesc->freeptr = (void *) cp = get_free_page();  //申请一页
  if (!cp)
   panic("Out of memory in kernel malloc()");
  /* Set up the chain of free objects */
  for (i=PAGE_SIZE/bdir->size; i > 1; i--) {
   *((char **) cp) = cp + bdir->size;   //非常有技巧的地方,自己领会吧.
   cp += bdir->size;
  }
  *((char **) cp) = 0;
  bdesc->next = bdir->chain; /* OK, link it in! */  
  bdir->chain = bdesc;           //把这个桶描述符链上..   
 }
 retval = (void *) bdesc->freeptr;
 bdesc->freeptr = *((void **) retval);
 bdesc->refcnt++;
 sti(); /* OK, we're safe again */
 return(retval);
}
4月22日

Linux Memory Management(0.11 release)<3>

一个进程的内存占用情况,从底到高依次是代码,数据,堆和堆栈。
 
下面看下缺页中断处理函数.
void do_no_page(unsigned long error_code,unsigned long address)
{
 int nr[4];
 unsigned long tmp;
 unsigned long page;
 int block,i;
 address &= 0xfffff000;
 tmp = address - current->start_code;    //计算出引发缺页中断的线性地址与当前task的代码起始地的offset.
 if (!current->executable || tmp >= current->end_data) {   //第一个条件,如果但前进程还没有任何可执行代码,则直接分配内存,
  get_empty_page(address);                //第二个条件,如果这个offset超过了进程的数据段,即进入了堆区,认为代码进行了申请了动态存
 存,如malloc(sizeof(INT)),则直接分配内存。
  return;                                               
 }
 if (share_page(tmp))                       //到这里,说明该地址在进程的代码和数据段内,所以要挖掘潜在的内存共享。(如子进程共享父进程的代码和数据段).试图共享一页物理内存。
  return;                                           
 if (!(page = get_free_page()))        //惨了,没有共享到,就只好申请一页内存,并从文件中读取数据出来。  
  oom();
/* remember that 1 block is used for header */
 block = 1 + tmp/BLOCK_SIZE;
 for (i=0 ; i<4 ; block++,i++)
  nr[i] = bmap(current->executable,block);
 bread_page(page,current->executable->i_dev,nr);
 i = tmp + 4096 - current->end_data;
 tmp = page + 4096;
 while (i-- > 0) {
  tmp--;
  *(char *)tmp = 0;
 }
 if (put_page(page,address))
  return;
 free_page(page);
 oom();
}
 
在这里面有做内存共享的动作,即share_page();
一个进程在创建时,都是先共享父进程的代码和数据段,被共享的内存页,属性会变为只读,而当父进程或子进程中任何一个试图去写该内存页时,即发生内存写保护错误,系统调用do_wp_page()进行处理
 
void do_wp_page(unsigned long error_code,unsigned long address)
{
#if 0
/* we cannot do this yet: the estdio library writes to code space */
/* stupid, stupid. I really want the libc.a from GNU */
 if (CODE_SPACE(address))
  do_exit(SIGSEGV);
#endif
 un_wp_page((unsigned long *)
  (((address>>10) & 0xffc) + (0xfffff000 &
  *((unsigned long *) ((address>>20) &0xffc)))));   //传递的参数,页表项指针
}
void un_wp_page(unsigned long * table_entry)
{
 unsigned long old_page,new_page;
 old_page = 0xfffff000 & *table_entry;
 if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
  *table_entry |= 2;
  invalidate();
  return;
 }
 if (!(new_page=get_free_page()))   //申请一页新的物理内存
  oom();
 if (old_page >= LOW_MEM)
  mem_map[MAP_NR(old_page)]--;
 *table_entry = new_page | 7;       //加属性后赋给该页表项
 invalidate();
 copy_page(old_page,new_page);  //将共享物理页面的内容,复制给新的物理页
}
可以看出,父子进程,谁先触发了页保护中断(和缺页是一个中断),谁就会获得新的一个物理页面。
 
再看页表拷贝函数,在fork.c中进程创建时调用,用于实现父子进程的内存共享。
int copy_page_tables(unsigned long from,unsigned long to,long size)
{
 unsigned long * from_page_table;
 unsigned long * to_page_table;
 unsigned long this_page;
 unsigned long * from_dir, * to_dir;
 unsigned long nr;
 if ((from&0x3fffff) || (to&0x3fffff))             //一个页表管理4M内存,
  panic("copy_page_tables called with wrong alignment");
 from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */   //获取源页目录表项
 to_dir = (unsigned long *) ((to>>20) & 0xffc);  //目的页目录表项
 size = ((unsigned) (size+0x3fffff)) >> 22;  //计算需要copy几个页表
 for( ; size-->0 ; from_dir++,to_dir++) {
  if (1 & *to_dir)
   panic("copy_page_tables: already exist");
  if (!(1 & *from_dir))
   continue;
  from_page_table = (unsigned long *) (0xfffff000 & *from_dir);   //源页表地址
  if (!(to_page_table = (unsigned long *) get_free_page()))  //为目的页表申请一页内存
   return -1; /* Out of memory, see freeing */
  *to_dir = ((unsigned long) to_page_table) | 7;   //加上属性赋给目的页表项
  nr = (from==0)?0xA0:1024;
  for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {  //此循环逐个copy页表项,并改变页的属性为只读
   this_page = *from_page_table;
   if (!(1 & this_page))
    continue;
   this_page &= ~2;
   *to_page_table = this_page;
   if (this_page > LOW_MEM) {
    *from_page_table = this_page;
    this_page -= LOW_MEM;
    this_page >>= 12;
    mem_map[this_page]++;  //
   }
  }
 }
 invalidate();
 return 0;
}

Linux Memory Management (0.11 release) <2>

接着上篇说,Linux kernel init时的task0, 使用了4个内核的页表,当应用程序运行的时候,其页表既是通过调用

void get_empty_page(unsigned long address)
{
    unsigned long tmp;

    if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
        free_page(tmp);        /* 0 is ok - ignored */
        oom();
    }
}

来获得.注意此页表已经是存放在主内存区的,不同于内核使用的四个页表,他们四个是固定放在物理内存0x1000,0x2000,x03000,0x4000处的,每个页表顺序映射4M物理内存,这样内核使用的线性地址等于实际的物理地址。

那么进程的4G(0x0000 0000 ~ 0xFFFF FFFF)线性地址是如何映射到16M(0x000 000~ 0xFFF FFF) 物理内存上去的呢?

接着上一篇的例子来说,逻辑地址:21h:12345678h转换为线性地址即为0x23456789h

由于进程在创建时,并不会把其会访问到的内存全部装入,而是在进程访问到该地址时才动态载入,这样必然导致第一次访问该地址出时,其所在页面不在内存中,触发缺页中断,系统最终会通过get_empty_page(0x23456789)来获取一页物理内存并建立和0x23456789的映射。

a. 假定里面的get_free_page()获取的物理内存页地址为 0x123 000。

a. put_page(0x123 000,0x23456789)来建立映射,线性地址0x23456789的高10bit为0x0010 0011 01 ,即为页目录表的索引值(140)

b. 该索引处的页目录表项的值高20bit不在内存,说明没有页表,需要再申请一页物理内存(假定获取的页面物理址在0xf10 000)用于存放页表,到此,这一页表即将对应从0x234x xxxx开始的4M线性内存. 将这页内存的地址加上访问属性后(0xf10 007)填入该页目录表项中。

c. 接着获取线性地址的中间10bit.  0x00 0101 0110=0x56,得到页表的偏移地址,加上页表的基址0xf10 000= 0xf10 056,此即为该线性地址对应的页表项地址,将 0x123 000 加上属性(0x123 007)填入到0xf10 056处,至此整个映射结束。

d. 对线性地址0x23456789 的访问,通过2级页表找到了它映射到的物理内存页面0x123 000, 然后取线性地址的低12位0x789,得到最终的物理内存 0x123 789。 这样以后对0x23456000 ~ 0x23456FFF的访问就是访问物理内存0x123 000~ 0x123 FFF。

这时,进程如果访问0x234FF789的线性地址,由于该页不再内存,而再次调用get_free_page()

1. 假定里面的get_free_page()获取的物理内存页地址为 0x234 000。

2. 由于此时0x234FF789对应的页目录项中的内存有效,即页表存在,所以只须,取0x234FF789的中间10bit来定位到具体的页表项,0xFF,加上页表的基址0xf10 000= 0xf10 0FF,此即为该线性地址对应的页表项地址,将 0x234 000 加上属性(0x234 007)填入到0xf10 0FF处完成本次映射。

3.至此,对0x234FF789的访问,通过2级页表找到了它映射到的物理内存页面0x234 000, 然后取线性地址的低12位0x789,得到最终的物理内存 0x234 789。 这样以后对0x234FF000 ~ 0x234FFFFF的访问就是访问物理内存0x234 000~ 0x234 FFF。

可见,进程的连续线性地址最终在物理内存上是按页分开,独立的。 显然4G的线性空间比16M大了很多,所以系统会尽量挖掘共享内存,如果还出现内存页面不够,则通过二级存储空间进行swap,将暂时不使用的页面调出,然后将这页分配出去。

 

4月21日

80386保护模式下的内存访问(ZZ)

1.分段机制 80386的两种工作模式   80386的工作模式包括实地址模式和虚地址模式(保护模式)。Linux主要工作在保护模式下。 分段机制   在保护模式下,80386虚地址空间可达16K个段,每段大小可变,最大达4GB。   从逻辑地址到线性地址的转换由80386分段机制管理。段寄存器CS、DS、ES、SS、FS或GS标识一个段。这些段寄存器作为段选择器,用来选择该段的描述符。 分段逻辑地址到线性地址转换图
图1 分段逻辑地址到线性地址转换图
2. 分页机制 分页机制的作用   分页机制是在段机制之后进行的,它进一步将线性地址转换为物理地址。   80386使用4K字节大小的页,且每页的起始地址都被4K整除。因此,80386把4GB字节线性地址空间划分为1M个页面,采用了两级表结构。 两级页表   两级表的第一级表称为页目录,存储在一个4K字节的页中,页目录表共有1K个表项,每个表项为4个字节,线性地址最高的10位(22-31)用来产生第一级表索引,由该索引得到的表项中的内容定位了二级表中的一个表的地址,即下级页表所在的内存块号。 第二级表称为页表,存储在一个4K字节页中,它包含了1K字节的表项,每个表项包含了一个页的物理地址。二级页表由线性地址的中间10位(12-21)位进行索引,定位页表表项,获得页的物理地址。页物理地址的高20位与线性地址的低12位形成最后的物理地址。 利用两级页表转换地址
 图2  利用两级页表转换地址
3. 内核空间和用户空间
用户空间   在Linux中,每个用户进程都可以访问4GB的线性虚拟内存空间。其中从0到3GB的虚存地址是用户空间,用户进程可以直接访问。 内核空间   从3GB到4GB的虚存地址为内核态空间,存放供内核访问的代码和数据,用户态进程不能访问。所有进程从3GB到4GB的虚拟空间都是一样的,linux以此方式让内核态进程共享代码段和数据段。

保护模式(1)---存储方式

Writen By Dangerman

  保护模式现代操作系统的基础,理解他是我们要翻越的第一座山。保护模式是相对实模式而言的,他们是处理器的两种工作方式。很久以前大家使用的dos就是运行在实模式下,而现在的windows操作系统则是运行在保护模式下。两种运行模式有着较大的不同,   实模式由于是由8086/8088发展而来因此他更像是一个运行单片机的简单模式,计算机启动后首先进入的就是实模式,通过8086/8088只有20根 地址线所以它的寻址范围只有2的20次幂,即1M。内存的访问方式就是我们熟悉的seg:offset逻辑地址方式,例如我们给出地址逻辑地址它将在 cpu内转换为20的物理地址,即将seg左移4位再加上offset值。例如地址1000h:5678h,则物理地址为10000h+5678h= 15678h。实模式在后续的cpu中被保留了下来,但实模式的局限性是很明显的,由于使用seg:offset逻辑地址只能访问1M多一点的内存空间, 在拥有32根地址线的cpu中访问1M以上的空间则变得很困难。而且随着计算机的不断发展实模式的工作方式越来越不能满足计算机对资源(存储资源和cpu 资源等等)的管理,由此产生了新的管理方式——保护模式。   80386及以上的处理器功能要大大超过其先前的处理器,但只有在保护模式下,处理器才能发挥作用。在保护模式下,全部32根地址线有效,可寻址4G的物 理地址空间;扩充的存储分段机制和可选的存储器分页机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持;支持多任务;4个 特权级和完善的特权级检查机制,实现了数据的安全和保密。计算机启动后首先进入的就是实模式,通过设置相应的寄存器才能进入保护模式(以后介绍)。保护模式是一个整体的工作方式,但分步讨论由浅入深更利于学习。 一.存储方式   存储方式主要体现在内存访问方式上,由于兼容和IA32框架的限制,保护模式在内存访问上延用了实模式下的seg:offset的形式(即:逻辑地址), 其实seg:offset的形式在保护模式下只是一个躯壳,内部的存储方式与实模式截然不同。在保护模式下逻辑地址并不是直接转换为物理地址,而是将逻辑 地址首先转换为线性地址,再将线性地址转换为物理地址。如图一:  

  线性地址是个新概念,但大家不要把它想的过于复杂,简单的说他就是0000000h~ffffffffh(即0~4G)的线性结构,是32个bite位能 表示的一段连续的地址,但他是一个概念上的地址,是个抽象的地址,并不存在在现实之中。线性地址地址主要是为分页机制而产生的。处理器在得到逻辑地址后首 先通过分段机制转换为线性地址,线性地址再通过分页机制转换为物理地址最后读取数据。如图二:

  分段机制是必须的,分页机制是可选的,当不使用分页的时候线性地址将直接映射为物理地址,设立分页机制的目的主要是为了实现虚拟存储(分页机制在后面介绍)。先来介绍一下分段机制,以下文字是介绍如何由逻辑地址转换为线性地址。   分段机制在保护模式中是不能被绕过得,回到我们的seg:offset地址结构,在保护模式中seg有个新名字叫做“段选择子”(seg.. selector)。段选择子、GDT、LDT构成了保护模式的存储结构,如图三`,GDT、LDT分别叫做全局描述符表和局部描述符表,描述符表是一个 线性表(数组),表中存放的是描述符。  

  “描述符”是保护模式中的一个新概念,它是一个8字节的数据结构,它的作用主要是描述一个段(还有其他作用以后再说),用描述表中记录的段基址加上逻辑地 址(sel:offset)的offset转换成线性地址。描述符主要包括三部分:段基址(Base)、段限制(Limit)、段属性(Attr)。一个 任务会涉及多个段,每个段需要一个描述符来描述,为了便于组织管理,80386及以后处理器把描述符组织成表,即描述符表。在保护模式中存在三种描述符表 “全局描述符表”(GDT)、“局部描述符表”(LDT)和中断描述符表(IDT)(IDT在以后讨论)。    

(1)全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。  

    (2)段选择子(Selector)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的,如图三①步。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)如图四  

  段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位 置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符(如图三①步)。然后用描述符表中的段基址加上逻辑地址(SEL: OFFSET)的OFFSET就可以转换成线性地址(如图三②步),段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在 LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。

例如给出逻辑地址:21h:12345678h转换 为线性地址      

 a. 选择子SEL=21h=0000000000100 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1      

b. OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h  

  (3)局部描述符表LDT(Local Descriptor Table)局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图五

  LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同LDTR的内容是一个段选择子。由 于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是 这样一个选择子。LDTR可以在程序中随时改变,通过使用lldt指令。如图五,如果装载的是Selector 2则LDTR指向的是表LDT2。

举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。        

a. 首先需要装载LDTR使它指向LDT2 使用指令lldt将Select2装载到LDTR        

b.  通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是 LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。OFFSET=12345678h。逻辑地址为1C:12345678h         

c. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h         

d. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)    

由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。     存储方式是保护模式的基础,学习他主要注意与实模式下的存储模式的对比,总的思想就是首先通过段选择子在描述符表中找到相应段的描述符,根据描述符中的段基址首先确定段的位置,再通过OFFSET加上段基址计算出线性地址。

4月20日

Linux Memory Management(0.11 release)<1>

 

当进程通过线性地址address访问内存时,如果出现缺页,则会触发缺页中断,内核经过一系列操作后,会申请出一页物理内存,并建立该页物理内存和线性地址的映射,供进程使用。

void get_empty_page(unsigned long address)
{
    unsigned long tmp;

    if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
        free_page(tmp);        /* 0 is ok - ignored */
        oom();
    }
}

//get_freee_page()即可从主内存区获得一页物理内存,返回该页物理内存的头指针。

//put_page(tmp,address)即建立该物理页面与线性地址的映射。

unsigned long put_page(unsigned long page,unsigned long address)
{
    unsigned long tmp, *page_table;

/* NOTE !!! This uses the fact that _pg_dir=0 */

//这里假定页目录表的在内存地址0x0处,不然页表的地址要加上相应的偏移地址,使程序复杂化 

  if (page < LOW_MEM || page >= HIGH_MEMORY)
        printk("Trying to put page %p at %p\n",page,address);
    if (mem_map[(page-LOW_MEM)>>12] != 1)
        printk("mem_map disagrees with %p at %p\n",page,address);
    page_table = (unsigned long *) ((address>>20) & 0xffc);

   // page_table为线性地址address对应的页目录表项,即在页目录表中的索引,用于查找页表。
    if ((*page_table)&1)  //如果该页目录表项对应的页表已经在内存
        page_table = (unsigned long *) (0xfffff000 & *page_table);

       //获取该页表的物理地址
    else {//如果线性地址对应的页表未在内存,则先申请一页物理内存,用于存页表。
        if (!(tmp=get_free_page()))
            return 0;
        *page_table = tmp|7; //设置该页的属性,并赋值给页目录表项。
        page_table = (unsigned long *) tmp;//page_table定位在该页内存。
    }
    page_table[(address>>12) & 0x3ff] = page | 7;

    //根据线性地址的中间10bit,定位到页表项,然后将page和该页表项建立映射。
/* no need for invalidate */
    return page;
}

Linux内核在初始化时,会建立一个页目录表项和四个页表,其中也目录表是内核和其他进程公用的,但四个页表是内核独用的,可以映射16M内存。对于32位的x86体系结构,保护模式下使用的是段页式内存管理,代码段和数据段不是简单的段地址而是全局段描述符表中的index,每个表项占据8个字节,通过索引这个表,即可找到代码段和数据段的更详细的信息,包括特权级(保护模式的核心),段的基址和限长等。

Linux内核的代码段和数据段的设定为

CS :0x00C0 9a00 0000 0fff  //基址为0,段限长为16M

DS: 0x00C0 9200 0000 0fff

可见,Linux内核中,逻辑地址即为线性地址,内核初始化四个内核页表的方法,则表明线性地址和实际物理地址一一对应,所以,内核使用的内存即为实际物理内存。

对于用户程序,系统会在主内存区申请一页内存来放页表,即会调用上面所说的get_empty_page,从主内存区获得一页内存。

2月21日

买房了

经济危机的时候买房,风险大啊,可又有什么办法,谁叫俺们是传说中的刚需呢!~

早晨起来4点多打车到梅林关,本以为太早,让人家看了笑话,窃窃的走进销售中心,却发现已经有六、七十人在排队了,打听才知,有人昨天晚上八点多就开始排了,真是虔诚啊。

清晨还是有些阴冷的,好不容易挨到太阳出来,没多久就发现,太晒了,一路坚持到拿号,选房,等我进去的时候,销售网络还出现了故障,等一切恢复之后,发现只剩少数的户型可以选择了,没多想就订了。

之后的手续都有销售人员陪同和指引,非常顺利的签拉一大堆的合同和协议,算下来,光签我的名字就有上百次,汗`~。

房子本身我看上的优点是:

紧挨地铁,出行方便准时;

户型88平再送上一个屋,这样对我来讲,基本一步到位了;

旁边紧挨梅林山公园,方便一家老小休闲锻炼。

看下卫星地图吧

Home

1月11日

逛在首尔

   不知不觉已经在韩国呆了一个礼拜,趁着周末带着同事出来逛逛,不过今天真的很冷,逛街实为下下策。
   一年没有来韩国,发现周围似乎多了很多中国人,无论你坐车,吃饭,逛街都能看到,尤其体现在购物上,不知道是否因为韩币的贬值,总之发现购物的中国人很多,而在这边的商家似乎也意识到了,在饭店,商店中雇拥了中国人,这样买卖双方都方便很多,而且很多韩国人也会简单的中国话(东北口音的,哈哈),和你打招呼,聊天,给身在国外的人一丝温暖,也可以独自意淫下,祖国强大了!
   明洞附近应该算是首尔扫街购物的首选了,这里店铺云集,种类繁多, 衣服,饰品,化妆品居多。 每次到明洞,都会去吃明洞饺子,这家店不知道是因为身处商圈,还是真的好吃,总之每次去都要排队等候,而她提供的就只是饺子和面条,口味上我觉得一般,倒是里面的泡白菜,我很喜欢,是那种加了很多蒜的,入口很辛辣,不会像其他家那样很酸,冬天吃起来,很暖身驱寒的,每次都要吃到肚子里火辣辣的。
   以前只是在电视上看到过,这次在大街上也见识了, 两个女孩手举高牌,上面写着"Free Hugs",站在寒风中,和主动上来的行人拥抱,不过似乎大多数人并不太感冒,也许他们不缺乏温暖吧,搞不懂这种行为艺术。
   在逛街等朋友的时候,一个在饭店门口发传单的外国人和我打招呼,“吃了吗?”
   我怀疑这句话是不是去了中国的外国人都会讲, "Already"。
   就这样我们聊了起来,原来是做兼职的土耳其留学生,他去过中国,北京,上海,非常喜欢中国,虽然现在是全球经济危机,但他认为中国以后将会成为经济No1,and He hates America & England。不过最能聊开的是足球,2002年的韩日世界杯,我们和土耳其是一个小组的,可是最后他们是季军,我们是last one..,他问我为什么么中国那么大,球却那么滥..唉..次题无解.我都不好意思讲,只好告诉他,我们都喜欢篮球,他也能知道姚明。不过我们都很讨厌韩国足球队,哈哈,他说在这里是不能说的秘密。
  他很喜欢北京女孩,very sweety,  shit,肯定没干好事~,我就转移到日本女孩,结果他说他有一个日本女朋友,下礼拜他就要去日本看她。不过我们都认为,日本人确很礼貌和友好,在韩国就差一些。
  朋友上来的时候,他还热情的打招呼告别,满热情的人,他对中国经济很有信心。
  到了南大门的时候,感觉这边人气就不如东大门,而其东西的品质差了很多,多了很多韩国特产店,海苔,人参,泡菜,柚子茶,酒之类的,不过价钱感觉很高,我看到有国内旅行团把游客拉到里面去消费,有些不厚道。
  逛到后来大家都觉得太冷了,还是打道回府吧。 
12月21日

感受日本(饮食篇)

个人对日式饮食没有兴趣,不过到了这边也没办法..入乡随俗了,总不能挨饿干活吧..好吧,看看日本人都吃些什么。

名古屋市酒店的早餐,米饭,鱼,一份汤,最困惑的是那个蛋,第一次吃的时候打到了汤里喝,后来发现别人都是直接拌到米饭里的,结果拌后米饭非常的腥..难以下咽,烤鱼的味道也很一般..鱼类等海鲜在日本饮食里面很常见.

DSC05617 

哪里都少不了麦当劳..

DSC05620

看看他们的价位..折算成人民币都是60多元一份..日本的人均收入可见一斑..

DSC05621

到达龟山市的小酒店..

 DSC05784

简单的自助早餐..蓝莓果酱还不错..

 DSC05785

忘了谁请的一次料理,吃的是寿司饭..很酸..那个荞麦面条还可以..就是有点少

 DSC05787

SHARP食堂的午饭..

 DSC05788

名古屋车站是一个集交通,购物,饮食等商业于一体的中心..里面就有一条拉面街..日式拉面很流行.

DSC05823

DSC05822

DSC05808

 DSC05806

下面是代理商请我们吃的一顿,饭店的名字叫"和食",主理日本传统饮食,所以猜想,"和食"这个名字难道是"大和民族饮食"的缩写?

DSC05832

我吃的还是寿司..味道和国内的没啥差..可能我不太感冒..

 DSC05830

同事吃的小火锅(日文叫,"sharp,sharp"),谁懂?

 DSC05831

走之前自己买单的一顿~~,大家一起又是去吃拉面..到处都是拉面馆啊,最普通的平民消费店.不过人气很足.

 DSC05836

吃面还是要看汤的~!..这一碗就将近百元..

 DSC05838

12月14日

感受日本(风景篇)

去日本的时候已经是12月的中旬了,如果是在我的家乡,已经是进入了隆冬,相信外面的世界都将是光秃秃的,没有任何生机。

但看了日本的风景,却让我猛然想起了中学的地理老师,伊笑先生,老师曾讲过我们是大陆性季风气候,而日本是海洋性气候,虽然纬度相同,但是气候景观确有很大不同,以前不懂会有什么差,现今见到了,不由得称奇。

名古屋市最值得一去的地方当然是名古屋城了,既有历史遗迹可以看,又有风景可以赏。

名古屋城入口

DSC05635

门口的大树,郁郁葱葱,难以想象现在是冬季。

DSC05633

进到里面才发现神奇才刚刚开始.映入眼帘的如此炫目。11月中旬开始的一个月,是日本看红叶的季节。

DSC05644

孤单却傲气十足

DSC05690 

刺眼的红

DSC05652 

DSC05653

 DSC05680

红与黄

DSC05728

DSC05721

日式的园林和建筑

DSC05731 

DSC05676

想起龚自珍的一句诗,"落红不是无情物,化作春泥更护花"。

 DSC05678

 DSC05723

红叶衬托下的名古屋城堡

DSC05679

留个影吧

DSC05730

日本的环境保护得真是很好,想想现在深圳的环境,真是天差地别,不过据说他们也有过工业发展破环环境的历史,目前我们国家应该正在经历这种情况,如何在经济发展中保护好环境,给人民一片蓝天,这确实是一个考验,但愿我们的明天也一样蓝。

12月6日

Nagoya Castle

很冷的风,很暖的阳,很艳的色彩。

DSC05649

 

DSC05722

12月5日

抵达日本

这次意外被安排到日本一次,由于准备不足,出发得很狼狈,本打算在国内换些日元,谁知道到了招商银行却被告知今天没有日元了,郁闷。

没办法,到香港机场解决吧,从蛇口坐船直达香港机场码头,刚要去换登机牌,却被几个机场工作人员拦下,说是要检查,便把我叫到一个办公室,弄得我一头雾水,之前来来往往香港很多次,都没这“待遇”啊。

接下来背盘问了些姓名,住址,工作,去哪里,做什么,要多久之类的问题,出示了下护照,机票,检查了下行李,手提电脑包,便没有了,我还追问到怎么会查到我,是不是行李有问题,答案确是随机抽查,更郁闷。

换完了登机牌,就可以乘坐通勤巴士到达机场,不过这离我登机的闸口还很远,要做一趟电车才能到达,汗~!香港机场果然购大。

国泰的飞机上可以看电影,这对打发飞机上的时间和缓解劳累很有帮助,三个半小时过得很惬意。

由于出发得很晚,到达名古屋中部国际机场时,已经是当地时间晚上九点半了,入境的检查很顺利,而且日本出入境署提供了多种语言的入境登记表和海关申报单,包括中文,因此填写起来很方便,当然要填写的东西相应得也很多,特别的地方是,入境时,日本会采集你的双手食指指纹。

从机场到名古屋市要做轻轨,名铁线,有好几种车型选择,

DSC05854

时间紧迫,当然要做最快的,已经没有多少人乘车了。

DSC05609

大约35分钟就到达名古屋站,跟着人流走,将票投进闸机入口,却没见票从出口上来,我以为出了故障,在那里等了半天,不见动静,由于日本人都使用卡,弄得我也无法判断这是否正常,可惨了~!拿什么报销,后来才知道,日本出于环保和资源的循环利用,纸制车票都是要回收的,如果要拿出去,就要到有人的闸口找工作人员盖戳以示作废。

车站很大,绕了好久也没找到人工售票的地方,询问工作人员才知道,我要乘的车次使用自动售票机,价钱便宜很多,看车型就知道了。

DSC05612

列车穿梭在黑暗之夜,向窗外看去,大多数的建筑物都是两层的阁楼式,和动画片里的屋子一样,偶尔能看到亮着灯的屋子,夜深了,路灯下面只能看到零星的自动售货机,大街小巷中已经没有行人,而是停满了密密麻麻的车,日本的人均汽车占有量真的很高。

到达目的地龟山站时,已经是子夜,空荡荡的车站显得很恐怖,还好不多时就来了一辆的士,拿出酒店的地图,司机便一下明白了。车开动了才发现,日本原来是左侧行驶的哦。

日本的酒店房间很小,可能是我住的标准太低,看一下就知道了。

DSC05773

DSC05775

太晚了,必须休息啦~。

6月10日

Happy Valey

来深圳的第三个年头,才去了欢乐谷,最近天气特别的热,所以下午三点多才去

进去右手边就是魔幻城堡,相片很好看,似是到了童话世界

   

这个牛很烫..

 

矿山车最好玩

 

雪山飞龙太过刺激,再也不做了,玩得时候还是坐在最前面..开始还可以挣着眼,后来就闭得死死的了,下来后脖子好疼

 

激流勇进排起的水浪花很漂亮,自己做的时候啥也没看到.

 

玩到晚上也已经很累了,很多项目没有玩到,下次继续:)

2月24日

冬日登水原光教山

     工作了就少了锻炼的时间,今天找机会去爬了光教山,在山脚下,还看到了几个中国人,寒暄一番。韩国这边属于典型的丘陵地区,山峦起伏连绵,植被也以北方榛叶林为主,冬天除了树干,什么都没有,光秃秃的,偶尔也能看到坚挺在树枝上的黄叶,当然松树是必不可少的了,万年青哦。
     沿着山间小路一直走,旁边是小溪,有些地方还结着厚厚的冰,有些地方又能看到清澈见底的山泉,周围的一切是那么静,静得可以听到潺潺的流水声和时有时无的鸟叫声,抬头就可以看到硕大的鸟窝挂在高高的枝头上,幸好是冬天,没有茂密的树叶遮挡,视野可以得到极大的满足。一路上来可以看到有很多登上的人,大多是一家人集体出动,从四五岁的孩子到六七十岁的老人,而且装备很是齐全,背包,登山杖,遮阳帽等等,一看就是经常爬山,注重锻炼,整个家庭出来锻炼,氛围也很好。
    爬到山顶的时候还有一个攀岩的平台,从最高点拉下两条很粗的绳子,看上去有些陡,不过爬起来还很顺手,几个来回爬上了光明顶,真是会当凌绝顶,一览众山小,水原的一切尽在眼底,原来这个地方也很大啊,沿着山梁一路走,可以看到好几个下山的口,不过都是韩文标注,无法辨识。难得在山上看到一个古迹,是一个将军的雕塑,看下面的铭写的是当年抗击清军的领袖。
    天好蓝,空气也很新鲜,想想国内的工业城市,整日不见天日,环境关乎我们的生活,看看韩国的景区,虽然有那么多人旅游,锻炼,却能保持得很干净,再看看国内的旅游景区,每天都有垃圾被随意丢弃,这是很现实的差距啊,看着别人的不免有些羡慕,我们的路还很长。
1月21日

终于可以回国了

  这次出来时间好长,不过相比于上次,项目问题不是很多,所以还算轻松惬意。
  可是我已经无法再忍受韩国的饮食了,想想自家门口的饭店,真是美味难求,在这边吃个排骨汤都要比划半天,十分不爽。
  回家吧,过个好年,团圆年。
 
 
 
1月1日

Journey to Seoul

  新年第一天,进城。从Suwon到Seoul,城铁一号线只需要四十分钟的样子,每个地铁站都有详细的乘车和转车图示,我们先做到Seoul Station然后转乘到到明洞商业街,在这里可以拿到Seoul地图,有韩文,英文,日文,繁体中文,简体中文各种版本,有名的商业圈和观光景点都在其中,所以拿到地图你就可以玩遍Seoul了。

  早就听说明洞饺子非常有名,于是专门拜访,可能我们去得比较早,没有排队就直接有位置坐下来,其实饺子的味道在国内只能算一般了,不过能在异国吃到很接近国内的口味,也算可以了,果然在我们离开的时候,门外已经排了很长的队伍,看来还是蛮受欢迎的。整个明洞商区还是很大的,有点像深圳东门商圈,不过东西实在是太贵了,虽然是年末打折,但也不是小数,可也不能空手而归,找到一家饰品店,买些小东西吧,还没等我们开口,服务员就问我们是否说汉语,得到肯定的答复后,她告诉我们她是中国人,原来是一名来自大连外国语学院的交换学生,假期在这边勤工俭学,能够听到熟悉的语言真的好舒服,买东西都方便不少,而且是有买有送。

 东西贵就只能四处闲逛了,于是我们去看了韩国的常德宫(Changdeokgung),门票不是很贵,分两种,个人游和团队游,前者要贵些,后者还提供解说。

景区大门,多么醒目的汉字牌匾,整个宫殿里到处都写着汉字名称。

DSC01732

景区导游提供四种解说,我们到的时候有些晚,中文解说已经结束,只好听英文的了

DSC01733

由于是冬天,没有太多的景色可以欣赏,只能专注于宫廷的建筑和布局,

干枯的护城河

DSC01737

仁政门

DSC01740

过了仁政门,就到了大殿,相当于故宫的太和殿了

DSC01742

两旁的小石柱上标了官阶,不同等级的官员,各就各位。

DSC01743

看看里面

DSC01747

大殿转过去就到了后院,是寝宫及后花园,景德宫不像故宫那样有一个中轴线,其本身的宫殿就少,排列也没有太多的讲究。

DSC01777

DSC01766

书院

DSC01770

古装韩剧里经常见到的房屋

DSC01785

内舍,女人居住的地方

DSC01794

大体就这样,虽然这里是古代韩国的主要宫殿,但和我们的故宫真是无法相比,规模气势都逊色很多,而且其整体建筑风格应该也是模仿故宫,在很多城门和大殿上也都书写着汉字名称(韩国直到十五世纪才有自己的文字),不过很多宫殿的设计还是体现了韩国独有的人文生活习俗,整个景区走一遍也不过一个小时,出来时,天色将晚,该回去了,天气真的很冷。

DSC01797

12月31日

辞旧迎新

  韩国时间现在已经是2008年了,时间就是这样在不知不觉中溜走,2007就这样成了记忆。
  外面真的好冷,尤其起风的时候,有点回到哈尔滨的意思,第一次遭遇到韩国堵车,可能都是回家团聚的缘故,平日里热闹的饭店,酒吧显得很清冷,所以当我们进入“酒瓮”时,几乎没有其他的客人,这是一家非常特别的酒肆,里面是韩国农社的陈设,很多东西我看了都觉得很熟悉,进门抬头就能看见很多簸箕,斗笠,高粱粟子等等,窗台上挂满了玉米,辣椒,过道处还放了把犁头,和我家的有些不同,这个是螺旋形的,自从离开家乡这些东西都已经很少有机会看到了。老板娘把我们带到了一处宽敞的地方,大家席地而坐,真有点坐在家里炕头的感觉,放心,有地热的。可到了点菜的时候大家就傻眼了,老板娘不断的给我们show一个汤,我们也不知道是什么,事后证明这个应该是她们的招牌菜,没办法只好打电话到酒店找金先生,告诉他我们希望7万块解决7个人的温饱问题,如此这般后,我们最终点了2个海鲜煎饼,一个鸡骨汤,一个豆腐汤,还有一坛说不上名字的酒,直接音译过来叫担担酒,应该是米酒之类的,不过喝起来有很大的人参味道,吃起来总算味道不错,大家就这样告别了2007年。

再到韩国

  元旦放假了,可是我却出差,昨天的行程一切都很顺利,新年将至,外出的人不是很多,所以飞机上有很多空位,倒是遇到很多到韩国旅行的,相比于深圳的温度,外面真的有些冷了,呼口气都可以看到水气,这时候吃一顿大骨汤确实很适宜,酒店的地热温度很高,晚上睡觉感觉口很干,打电话到Infor Bar,却告知温度是按照楼层调节的,不能单独调房间,郁闷,害得我喝了一晚上的水。
  在韩国看电视真的很爽,当然不是因为我在深圳看不到电视的原因,而是这边很多电视节目都已经是高清,之前只是停留在概念上的差别,当你看到真正的高清节目后,才知道什么是视觉上的享受,所以你看到网上很多韩国电视娱乐节目的录像都很清楚,韩国的出租车里面都可以看到地面广播的高清电视,至于我们国家的高清计划就不多评论了。偶尔也可以看到国语节目,上次来的时候就看了很多集的三国演义,当然有一种节目你不用care是什么语言,只看不听就可以让你热血沸腾,没错是--足球,最舒服的就是可以看欧洲顶级足球联赛了,不过更幸运的是看到了皇马和巴萨的世纪之战,这场比赛前皇马带着4分优势来到诺坎普,皇马是带着1分优势回家还是7分回家,意义可是大不同。如果哪一天可以亲临现场观战,感受就不是一般的震撼了,希望我可以有这个机会,综观全场,皇马的中后场防守做的非常完美,那个曾经赢得伯纳乌掌声的小罗,那个曾经风一样过掉拉莫斯的小罗,如今显得有些平庸,面对拉莫斯和佩佩的防守,速度和技术没有任何优势,皇马赢得比赛当之无愧。今年的皇马终于复苏了,Raul本赛季焕发新的青春,进球后的庆祝动作也有新的改变,曾经的伯纳乌王子进球后展翅飞翔,意气风发,到现在的王者归来,尽显大气,希望皇马能够在08年卫冕成功。
12月15日

原来你也在这里

    生活中有许多意料不到的事,可能你觉得不可能,难以置信,但它确实还是发生了。
    昨天下班坐公车,快到家的时候,老婆的电话来了。
  “我快到了,做370差一站在等红灯。”
  “我也做370差一站在等红灯,你是不是在后面一辆?”我说。西丽这边是370的终点站,而这个路口的红灯又比较久,所以经常会有几辆车一起等。
  “没有啊,我在前面一辆啊”,老婆肯定地回答。
  “那我前面也没有车了啊”, 公交车前面就是斑马线了。这时候我四周看了一下,发现在我前面第三排有个女孩在打电话,手上戴的戒指有点眼熟耶,再看头型背影几分形似。
  “老婆,你能回头看一下吗?”我还不是很确定。
   这时候前面的女孩应声转过脸来,晕掉,怎么可能...原来你也在这里!
   
   (二十分钟前..大冲站)
   等公交车,真是很烦,当你不做某某路公交车时,你会发现它一辆接一辆的来,而你等的车就好想消失一样,每次都要考验你的忍耐极限。所以当370来的时候,你几乎没有什么心情去看车里的人而是直接找个座,然后就是外面熟悉而陌生的一切了。
 
   (十五分钟前..科技园站)
   老婆听到是科技园站,下意识的往前后门看了一下,因为她以为我可能会在这站上车,可是发现没有人上车,然后就没有再看。
 
   就这样,发生了后面的一切,想起来有些搞笑,却让我们感到一分意外的惊喜。
12月10日

水原行宫

工作的日子很是辛苦,不想多提。

好不容易挨到一个周末,和同事去参观一下水原,最有名的当然是华城行宫了。古时的王在水原的一个行宫,相当于我们的避暑山庄吧。

DSC01597

这里是电视剧<<大长今>>的一个拍摄地,先来个合影。

DSC01600

这颗大榉树有些年头,行宫建造之间就有了,很多人在这里许下美好的愿望。

复件 DSC01603

进门右手就是执事厅

DSC01607

整个行宫不是很大,只有两道门就到了尽头--奉寿堂,王处理事务的地方。

DSC01612

从后山的花园上俯瞰整个行宫,你就知道她到底有多大,有几间房了。

DSC01622

这是内堂,居住的地方,应该是女性居住地

DSC01639

维兴宅--王居住的地方

DSC01645

到处都是拍摄大长今的痕迹。

 复件 DSC01642 

DSC01631 

宫廷某司的大印,盖一张,墨不多,无法分辨。

 DSC01636

各种乐器

DSC01658

编钟

DSC01660

行宫的安防部队--南军营,还有一个北军营,其着装有着我国清朝部队的影子。

DSC01667

整个行宫也就走完了,不是很大,不过麻雀随小,五脏俱全,在这里可以找到各个职能部门及宫廷生活的影子,是古代王朝的一个缩影了。

11月19日

出差韩国

出国的手续真是麻烦,光是各种资料就要七八种,还好都是HR帮忙办理的手续。

深圳去韩国的飞机是有的,不过班次很少,不利于各种该签,所以公司都是定的香港的班机,深圳到香港非常方便,几个口岸都畅通无阻,这次走的是皇岗口岸,到了那里会有专车把你送到香港机场,口岸到机场大约要一小时,途中还要经过青马大桥,汽车飞驰在桥上,轮船穿梭在桥下,繁忙的码头见证着香港的繁荣。

IMG_4034

过了青马大桥不久,还会见到去迪斯尼的路标。

香港国际机场候机室

DSC01564

国航的奥运飞机,五个可爱的福娃

DSC01567

下午两点的飞机,到韩国是已经是傍晚6点,由于和我们国家有一个小时的时差,当地时间已经是晚上7点了,出了机场就到兑换点换了些韩币,汇率是1:122,韩币的最大面值是一万,最小的是10块的硬币。

DSC01589 

从地理位置上看,韩国和我们山东省在一个纬度上,尽管是冬天,我却不太感觉到冷。

从首尔机场到Suwon(水原市)还有1个多小时的车程,由于事先没有预约酒店,所以到了Suwon的酒店后,被安排到另一家Motel,事后发现那家酒店比公司指定的酒店好多了。

安顿好后,同事带我去吃有名的大骨汤,这家店的装饰着实购简单,里面是长条桌子和大木椅

DSC01715

这么冷的天气,来份热乎的大骨汤确实很惬意,两个泡菜(辣白菜和萝卜),一碗米饭,一碗排骨汤,骨头煮得很烂,汤有些微辣,就这样一顿,韩币是5000块。

DSC01717

走在大街上才知道,住的附近可是有名的红灯区啊,灯红酒绿,车水马龙,地上和车上到处都是各种广告单子,内容就不言而喻了,穿着暴露的靓女往来在大街之上,随时都有人过来和你打招呼,张口就是"massage",见你和他说No,他就大约猜到我们是中国人,还能来一句"按摩"。

DSC01720

后来的日子更是发现,这地方就算到了凌晨的四五点钟都是热闹非凡,果然是不夜之地。