星期二, 12月 21, 2010

What is linux-gate.so.1

What is linux-gate.so.1 :
http://www.trilithium.com/johan/2005/08/linux-gate/

中譯:
http://www.builder.com.cn/2008/0327/784518.shtml

以下是中文版轉貼(簡轉繁):



Linux-gate.so.1的含義?(What is linux-gate.so.1)


當你在一個比較新的linux系統下使用ldd命令時,你會經常看到一個比較奇怪的文件名,即linux-gate.so.1:

ldd /bin/sh
linux-gate.so.1 => (0xffffe000)
libdl.so.2 => /lib/libdl.so.2 (0xb7fb2000)
libc.so.6 => /lib/libc.so.6 (0xb7e7c000)
/lib/ld-linux.so.2 (0xb7fba000)

它到底是什麼文件呢?僅僅是一個動態載入庫(dynamically loaded library),對嗎?

對於廣義的動態載入庫定義來說,有那麼幾分意思。輸出缺少文件名暗示了ldd不能找到這個文件的位置。事實上,任何試圖找到這個文件的做法——無論是手工查找還是利用自動載入和分析這種庫文件的軟件——都不會成功。

一些用戶時常因為尋找並不存在的系統文件而暈頭轉向並屢屢受挫。對於這種根本就不會有結果的尋找,你可以很自信的告訴用戶:linux-gate.so.1文件目前在文件系統中根本就不被支持;它只是一個虛擬的DSO(譯者注virtual DSO:dynamically shared object),一個在每個進程的存儲空間(process' memory)指定的地址點被內核暴露出來的共享對象:

cat /proc/self/maps
08048000-0804c000 r-xp 00000000 08:03 7971106 /bin/cat
0804c000-0804d000 rwxp 00003000 08:03 7971106 /bin/cat
0804d000-0806e000 rwxp 0804d000 00:00 0 [heap]
b7e88000-b7e89000 rwxp b7e88000 00:00 0
b7e89000-b7fb8000 r-xp 00000000 08:03 8856588 /lib/libc-2.3.5.so
b7fb8000-b7fb9000 r-xp 0012e000 08:03 8856588 /lib/libc-2.3.5.so
b7fb9000-b7fbc000 rwxp 0012f000 08:03 8856588 /lib/libc-2.3.5.so
b7fbc000-b7fbe000 rwxp b7fbc000 00:00 0
b7fc2000-b7fd9000 r-xp 00000000 08:03 8856915 /lib/ld-2.3.5.so
b7fd9000-b7fdb000 rwxp 00016000 08:03 8856915 /lib/ld-2.3.5.so
bfac3000-bfad9000 rw-p bfac3000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]

這裡,cat輸出了它自己的內存映射。標記[vdso]的行是該進程的linux-gate.so.1對象,一個映射到地址為ffffe000的單獨的內存頁面。程序可以通過查詢ELF輔助向量中的AT_SYSINFO入口點來獲取內存中共享對象的位置。和程序參數(argv)和環境變量(envp)類似,輔助向量(auxv)是一個指向新進程的指針數組。

理論上進程之間的地址可能是不同的,但是,據我所知linux內核總是把它映射到一個固定的位置。上面例子的輸出來自一個x86 box,那裡進程存在於無格式老的32位地址空間,空間頁面大小為4096字節,使得ffffe000為倒數第二個頁面。真正的最後一個頁面被保存用來捕捉通過無效指針訪問,例如,對一個消耗掉的NULL指針或者從mmap返回的MAP_FAILED指針進行解除參照。

因為所有的進程在相同的位置共享相同的對象,所以如果我們想進一步查看,很容易的就可以摘錄出它的一個副本。例如,我們可以簡單的使用dd來從它自己的空間中得到這個頁面(謹慎的選擇一個與linux-gate.so.1不同的輸出名以免創建已存在的文件):

dd if=/proc/self/mem of=linux-gate.dso bs=4096 skip=1048574 count=1
1+0 records in
1+0 records out

我們跳過1048574個頁面是因為總共有220=1048576個頁面,我們想要的是與最後一個頁面相鄰的那個頁面。和其它的共享ELF對象文件類似,結果如下:

file -b linux-gate.dso
ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), stripped

objdump -T linux-gate.dso

linux-gate.dso: file format elf32-i386

DYNAMIC SYMBOL TABLE:
ffffe400 l d .text 00000000
ffffe460 l d .eh_frame_hdr 00000000
ffffe484 l d .eh_frame 00000000
ffffe608 l d .useless 00000000
ffffe400 g DF .text 00000014 LINUX_2.5 __kernel_vsyscall
00000000 g DO *ABS* 00000000 LINUX_2.5 LINUX_2.5
ffffe440 g DF .text 00000007 LINUX_2.5 __kernel_rt_sigreturn
ffffe420 g DF .text 00000008 LINUX_2.5 __kernel_sigreturn

這些符號是rt_sigreturn/sigreturn函數的入口點,也是為了虛擬系統調用(virtual system call)用而設置。在x86平台上,linux-gate.so.1一開始的名字是linux-vsyscall.so.1,但是在開發過程中,為了使用一個通俗的名字而被修改了,從而精確的反映了在跨平台下的目的:在用戶和內核空間之間起到網關(gateway)的作用。並不是所有的系統都需要虛擬系統調用,但是對x86它們一定同等重要,從而來保證這個精細的機制。

傳統中,x86系統調用通過中斷來實現。你可能還記得在不方便的老的MS-DOS時代,請求操作系統函數的方式是通過中斷33(21h)。Windows系統調用隱藏在用戶模式API層,但在一些地方它們同樣深入到了int ox2e。在linux和其它的*nix內核中類似的實現使用int 0x80。

然而,在很多x86處理器家族中的新成員中,通過使用中斷來調用系統調用的效率被證明是「相當」(^_^)低的。一個0x80系統調用單從命令來說,在2GHz Pentium上要比在一個850MHz Pentium Ⅲ上慢很多很多。因為這個原因而產生的在執行上的影響是明顯的,至少對於執行大量系統調用的應用程序是這樣的。

Intel在早期就注意到了這個問題,並且引進了一個更有效的以sysenter和sysexit的形式的系統調用接口。快速系統調用特色最初在Pentium Pro處理器中出現,但是由於硬件上的bug,它實際上被早期的大部分CPU所拋棄(broken)。這就是你可能看到PentiumⅡ甚至Pentium Ⅲ引入了sysenter的聲明的原因了。

硬件的問題也可以幫助解釋為什麼在操作系統開始支持快速系統調用之前經歷了很長時間。如果我們忽略早期的實驗性質的片段(patches),Linux對sysenter的支持出現在2002年12月,那時內核2.5正在開發中。距離sysenter的提出已經有10年了!Micorsoft在Windows XP中才剛剛使用sysenter。

如果你的Linux機器在系統調用上使用了sysenter指令,你可以通過反彙編__kernel_vsyscall找到:

objdump -d --start-address=0xffffe400 --stop-address=0xffffe414 linux-gate.dso

linux-gate.dso: file format elf32-i386

Disassembly of section .text:

ffffe400 <__kernel_vsyscall>:
ffffe400: 51 push %ecx
ffffe401: 52 push %edx
ffffe402: 55 push %ebp
ffffe403: 89 e5 mov %esp,%ebp
ffffe405: 0f 34 sysenter
ffffe407: 90 nop
ffffe408: 90 nop
ffffe409: 90 nop
ffffe40a: 90 nop
ffffe40b: 90 nop
ffffe40c: 90 nop
ffffe40d: 90 nop
ffffe40e: eb f3 jmp ffffe403 <__kernel_vsyscall data-blogger-escaped-x3="x3">
ffffe410: 5d pop %ebp
ffffe411: 5a pop %edx
ffffe412: 59 pop %ecx
ffffe413: c3 ret

系統調用的首選調用策略在啟動時由內核確定,很明顯這個box使用了sysenter。在老式機器上你可能會看到int 0x80被使用。如果你正努力地想搞清楚這個跳轉(jump)的含義(像我第一次看到它一樣),你可能會興趣很濃的去學習以至於認為其原因是:Linus Torvalds is a disgusting pig and proud of it(使用六個參數來處理重起系統調用是一個小竅門)。

沒有留言: