1. 程式人生 > >kernel筆記——庫文件與系統調用

kernel筆記——庫文件與系統調用

lib64 root 映射 number 寄存器 頭文件 include pan all

庫文件

先從我們熟悉的c庫入手,理解系統調用(system call)。c代碼中調用printf函數,經歷了以下調用過程:

最終輸出的功能由內核中write調用完成,c庫封裝了系統調用。

對於以下hello world程序:

#include
int main()
{
  printf("Hello world.\n");
  return 0;
}

我們可以使用ldd查看程序依賴的庫文件:

linux # ldd hello
    linux-vdso.so.1 => (0x00007fff89fe2000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fd142094000
)     /lib64/ld-linux-x86-64.so.2 (0x00007fd1423f2000)

輸出結果中顯示了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.6
glibc-2.11.1-0.17.4

反過來,對某一rpm包,我們可以查其包含的內容:

linux # rpm -ql glibc-2.11.1-0.17.4
/etc/bindresvport.blacklist
/etc/default/nss
/etc/gai.conf
/etc/ld.so.cache
/etc/ld.so.conf
……

devel包中包含了c庫函數的頭文件,而普通包中不包含頭文件,可以使用rpm查詢對比開發包和普通包:

rpm -ql glibc-2.4
-31.77.88.4 rpm -ql glibc-devel-2.4-31.77.88.4

系統調用

每一個系統調用對應一個系統調用號(system call number),使用系統調用的過程就是將系統調用號和參數傳遞給內核。

使用objdump,可以對庫文件進行反匯編,以下對/lib64/libc.so.6進行反匯編,並查看getpid函數相應的部分匯編代碼:

00000000000933e0 <__getpid>:
……
933fa: 00
933fb: 85 c0           test %eax,%eax
933fd: 75 f0           jne 933ef <__getpid+0xf>
933ff: b8 27 00 00 00  mov $0x27,%eax
93404: 0f 05           syscall
93406: 85 d2           test %edx,%edx
……

在以上輸出中,mov指令將系統調用號0x27放入eax寄存器中,0x27作為syscall的參數,syscall完成調用getpid的工作。

系統調用與系統調用號對應關系在include/asm/unistd.h中定義,我們可以查到getpid相應的定義語句:

#define __NR_getpid 39
__SYSCALL(__NR_getpid, sys_getpid)

unistd.h定義了POSIX標準提供的系統調用,所有符合POSIX標準的Unix系統均提供該頭文件。

我們可以直接傳遞系統調用號給syscall函數,完成系統調用,以下程序說明了如何使用syscall直接完成getpid系統調用:

#define _GNU_SOURCE
#include
#include
#include
#include
int main(int argc, char *argv[])
{
pid_t tid;
tid = syscall(SYS_gettid);
printf("%d\n", tid);
}

因而總結來說,使用系統調用的方式有兩種:

  1. c庫中封裝了系統調用,通過c庫間接調用
  2. 傳遞系統調用號,通過syscall直接調用

第2種方式存在的意義在於,當kernel提供了新的系統調用,而c庫又沒有更新時,可以使用syscall調用新的系統調用。

Reference: Chapter 5 - System Calls, Linux kernel development.3rd.Edition

kernel筆記——庫文件與系統調用