zoukankan      html  css  js  c++  java
  • linux下的vdso与vsyscall

     

         传统的系统调用是怎样的?    —— int 0x80的时代

    ....             ;通过寄存器传参
    mov $n ,eax      ;将系统调用号放到eax中
    int 0x80


    sysenter/sysexit的出场

            在一个Kernel.org的邮件列表中,有一封邮件讨论了“"Intel P6 vs P7 system call performance”,最后得出的结论是采用传统的int 0x80的系统调用浪费了很多时间(具体原因可以看参考资料1),而sysenter/sysexit可以弥补这个缺点,所以决定在linux内核中用后都替换前者(最终在2.6版本的内核中才加入了此功能,即采用sysenter/sysexit)。

            在替换之前首先需要知道满足如下条件的ntel机器才会有sysenter/sysexit指令对:Family >= 6,Model >= 3,Stepping >= 3

            如何用替换sysenter/sysexit替换以前的int 0x80呢?linux kenerl 需要考虑到这点:有的机器并不支持sysenter/sysexit  , 于是它跟glibc说好了,“你以后调用系统调用的时候就从我给你的这个地址调用,这个地址指向的内容要么是int 0x80调用方式,要么是sysenter/sysexit调用方式,我会根据机器来选择其中一个”(kernel与glibc的配合是如此的默契),这个地址便是vsyscall的首地址。

             可以将vdso看成一个shared objdect file(这个文件实际上不存在),内核将其映射到某个地址空间,被所有程序所共享。(我觉得这里用到了一个技术:多个虚拟页面映射到同一个物理页面。即内核把vdso映射到某个物理页面上,然后所有程序都会有一个页表项指向它,以此来共享,这样每个程序的vdso地址就可以不相同了)

    hex108@ubuntu:~/program$ uname -a
    Linux ubuntu 2.6.35-22-generic #33-Ubuntu SMP Sun Sep 19 20:34:50 UTC 2010 i686 GNU/Linux
    hex108@ubuntu:~/program$ sudo sysctl -w kernel.randomize_va_space=0 //这个是必须的,否则vdso的地址是随机的(vsyscall的地址也会相应
                                                                            // 地发生变化 ),在下面dd的时候就会出现错误
                                                                            //dd: reading `/proc/self/mem': Input/output error
    kernel.randomize_va_space = 0
    hex108@ubuntu:~/program$ cat /proc/self/maps 
    00110000-0012c000 r-xp 00000000 08:01 260639     /lib/ld-2.12.1.so
    0012c000-0012d000 r--p 0001b000 08:01 260639     /lib/ld-2.12.1.so
    0012d000-0012e000 rw-p 0001c000 08:01 260639     /lib/ld-2.12.1.so
    0012e000-0012f000 r-xp 00000000 00:00 0          [vdso]
    0012f000-00286000 r-xp 00000000 08:01 260663     /lib/libc-2.12.1.so
    00286000-00287000 ---p 00157000 08:01 260663     /lib/libc-2.12.1.so
    00287000-00289000 r--p 00157000 08:01 260663     /lib/libc-2.12.1.so
    00289000-0028a000 rw-p 00159000 08:01 260663     /lib/libc-2.12.1.so
    0028a000-0028d000 rw-p 00000000 00:00 0 
    08048000-08051000 r-xp 00000000 08:01 130326     /bin/cat
    08051000-08052000 r--p 00008000 08:01 130326     /bin/cat
    08052000-08053000 rw-p 00009000 08:01 130326     /bin/cat
    08053000-08074000 rw-p 00000000 00:00 0          [heap]
    b7df0000-b7ff0000 r--p 00000000 08:01 660864     /usr/lib/locale/locale-archive
    b7ff0000-b7ff1000 rw-p 00000000 00:00 0 
    b7ffd000-b7ffe000 r--p 002a1000 08:01 660864     /usr/lib/locale/locale-archive
    b7ffe000-b8000000 rw-p 00000000 00:00 0 
    bffdf000-c0000000 rw-p 00000000 00:00 0          [stack]
    hex108@ubuntu:~/program$ dd if=/proc/self/mem of=gate.so bs=4096 skip=$[0x12e] count=1
    dd: `/proc/self/mem': cannot skip to specified offset
    1+0 records in
    1+0 records out
    4096 bytes (4.1 kB) copied, 0.00176447 s, 2.3 MB/s
    hex108@ubuntu:~/program$ file gate.so 
    gate.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped
    hex108@ubuntu:~/program$ objdump -d gate.so 
    
    gate.so:     file format elf32-i386
    
    
    Disassembly of section .text:
    
    ffffe400 <__kernel_sigreturn>:
    ffffe400:	58                   	pop    %eax
    ffffe401:	b8 77 00 00 00       	mov    $0x77,%eax
    ffffe406:	cd 80                	int    $0x80
    ffffe408:	90                   	nop
    ffffe409:	8d 76 00             	lea    0x0(%esi),%esi
    
    ffffe40c <__kernel_rt_sigreturn>:
    ffffe40c:	b8 ad 00 00 00       	mov    $0xad,%eax
    ffffe411:	cd 80                	int    $0x80
    ffffe413:	90                   	nop
    
    ffffe414 <__kernel_vsyscall>:
    ffffe414:	cd 80                	int    $0x80
    ffffe416:	c3                   	ret    
     

    syscall 才是最后的赢家?

             x86 64位从AMD引进了syscall指令(我在x86 64的机器上,看到的结果是syscall取代了sysenter/sysexit(所有的系统调用用的都是syscall)),但是vdso,vsyscall的机制依旧未变,只是kernel决定只在遇到以下几个系统调用gettimeofday,time和getcpu(通过内核里vsyscall.h中enum vsyscall_num的声明看出来,或者在glibc源代码中搜索“VSYSCALL_ADDR_”(

    #define VSYSCALL_ADDR_vgettimeofday    0xffffffffff600000

    #define VSYSCALL_ADDR_vtime            0xffffffffff600400

    #define VSYSCALL_ADDR_vgetcpu          0xffffffffff600800

    ))时才采用vdso机制(间接调用syscall,具体可以参看资料2),其他系统调用直接用指令syscall,原因是:


             "快速系统调用指令"比起中断指令来说,其消耗时间必然会少一些,但是随着 CPU 设计的发展,将来应该不会再出现类似 Intel Pentium4 这样悬殊的差距。而"快速系统调用指令"比起中断方式的系统调用方式,还存在一定局限,例如无法在一个系统调用处理过程中再通过"快速系统调用指令"调用别的系统调用。因此,并不一定每个系统调用都需要通过"快速系统调用指令"来实现。比如,对于复杂的系统调用例如 fork,两种系统调用方式的时间差和系统调用本身运行消耗的时间来比,可以忽略不计,此处采取"快速系统调用指令"方式没有什么必要。而真正应该使用"快速系统调用指令"方式的,是那些本身运行时间很短,对时间精确性要求高的系统调用,例如 getuid、gettimeofday 等等。因此,采取灵活的手段,针对不同的系统调用采取不同的方式,才能得到最优化的性能和实现最完美的功能。      ----引自参考资料1

  • 相关阅读:
    SQL_Server_2005_启用远程连接
    Oracle外网或内网远程连接
    还原数据库到指定时间点
    SQL Server账号密码(sa)登录失败 错误原因:18456
    sql server如何设置密码过期时间呢?
    安装SQL Server2014 :规则”Windows Management Instrumentation (WMI)服务 “失败
    impala
    dolphinscheduler
    829. Consecutive Numbers Sum 连续的数字求和
    472. Concatenated Words 查找自己拼出来的单词 反向word break
  • 原文地址:https://www.cnblogs.com/SZLLQ2000/p/5102386.html
Copyright © 2011-2022 走看看