系統調用的實現細節(用戶態)
該文檔以一個具體的事例來描述系統調用的細節。本文檔的事例在Ubuntu 14.04.4 LTS環境,CPU架構為x86_64,glibc版本為RELEASE = development,VERSION = 2.26.9000。
假如我們寫了一個應用程序test.c如下所示:
fork的函數申明在/usr/include/unistd.h頭文件中,如下所示:
我們知道gcc會在編譯完成test.c後,然後鏈接libc.so動態庫中的fork,所以fork的實現就在glibc代碼中,下載glibc源代碼,然而glibc中並沒有找到fork()的實現。
所以先做一個小實驗:編譯test.c,然後靜態鏈接libc.a,生成test,然後
反彙編可執行文件test,查看test.s
部分如下內容:
看到main函數實際調用的是__libc_fork。
__libc_fork實現在glibc工程sysdeps/nptl/fork.c路徑下,代碼略長,此處省略。
但是我們的應用程序調用fork的,是怎麼鏈接到__libc_fork的?在__libc_fork實現下方有如下代碼:
__weak_alias實現在glibc工程include/libc-symbols.h路徑下,如下:
現在,我們來看看__libc_fork函數,它調用系統功能的代碼如下:
可以看出是通過調用ARCH_FROK宏實現調用系統功能的。
該宏在glibc工程sysdeps/unix/sysv/linux/x86_64/arch-fork.h目錄下,如下代碼所示:
可以看出是通過INLINE_SYSCALL宏調用,傳遞的參數是clone,該宏在glibc工程sysdeps/unix/sysv/linux/x86_64/sysdep.h頭文件定義,與體系結構相關,代碼如下:
其中INTERNAL_SYSCALL定義在同文件中,代碼如下:
SYS_ify宏定義,代碼如下:
__NR_##syscall_name定義在ubuntu系統的/usr/include/x86_64-linux-gnu/asm/unistd_64.h文件中,它是系統調用具體調用系統函數的參數,它是一個編號,比如我們的fork系統調用,它實際通過__NR_clone標號傳參,它的定義如下:
#define __NR_clone 56
其中internal_syscall##nr相關的宏在glibc工程sysdeps/unix/sysv/linux/x86_64/sysdep.h定義,如下:
239 #undef internal_syscall0
240 #define internal_syscall0(number, err, dummy...)
241 ({
242 unsigned long int resultvar;
243 asm volatile (
244 "syscall
"
245 : "=a" (resultvar)
246 : "0" (number)
247 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
248 (long int) resultvar;
249 })
250
251 #undef internal_syscall1
252 #define internal_syscall1(number, err, arg1)
253 ({
254 unsigned long int resultvar;
255 TYPEFY (arg1, __arg1) = ARGIFY (arg1);
256 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;
257 asm volatile (
258 "syscall
"
259 : "=a" (resultvar)
260 : "0" (number), "r" (_a1)
261 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
262 (long int) resultvar;
263 })
264
265 #undef internal_syscall2
266 #define internal_syscall2(number, err, arg1, arg2)
267 ({
268 unsigned long int resultvar;
269 TYPEFY (arg2, __arg2) = ARGIFY (arg2);
270 TYPEFY (arg1, __arg1) = ARGIFY (arg1);
271 register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;
272 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;
273 asm volatile (
274 "syscall
"
275 : "=a" (resultvar)
276 : "0" (number), "r" (_a1), "r" (_a2)
277 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
278 (long int) resultvar;
279 })
280
281 #undef internal_syscall3
282 #define internal_syscall3(number, err, arg1, arg2, arg3)
283 ({
284 unsigned long int resultvar;
285 TYPEFY (arg3, __arg3) = ARGIFY (arg3);
286 TYPEFY (arg2, __arg2) = ARGIFY (arg2);
287 TYPEFY (arg1, __arg1) = ARGIFY (arg1);
288 register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;
289 register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;
290 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;
291 asm volatile (
292 "syscall
"
293 : "=a" (resultvar)
294 : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3)
295 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
296 (long int) resultvar;
297 })
299 #undef internal_syscall4
300 #define internal_syscall4(number, err, arg1, arg2, arg3, arg4)
301 ({
302 unsigned long int resultvar;
303 TYPEFY (arg4, __arg4) = ARGIFY (arg4);
304 TYPEFY (arg3, __arg3) = ARGIFY (arg3);
305 TYPEFY (arg2, __arg2) = ARGIFY (arg2);
306 TYPEFY (arg1, __arg1) = ARGIFY (arg1);
307 register TYPEFY (arg4, _a4) asm ("r10") = __arg4;
308 register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;
309 register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;
310 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;
311 asm volatile (
312 "syscall
"
313 : "=a" (resultvar)
314 : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4)
315 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
316 (long int) resultvar;
317 })
318
319 #undef internal_syscall5
320 #define internal_syscall5(number, err, arg1, arg2, arg3, arg4, arg5)
321 ({
322 unsigned long int resultvar;
323 TYPEFY (arg5, __arg5) = ARGIFY (arg5);
324 TYPEFY (arg4, __arg4) = ARGIFY (arg4);
325 TYPEFY (arg3, __arg3) = ARGIFY (arg3);
326 TYPEFY (arg2, __arg2) = ARGIFY (arg2);
327 TYPEFY (arg1, __arg1) = ARGIFY (arg1);
328 register TYPEFY (arg5, _a5) asm ("r8") = __arg5;
329 register TYPEFY (arg4, _a4) asm ("r10") = __arg4;
330 register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;
331 register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;
332 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;
333 asm volatile (
334 "syscall
"
335 : "=a" (resultvar)
336 : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4),
337 "r" (_a5)
338 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
339 (long int) resultvar;
340 })
341
342 #undef internal_syscall6
343 #define internal_syscall6(number, err, arg1, arg2, arg3, arg4, arg5, arg6)
344 ({
345 unsigned long int resultvar;
346 TYPEFY (arg6, __arg6) = ARGIFY (arg6);
347 TYPEFY (arg5, __arg5) = ARGIFY (arg5);
348 TYPEFY (arg4, __arg4) = ARGIFY (arg4);
349 TYPEFY (arg3, __arg3) = ARGIFY (arg3);
350 TYPEFY (arg2, __arg2) = ARGIFY (arg2);
351 TYPEFY (arg1, __arg1) = ARGIFY (arg1);
352 register TYPEFY (arg6, _a6) asm ("r9") = __arg6;
353 register TYPEFY (arg5, _a5) asm ("r8") = __arg5;
354 register TYPEFY (arg4, _a4) asm ("r10") = __arg4;
355 register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;
356 register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;
357 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;
358 asm volatile (
359 "syscall
"
360 : "=a" (resultvar)
361 : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4),
362 "r" (_a5), "r" (_a6)
363 : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);
364 (long int) resultvar;
365 })
可以看出internal_syscall##nr宏的number是每個系統調用內核中的入口函數向量地址,通過彙編syscall指令,實現系統調用,從用戶態到核心態。
其他體系架構的系統調用基本是這個流程,但從用戶態到核心態的彙編指令各不相同,如下:
本人水平有限,歡迎大家指正批評,歡迎私信聯繫我。
推薦閱讀: