庫文件與系統調用
庫文件
先從我們熟悉的c庫入手,理解系統調用(system call)。c代碼中調用printf函數,經歷了以下調用過程:
最終輸出的功能由內核中write調用完成,c庫封裝了系統調用。
對於以下hello world程序:
int main()n{n printf("Hello world.n");n return 0;n}n
我們可以使用ldd查看程序依賴的庫文件:
linux # ldd hellon linux-vdso.so.1 => (0x00007fff89fe2000)n libc.so.6 => /lib64/libc.so.6 (0x00007fd142094000)n /lib64/ld-linux-x86-64.so.2 (0x00007fd1423f2000)n
輸出結果中顯示了hello程序依賴的動態庫文件,其中linux-vdso.so.1指向進程虛擬內存地址,是一個虛擬的庫文件,在每個程序的虛擬內存都存在,其將內核態的調用映射到用戶地址空間中,使得調用開銷更小。
有些時候,在我們編譯程序時,會出現找不到某某lib的提示,又或者ldd查詢到某庫鏈接指示」not found」,這是因為ldd在/etc/ld.so.cache中不存在相應庫文件的查找路徑(使用strace跟蹤ldd命令可以看到ld.so.cache文件被讀取)。
要解決找不到某庫的問題,我們可以將庫文件路徑加到用戶的LIBRARY_PATH環境變數中,也可以添加到全局的/etc/ld.so.conf配置文件中,添加完後以root用戶執行ldconfig,以更新/etc/ld.so.cache緩存文件。
使用rpm命令可以查到動態庫屬於哪個rpm包:
linux # rpm -qf /lib64/libc.so.6nglibc-2.11.1-0.17.4n
反過來,對某一rpm包,我們可以查其包含的內容:
linux # rpm -ql glibc-2.11.1-0.17.4n/etc/bindresvport.blacklistn/etc/default/nssn/etc/gai.confn/etc/ld.so.cachen/etc/ld.so.confn……n
devel包中包含了c庫函數的頭文件,而普通包中不包含頭文件,可以使用rpm查詢對比開發包和普通包:
rpm -ql glibc-2.4-31.77.88.4nrpm -ql glibc-devel-2.4-31.77.88.4n
系統調用
每一個系統調用對應一個系統調用號(system call number),使用系統調用的過程就是將系統調用號和參數傳遞給內核。
使用objdump,可以對庫文件進行反彙編,以下對/lib64/libc.so.6進行反彙編,並查看getpid函數相應的部分彙編代碼:
00000000000933e0 <__getpid>:n……n933fa: 00n933fb: 85 c0 test %eax,%eaxn933fd: 75 f0 jne 933ef <__getpid+0xf>n933ff: b8 27 00 00 00 mov $0x27,%eaxn93404: 0f 05 syscalln93406: 85 d2 test %edx,%edxn……n
在以上輸出中,mov指令將系統調用號0x27放入eax寄存器中,0x27作為syscall的參數,syscall完成調用getpid的工作。
系統調用與系統調用號對應關係在include/asm/unistd.h中定義,我們可以查到getpid相應的定義語句:
#define __NR_getpid 39n__SYSCALL(__NR_getpid, sys_getpid)n
unistd.h定義了POSIX標準提供的系統調用,所有符合POSIX標準的Unix系統均提供該頭文件。
我們可以直接傳遞系統調用號給syscall函數,完成系統調用,以下程序說明了如何使用syscall直接完成getpid系統調用:
#define _GNU_SOURCEnint main(int argc, char *argv[])n{n pid_t tid;n tid = syscall(SYS_gettid);n printf("%dn", tid);n}n
因而總結來說,使用系統調用的方式有兩種:
- c庫中封裝了系統調用,通過c庫間接調用
- 傳遞系統調用號,通過syscall直接調用
第2種方式存在的意義在於,當kernel提供了新的系統調用,而c庫又沒有更新時,可以使用syscall調用新的系統調用。
Reference: Chapter 5 - System Calls, Linux kernel development.3rd.Edition
推薦閱讀:
※十年回顧-我的Linux之路
※openvswitch有什麼用?
※感覺autoconf真不太好用,有何替代方案?
※Linux頁表中虛擬內存地址如何映射到硬碟數據塊地址?
※mount --bind 可以算是ln一個目錄么?