转自:http://blog.chinaunix.net/uid-24774106-id-3427836.html

我们都知道,动态共享库里面的函数的共享的,这也是动态库的优势所在,就是节省内存。C 编译出来的可执行文件几乎都会用到libc的库,假如没有这个共享的技术,每个可执行文件都要占一份libc库的内存,这将是极大的内存浪费。 可是一直没搞明白,怎么样才能证明共享库里面函数的地址在物理内存层面是同一份?其实,这个问题的本质是程序里面的逻辑地址和物理内存地址之间是怎样映射的,说的再赤裸裸一点,就是我给你个逻辑地址,请你在物理内存中找到对应的地址,或者我给你个物理地址,请你把这个物理内存里面存的东西告诉我。

最近两天,发现了一篇很牛的博文,这个博文彻底解决了逻辑地址 线性地址 物理地址的内存映射(这里的线性地址是不是之前博文中的MVA,修改后的虚拟地址?)问题,作者的功力特别深厚,他十分kind的提供了一篇29页的pdf文档,此文章一出,就彻底终结这个问题了。那我为什么还要写这篇博文呢。作者以2.6.18内核为例,提供了两个内核模块和两个应用层的程序,我在自己的Ubuntu 12.04上花了时间完整的验证了文档里面PAE(Physical Address Extension)模式的地址映射,发现代码里面存在一些兼容性的问题,导致编译不过,主要是内核版本不同和gcc带来的一些小问题。所以我花了4个多小时才把这个实验完整的做下来。如果想通过做实验来加深理解的筒子可以参考我修改后的程序。我无意抄袭,还是那句话,光荣属于前辈。

下面的图来自Intel的手册64-ia-32-architectures-software-developer-vol-3a-part-1-manual ,很好的解释的逻辑地址到物理地址的映射。所谓逻辑地址,就是我们C 语言中取地址符后,看到的地址。
    
    采用原文的函数

 #include <stdio.h>
int main()
{
unsigned long tmp;
tmp = 0x12345678;
printf("tmp address:0x%08lX\n", &tmp);
return ;
}
tmp address:0xBF86D16C

输出的地址为0xBF86D16C,这个就是官方手册上说的逻辑地址。首先需要将逻辑地址转化成线性地址。然后将线性地址转化成物理地址。将逻辑地址转化成线性地址,就是江湖传说的分段机制,也就是上图下面的segmentation。

1.段式映射

临时变量tmp的逻辑地址0xBF86D16C就是偏移量(?),因为tmp位于栈中,IA-32提供了SS(Stack Segment)寄存器。

  1.  //arch/x86/kernel/process_32.c
    //-------------------------------------------
    void
    start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
    {
    set_user_gs(regs, );
    regs->fs = ;
    regs->ds = __USER_DS;
    regs->es = __USER_DS;
    regs->ss = __USER_DS;
    regs->cs = __USER_CS;
    regs->ip = new_ip;
    regs->sp = new_sp;
    /*
    * Free the old FP and other extended state
    */
    free_thread_xstate(current);
    }

    实际上有6个段寄存器,但是DS,ES ,SS的值是一样的,FS和GS都是0,这样其实6个段寄存器本质是两个:CS和DS。每个进程的6个寄存器是一样的,不同的是EIP和ESP。从上面的代码中也可以看到。

  1.  //arch/x86/kernel/process_32.c
    //-------------------------------------------
    void
    start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
    {
    set_user_gs(regs, );
    regs->fs = ;
    regs->ds = __USER_DS;
    regs->es = __USER_DS;
    regs->ss = __USER_DS;
    regs->cs = __USER_CS;
    regs->ip = new_ip;
    regs->sp = new_sp;
    /*
    * Free the old FP and other extended state
    */
    free_thread_xstate(current);
    }

__USER_CS(14*8 +3 = 115)的值展开二进制的结果为:

  1.   

__USER_DS(15*8 + 3 =123)的值展开二进制的结果为:

  1.    

上面的两组数字就是段选择符,段选择符有16位,其中含义如下图:

TI表示我要选择的段描述符是存在GDT中还是LDT中。GDT和LDT可以简单理解成两个表,每个表里面都存放这一组地址。

我们的CS和DS对应的TI位都是0,换句话说,我们要着的段描述符在GDT中。实际上,我们的Linux程序里用的段描述符总是选择GDT,几乎没有选择LDT的。毛德操老爷子说,只有像wine这种进程才会用到LDT这样的东西。

RPL表示特权等级,0表示最高权限,3表示无特权。之所以在

  1. #define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS*8+3)

有个+3,就是表示,我的段无特权,同时我的段描述符存在GDT这张表里面。前面的13位表示是GDT表的index,或者说是第几项。

接下来就是去GDT这张表,去找到我们要的段描述符。等等,我们一直很爽的叫着GDT,知道我们的DS段描述符是在index =15的位置,可是从来没有人告诉我们GDT这张表放在哪里。

GDTR横空出世了,GDT的地址就存放在GDTR这个寄存器里面。问题是怎么读出啦GDTR寄存器的值?

前面提到的博文作者写了一个内核模块,来提取GDTR,CR0 CR3  等的值,主干代码在下面:

  1.  static int my_get_info( char *buf, char **start, off_t off, int count )
    {
    int len = ;
    struct mm_struct *mm;
    mm = current->active_mm;
    cr0 = read_cr0();
    cr3 = read_cr3();
    cr4 = read_cr4();
    //asm(" sgdt gdtr");
    asm("sgdt %0":"=m"(gdtr));
    len += sprintf( buf+len, "cr4=%08X ", cr4 );
    len += sprintf( buf+len, "PSE=%X ", (cr4>>)& );
    len += sprintf( buf+len, "PAE=%X ", (cr4>>)& );
    len += sprintf( buf+len, "\n" );
    len += sprintf( buf+len, "cr3=%08X cr0=%08X\n",cr3,cr0);
    len += sprintf( buf+len, "pgd:0x%08X\n",(unsigned int)mm->pgd);
    len += sprintf( buf+len, "gdtr address:%lX, limit:%X\n", gdtr.address,gdtr.limit);
    // len += sprintf( buf+len, "cpu_gdt_table address:0x%08lX\n", cpu_gdt_table);
    return len;
    }

asm那句代码在我的gcc下编译不过,我修改了下。感兴趣的同学可以考虑下为啥编译不过。
    
    总之我们有办法取GDTR寄存器的值,从而找到了GDT这张表,然后从这张表里面着第16项(index=15),我们就能找到我们的DS段描述符。

  1.  root@manu:~/code/c/self/mm_addr# ./mem_map
    %ebp:0xBF86D178
    tmp address:0xBF86D16C
    cr4=000006F0 PSE= PAE=
    cr3=06E3C000 cr0=8005003B
    pgd:0xC6E3C000
    gdtr address:F7BB9000, limit:FF

国外大牛提供了一个叫做dram的内核模块,还有一个fileview的tool,这个tool+模块相互配合,能够读到物理地址里面对应的内容。这个内核模块是大杀器啊,我解决共享库迷惑就全靠在这个内核模块上了。作者是低于2.6.32的内核,我们是高于2.6.32的内核,所以稍加修改,就能用在我的Ubuntu上了。

可以算出GDT的地址为F7BB9000 - c0000000,然后用作者提供的工具fileview去看下内存内容

  1.  -----------------------------------------------------------
    gdtr : f7bb9000 - c0000000 = 37bb9000
    0000037BB9000 ................
    0000037BB9010 ................
    0000037BB9020 ................
    0000037BB9030 FF FF B9 F3 DF B7 ....a...........
    0000037BB9040 ................
    0000037BB9050 ................
    0000037BB9060 FF FF 9B CF FF FF CF ................
    0000037BB9070 FF FF FB CF FF FF F3 CF ................
    0000037BB9080 6B C0 EA BB 8B F7 k ..............
    0000037BB9090 FF FF 9A FF FF 9A ......@.........
    0000037BB90A0 FF FF ................
    0000037BB90B0 FF FF 9A ..............@.
    0000037BB90C0 FF FF 9A FF FF ..............@.
    0000037BB90D0 FF FF CF FF FF 8F ...........@)..
    0000037BB90E0 0C BC F7 ......@.........
    0000037BB90F0 6B C1 ........k .H....

OK ,我们取到了我们的DS段描述符:

  1. FF FF 00 00 00 F3 CF 00 = 00cff300 0000ffff

自己对照就能的出,BASE=0x00000000,费了半点的劲,最后的得出:
    分段机制是fake的,虚拟地址总是能线性地址。
    
    我们还可以得到其他有用的信息:

  1. S=1 非系统段
  2. G=1 以4096为单位
  3. DPL=0x11,内核态用户态均可访问

所以经过这么一番折腾,最终的结果是,虚拟地址总是等于线性地址。以后就不要再折腾了。

 

2.页式映射

有了线性地址,下一步就是获取物理地址了。

我的电脑采用了PAE,物理地址扩展分页机制,看下我的uname -ar

  1. uname -r
  2. 3.2.0-29-generic-pae

上面程序也正PAE=1也证明了我的的确确的采用了PAE. PAE要比常规分页稍稍复杂一点。

先讲讲啥是PAE。 目前的服务器基本都突破了4G的内存,很多PC都已经突破4G 了,我有同事就有16G 内存的PC,让我羡慕的直流口水。Intel通过把管脚从32增加到36,可以支持64G内存,但是,必须引入一种新的分页机制,把32位的线性地址转化成36位的物理地址,才能充分利用这64G的内存。

这个机制就是PAE : 
    1 引入一个页目录指针表PDPT,有4个64位的item组成。
    2 cr3寄存器中27位用来表示 页目录指针表PDPT的地址(32字节对齐,所以不需要32来表示)。 
    3 线性地址的高2位决定4个PDPT item的的哪一个。

    
    上图完整的描述了PAE模式下线性地址到物理地址的映射。稍微不好懂的就是40这个数字的含义:
     Intel手册里面有下面的句子:

1)A PDE is selected using the physical address defined as follows:
— Bits 51:12 are from PDPTEi.
— Bits 11:3 are bits 29:21 of the linear address.
— Bits 2:0 are 0.
2)PDE的bit7(PS位)决定了采用4K大小的页还是2M 大小的页。如果是2M 大小的页,上面的图针对的是4K 大小的页。2M大小的页采用这种模式:


    对于我们而言,我们采用的不是2M 大小的页,后面实验中我们可以看下PS位。所以这种2M的页的模式,后面我们就不讲了。

3)A PTE is selected using the physical address defined as follows:
— Bits 51:12 are from the PDE.      
— Bits 11:3 are bits 20:12 of the linear address.
— Bits 2:0 are 0.
4)获取最后的物理地址
— Bits 51:12 are from the PTE.
— Bits 11:0 are from the original linear address.

OK 回到我们的例子:

  1. 线性地址:
  2. 0xBF86D16C
  3. 0x           111111 100          0 0110 1101           0001 0110 1100

高2位是10,表示选择index =2 的那个PDPT item

  1. root@manu:~/code/c/self/mm_addr# ./mem_map
  2. %ebp:0xBF86D178
  3. tmp address:0xBF86D16C
  4. cr4=000006F0 PSE=1 PAE=1
  5. cr3=06E3C000 cr0=8005003B
  6. pgd:0xC6E3C000
  7. gdtr address:F7BB9000, limit:FF

CR3的值是0x06E3C000 ,注意下图,后5位ignore,所以真正的地址还是0x6E3C000.

