我們以一個 'gethostbyname' 的例子來說明 NSS 的運作原理
假設我們想要查詢網路上某台 host 的 IP 位址,那麼 GNU C Library 手冊告訴我們說,有一個 gethostbyname() 的函數可以使用。
不過,在真正寫 code 時,其實並不建議呼叫此函數,在說明原因前,有一句必名言是大家一定要銘記在心的:
Multi-threaded safe code must be reentrant.
由於 gethostbyname() 並非 reentrant 的版本;所以,呼叫 gethostbyname() 並不是正確寫法,我們必須改成呼叫 reentrant 的版本:gethostbyname_r()。
NSS module 裡 non-reentrant 的函數,是不能使用在 multi-threaded applications 的設計上的,但是根據 NSS manual 的解釋,其實 gethostbyename() 與 gethostbyname_r() 都會呼叫到 NSS module 裡相同的函數,原因是 NSS module 只提供 reentrant 版本的 gethostbyname() 服務。
接下來讓我們來討論,gethostbyname() 與 gethostbyname_r() 倒底會呼叫到 NSS module 裡的哪一個函數。
_nss_service_function
當我們呼叫 gethostbyname_r() 函數時 (或 gethostbyname),假設我們在 /etc/nsswitch.conf 裡有一行這樣的設定:
hosts: files
那麼根據之前 Jollen 在 Blog 裡的說明,此時會使用到的 NSS module 會是 libnss_files.so.2,並且以下的 routine 會被呼叫:
_nss_files_gethostbyname_r
查看一下:
# nm /lib/libnss_files.so.2 |egrep "gethostbyname"
00004320 T _nss_files_gethostbyname2_r
00003ba0 T _nss_files_gethostbyname_r
現在我們終於知道了,當我們呼叫查詢的函數 'function' 時,在 libnss_SERVICE.so.2 裡的相對應函數便會被呼叫,此函數的 naming 方式為:
_nss_service_function
另外,NSS module 僅包含 reentrant 的版本。
最後我們要講的是,gethostbyename() 與 gethostbyname_r(),或是之前提到的 getpwnam() 都是實作在 libc.so.6 裡的。For example:
# nm /lib/libc.so.6 |egrep "T.gethostbyname"
000f72b0 T gethostbyname
000f7480 T gethostbyname2
000f78c0 T gethostbyname2_r@GLIBC_2.0
000f7660 T gethostbyname2_r@@GLIBC_2.1.2
000f7b80 T gethostbyname_r@GLIBC_2.0
000f7930 T gethostbyname_r@@GLIBC_2.1.2
# nm /lib/libc.so.6 |egrep "T.getpwnam"
000acf50 T getpwnam
000ad5c0 T getpwnam_r@GLIBC_2.0
000ad440 T getpwnam_r@@GLIBC_2.1.2
這就是為什麼我們在 ldd (or objdump) 時,都看不到 libnss_SERVICE.so.6 的道理.當我們做查詢時,NSS 才去載入對應的 service module。
Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue
您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw