以軟中斷或指令方式執行的系統調用,需要切換到內核空間,無論采用早期的int 0x80/iret中斷,還是sysenter/sysexit指令,再到syscall/sysexit指令,是一個比較慢的操作。例如像gettimeofday()這種,若每次為了從內核讀取時間值而都切換上下文的話,成本就太高了,這些系統調用並不會向內核提交參數,而僅僅只是從內核里請求讀取某個數據,,內核在處理這部分系統調用時可以把系統當前時間寫在一個固定的位置,而應用程序直接從該位置簡單讀取即可,無需發起系統調用。。所以Linux推出了一些措施,來幫助這些系統調用執行更快,那就是vsyscall和vdso。在64位系統上執行cat /proc/self/maps可以看到:
vsyscall
首先談vsyscall,這是內核最先引入的機制。內核直接把類似gettimeofday()這種功能簡單的函數的二進制實現,直接映射到任務的用戶空間中,這樣任務就可以在用戶空間把這些系統調用當成普通函數來用,這樣效率就大大提升。
但vsyscall有一些致命的問題,一是vsyscall的用戶空間映射所在的地址是固定不變的,容易被別有用心的人利用;二是vsyscall能支持的系統調用數有限,不利於擴展。所以,漸漸地一些替代方案就產生了。
vdso
vdso是vsyscall的主要替代方案,全稱是Virtual Dynamic Shared Object. 這是一個虛擬動態鏈接庫,內核把那些系統調用放到這里面,然后用戶程序在啟動的時候通過動態鏈接操作,把這個vdso鏈到自己的內存空間中來。動態鏈接保證了vdso每次所在的地址都不一樣,所以不太容易被利用,而且可以支持數量較多的系統調用。
Glibc封裝
Glibc為系統調用提供了封裝,保證使用的是當前機器上能夠支持的最快的調用。我們所要做的只是直接調用Glibc的接口函數而已,如gettimeofday。但是Linux也提供了syscall()來讓你直接通過中斷的方式進行系統調用
此處還要提一個函數名為__kernel_vsyscall,名字和vsyscall很像,不過正好相反,這個函數是與vdso相關的函數。根據《深入理解linux內核》,__kernel_vsyscall是為兼容int 0x80與sysenter兩種系統調用觸發方式,而存在的函數。但在x64上不再需要__kernel_vsyscall,因為它只有一種調用方式,即syscall指令。
在推薦另一篇blog:http://www.lenky.info/archives/2013/02/2199
vdso= [X86,SH] On X86_32, this is an alias for vdso32=. Otherwise: vdso=1: enable VDSO (the default) vdso=0: disable VDSO mapping vdso32= [X86] Control the 32-bit vDSO vdso32=1: enable 32-bit VDSO vdso32=0 or vdso32=2: disable 32-bit VDSO See the help text for CONFIG_COMPAT_VDSO for more details. If CONFIG_COMPAT_VDSO is set, the default is vdso32=0; otherwise, the default is vdso32=1. For compatibility with older kernels, vdso32=2 is an alias for vdso32=0. Try vdso32=0 if you encounter an error that says: dl_main: Assertion `(void *) ph->p_vaddr == _rtld_local._dl_sysinfo_dso' failed!