我们看下0x6E3C000地址下存放的啥东西,再次祭出我们的dram神器:

  1. 0000006E3C000 01 B0 E3 06 00 00 00 00 01 60 3C 08 00 00 00 00 .........`<.....
  2. 0000006E3C010 01 50 3C 08 00 00 00 00 01 40 93 01 00 00 00 00 .P<......@......
  3. 0000006E3C020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  4. 0000006E3C030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  5. 0000006E3C040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  6. 0000006E3C050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  7. 0000006E3C060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  8. 0000006E3C070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  9. 0000006E3C080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  10. 0000006E3C090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  11. 0000006E3C0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  12. 0000006E3C0B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  13. 0000006E3C0C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  14. 0000006E3C0D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  15. 0000006E3C0E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  16. 0000006E3C0F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

蓝色的地址就是我们要找的:

  1. 01 50 3C 08 00 00 00 00 = 0x083c5001,

其实这是一个64位的地址,12~51位是页目录表项的基地址。
其中bit 0表示的是present,表示该64位地址是有效的。
其中bit7(PS位)没有置位,表明采用的页是4K 大小的页,而不是2M大小的页。
可以算出表项的基地址为:0x83c5000。

  1. 线性地址:
  2. 0xBF86D16C
  3. 0x 10          111111 100          0 0110 1101           0001 0110 1100

0x83c5000+()b *8= 0x83c5fe0。

看下这个地址下的内容:

  1. 00000083C5FB0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  2. 00000083C5FC0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  3. 00000083C5FD0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  4. 00000083C5FE0 67 70 D8 06 00 00 00 00 00 00 00 00 00 00 00 00 gp..............
  5. 00000083C5FF0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  6. 00000083C6000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  7. 00000083C6010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  8. 00000083C6020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  9. 00000083C6030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  10. 00000083C6040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  11. 00000083C6050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  12. 00000083C6060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  13. 00000083C6070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  14. 00000083C6080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  15. 00000083C6090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  16. 00000083C60A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000

取到的地址为

  1. 67 70 D8 06 00 00 00 00 = 0x6d87067

考虑到4096对齐和12~51位是有效地址, 页面目录表的地址为6d87000

  1. 线性地址:
  2. 0xBF86D16C
  3. 0x 10         111111 100        0 0110 1101            0001 0110 1100

0x6d87000 + (001101101)b *8  = 0x6d87368,
看下这个地址下的内容

  1. 0000006D87360 47 40 65 07 00 00 00 80 47 A0 94 0D 00 00 00 80 G@e.....G.......
  2. 0000006D87370 47 B0 BD 09 00 00 00 80 00 00 00 00 00 00 00 00 G...............
  3. 0000006D87380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  4. 0000006D87390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  5. 0000006D873A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  6. 0000006D873B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  7. 0000006D873C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  8. 0000006D873D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  9. 0000006D873E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  10. 0000006D873F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  11. 0000006D87400 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  12. 0000006D87410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  13. 0000006D87420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  14. 0000006D87430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  15. 0000006D87440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  16. 0000006D87450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

我们终于到了最后一个页表了:

  1. 47 A0 94 0D 00 00 00 80 = 0x80000000 0d94a047

最终物理地址计算
1)  12~51位来自 0x80000000 0d94a047
      换句话说就是:0d94a000      
2) 0 ~11来自线性地址的最后12位

  1. 线性地址:
  2. 0xBF86D16C
  3. 0x 10         111111 100                0 0110 1101          0001 0110 1100
  1. 0xd94a000 + (0001 0110 1100)b = 0x0d94a16c

OK ,最后总算得到了物理地址 0x0d94a16c.
用我们的神器看下物理地址的内容是不是0x12345678

  1. 000000D94A160 70 A2 7A B7 00 00 00 00 A9 86 04 08 78 56 34 12 p.z.........xV4.
  2. 000000D94A170 A0 86 04 08 00 00 00 00 00 00 00 00 D3 14 5F B7 .............._.
  3. 000000D94A180 01 00 00 00 14 D2 86 BF 1C D2 86 BF 58 98 79 B7 ............X.y.
  4. 000000D94A190 00 00 00 00 1C D2 86 BF 1C D2 86 BF 00 00 00 00 ................
  5. 000000D94A1A0 A0 82 04 08 F4 DF 77 B7 00 00 00 00 00 00 00 00 ......w.........
  6. 000000D94A1B0 00 00 00 00 A9 68 DD 32 B8 4C 57 81 00 00 00 00 .....h.2.LW.....
  7. 000000D94A1C0 00 00 00 00 00 00 00 00 01 00 00 00 A0 84 04 08 ................
  8. 000000D94A1D0 00 00 00 00 A0 F6 7A B7 E9 13 5F B7 F4 BF 7B B7 ......z..._...{.
  9. 000000D94A1E0 01 00 00 00 A0 84 04 08 00 00 00 00 C1 84 04 08 ................
  10. 000000D94A1F0 54 85 04 08 01 00 00 00 14 D2 86 BF A0 86 04 08 T...............
  11. 000000D94A200 10 87 04 08 70 A2 7A B7 0C D2 86 BF 18 C9 7B B7 ....p.z.......{.
  12. 000000D94A210 01 00 00 00 DF E8 86 BF 00 00 00 00 E9 E8 86 BF ................
  13. 000000D94A220 F9 E8 86 BF 04 E9 86 BF 0E E9 86 BF 2F EE 86 BF ............/...
  14. 000000D94A230 3E EE 86 BF 4C EE 86 BF 5A EE 86 BF 6E EE 86 BF >...L...Z...n...
  15. 000000D94A240 B0 EE 86 BF D3 EE 86 BF E4 EE 86 BF EC EE 86 BF ................
  16. 000000D94A250 03 EF 86 BF 13 EF 86 BF 25 EF 86 BF 32 EF 86 BF ........%...2...

看下右上角的蓝色0x12345678,那就是我们存储的tmp的值。。

再次感谢ilinuxkernel博主写的文档,让我解决了这个彻底解决了这个虚拟地址到物理地址的转换,我喜欢这样的文章,他让我更深刻的理解计算机的原理,这片博文绝大部分的贡献都是这位kind的博主,光荣属于前辈。

为了方便感兴趣的筒子顺利的做这个实验,我将这个修改后的代码放在github上。没有窃取原博主劳动成果的意思。
地址为:https://github.com/manuscola/mm_addr

plus:
fileview工具提供了按照字节,双字节 ,4字节,8字节的方式来展示内存内容,可惜我昨晚实验的时候,没好好看fileview的源代码,所以都是按照BYTE的方式展现物理内存的内容。后面有感兴趣的筒子想做实验的话,可以好好看下fileview的source code。

参考文献:
1Linux内存地址映射
2 深入理解计算机系统
3 深入理解linux内核
Linux用户程序如何访问物理内存
5 http://cs.usfca.edu/~cruse/cs635/

6 intel 官方手册64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf

Linux从逻辑地址到物理地址的更多相关文章

  1. Linux下逻辑地址、线性地址、物理地址详细总结

    Linux下逻辑地址.线性地址.物理地址详细总结 一.逻辑地址转线性地址      机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址 ...

  2. Linux下逻辑地址-线性地址-物理地址图解(转)

    一.逻辑地址转线性地址 机器语言指令中出现的内存地址,都是逻辑地址,需要转换成线性地址,再经过MMU(CPU中的内存管理单元)转换成物理地址才能够被访问到. 我们写个最简单的hello world程序 ...

  3. [转帖]Linux下逻辑地址、线性地址、物理地址详细总结

    Linux下逻辑地址.线性地址.物理地址详细总结 https://www.cnblogs.com/alantu2018/p/9002441.html 总结的挺好的 现在应该是段页式管理 使用MMU和T ...

  4. Linux文件寻址算法:逻辑地址到物理地址的转换

    题目描述: 编写一个函数实现Linux文件寻址的算法,即读取文件当前位置到物理存储位置的转换函数,需要给出运行的测试数据,可以假设和模拟需要的数据和结构.即编写一个函数unsigned long lt ...

  5. linux内核——PAE(物理地址扩展)

    引入PAE机制后,分页模式是怎样的呢? 首先,要搞明白几件事,2.6.11以上版本的linux内核中,存在4中页表(页全局目录,页上级目录,页中级目录,页表),这些页表结构是已经存在于硬盘中的,当进程 ...

  6. Linux驱动虚拟地址和物理地址的映射

    一般情况下,Linux系统中,进程的4GB内存空间被划分成为两个部分------用户空间和内核空间,大小分别为0~3G,3~4G. 用户进程通常情况下,只能访问用户空间的虚拟地址,不能访问到内核空间. ...

  7. Linux下直接读写物理地址内存

    虚拟 转 物理地址  virt_to_phys( *addr );物理 转 虚拟地址  phys_to_virt( *addr ); 如: unsigned long pProtectVA; phys ...

  8. Linux 内核虚拟地址到物理地址转换讨论【转】

    转自:https://blog.csdn.net/sunlei0625/article/details/59476987 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请 ...

  9. linux内存管理---虚拟地址、逻辑地址、线性地址、物理地址的区别(一)

    分析linux内存管理机制,离不了上述几个概念,在介绍上述几个概念之前,先从<深入理解linux内核>这本书中摘抄几段关于上述名词的解释: 一.<深入理解linux内核>的解释 ...

随机推荐

  1. Centos6下DRBD的安装配置

    导读 Distributed Replicated Block Device(DRBD)是一个用软件实现的.无共享的.服务器之间镜像块设备内容的存储复制解决方案.数据镜像:实时.透明.同步(所有服务器 ...

  2. ZOJ 1092 Arbitrage

    原题链接 题目大意:Arbitrage这个单词的解释是“套利交易”,就是利用几个币种之间的汇率差价来赚钱.比如人民币兑美元6:1,美元兑欧元1.5:1,欧元兑人民币10:1,那么用9元人民币可以换1. ...

  3. Sql Server_笔记

    1.随机取出10条数据:select top 10 * from tablename order by newid()

  4. leetcode 148. Sort List ----- java

    Sort a linked list in O(n log n) time using constant space complexity. 排序,要求是O(nlog(n))的时间复杂度和常数的空间复 ...

  5. leetcode 138. Copy List with Random Pointer ----- java

    A linked list is given such that each node contains an additional random pointer which could point t ...

  6. 故障模块名称: mso.dll

    本人今天早上打开word文档的时候打不开了,反复试了n次也不成,一想八成儿要重新装了,结果我点开详细信息看了一下,看到了“故障模块名称: mso.dll”这个提示,结果我就放到了百度上找了一下,都是只 ...

  7. 附录二 C语言标准库

    上章回顾 数组和指针相同与不同 通过指针访问数组和通过数组访问指针 指针在什么时候可以加减运算 函数指针的申明和调用 函数数组和数组函数 git@github.com:Kevin-Dfg/Data-S ...

  8. CentOS 6.0修改ssh远程连接端口

    转自:系统运维 » CentOS 6.0修改ssh远程连接端口 实现目的:把ssh默认远程连接端口修改为2222 方法如下: 1.编辑防火墙配置:vi /etc/sysconfig/iptables ...

  9. java的nio之:java的nio的原理

    转载:http://weixiaolu.iteye.com/blog/1479656 Java NIO原理图文分析及代码实现 前言: 最近在分析hadoop的RPC(Remote Procedure ...

  10. jquery mouseout事件错误(bug)

    移到子元素上时(例如,处在div中的图像),触发移出事件 (mouseout事件的一个常见错误). 解决办法是使用hover事件 在使用hover事件前,我抓耳挠腮的以为是margin或padding ...