Linux

核心中的哪個文件指定 fork(), vfork()… 使用 sys_clone() 系統呼叫

  • November 5, 2020

ltrace用於跟踪系統呼叫時,我可以看到 fork() 使用 sys_clone() 而不是 sys_fork()。但是我找不到定義它的linux原始碼。

我的程序是:

#include<stdio.h>

main()
{
       int pid,i=0,j=0;
       pid=fork();
       if(pid==0)
               printf("\nI am child\n");
       else
               printf("\nI am parent\n");

}

ltrace輸出是:

SYS_brk(NULL)                                                                               = 0x019d0000
SYS_access("/etc/ld.so.nohwcap", 00)                                                        = -2
SYS_mmap(0, 8192, 3, 34, 0xffffffff)                                                        = 0x7fe3cf84f000
SYS_access("/etc/ld.so.preload", 04)                                                        = -2
SYS_open("/etc/ld.so.cache", 0, 01)                                                         = 3
SYS_fstat(3, 0x7fff47007890)                                                                = 0
SYS_mmap(0, 103967, 1, 2, 3)                                                                = 0x7fe3cf835000
SYS_close(3)                                                                                = 0
SYS_access("/etc/ld.so.nohwcap", 00)                                                        = -2
SYS_open("/lib/x86_64-linux-gnu/libc.so.6", 0, 00)                                          = 3
SYS_read(3, "\177ELF\002\001\001", 832)                                                     = 832
SYS_fstat(3, 0x7fff470078e0)                                                                = 0
SYS_mmap(0, 0x389858, 5, 2050, 3)                                                           = 0x7fe3cf2a8000
SYS_mprotect(0x7fe3cf428000, 2097152, 0)                                                    = 0
SYS_mmap(0x7fe3cf628000, 20480, 3, 2066, 3)                                                 = 0x7fe3cf628000
SYS_mmap(0x7fe3cf62d000, 18520, 3, 50, 0xffffffff)                                          = 0x7fe3cf62d000
SYS_close(3)                                                                                = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf834000
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf833000
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf832000
SYS_arch_prctl(4098, 0x7fe3cf833700, 0x7fe3cf832000, 34, 0xffffffff)                        = 0
SYS_mprotect(0x7fe3cf628000, 16384, 1)                                                      = 0
SYS_mprotect(0x7fe3cf851000, 4096, 1)                                                       = 0
SYS_munmap(0x7fe3cf835000, 103967)                                                          = 0
__libc_start_main(0x40054c, 1, 0x7fff47008298, 0x4005a0, 0x400590 <unfinished ...>
fork( <unfinished ...>
SYS_clone(0x1200011, 0, 0, 0x7fe3cf8339d0, 0)                                               = 5967
<... fork resumed> )                                                                        = 5967
puts("\nI am parent" <unfinished ...>
SYS_fstat(1, 0x7fff47008060)                                                                = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff
)                                                        = 0x7fe3cf84e000
I am child
SYS_write(1, "\n", 1
)                                                                       = 1
SYS_write(1, "I am parent\n", 12)                                                           = -512
--- SIGCHLD (Child exited) ---
SYS_write(1, "I am parent\n", 12I am parent
)                                                           = 12
<... puts resumed> )                                                                        = 13
SYS_exit_group(13 <no return ...>
+++ exited (status 13) +++

glibc 中的fork()andvfork()包裝器是通過clone()系統呼叫實現的。為了更好地理解 和 之間的關係fork()clone()我們必須考慮 Linux 中程序和執行緒之間的關係。

傳統上,fork()會複製父程序擁有的所有資源並將副本分配給子程序。這種方法會產生相當大的成本,如果孩子立即呼叫exec(). 在 Linux 中,fork()利用寫時複製頁面來延遲或完全避免複製可以在父程序和子程序之間共享的數據。因此,正常期間產生的唯一成本是fork()複制父頁表和為task_struct子分配唯一的程序描述符結構。

Linux 還對執行緒採取了一種特殊的方法。在 Linux 中,執行緒只是與其他程序共享一些資源的普通程序。與其他作業系統(如 Windows 或 Solaris)相比,這是一種完全不同的執行緒方法,其中程序和執行緒是完全不同種類的野獸。在 Linux 中,每個執行緒都有自己的一個普通執行緒task_struct,它恰好被設置為與父程序共享某些資源,例如地址空間。

系統呼叫的flags參數clone()包括一組標誌,這些標誌指示父程序和子程序應該共享哪些資源(如果有的話)。程序和執行緒都是通過創建clone()的,唯一的區別是傳遞給的標誌集clone()

法線fork()可以實現為:

clone(SIGCHLD, 0);

這將創建一個不與其父級共享任何資源的任務,並設置為SIGCHLD在其退出時向父級發送終止信號。

相反,與父級共享地址空間、文件系統資源、文件描述符和信號處理程序的任務,換句話說,執行緒,可以通過以下方式創建:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

vfork()反過來是通過一個單獨的CLONE_VFORK標誌實現的,這將導致父程序休眠,直到子程序通過信號喚醒它。子程序將是父程序命名空間中唯一的執行執行緒,直到它呼叫exec()或退出。不允許孩子寫入記憶體。對應的clone()呼叫可能如下:

clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)

的實現sys_clone()是特定於架構的,但大部分工作發生do_fork()kernel/fork.c. 此函式呼叫 static clone_process(),它創建一個新程序作為父程序的副本,但尚未啟動它。clone_process()複製寄存器,為新任務分配 PID,並複製或共享由 clone 指定的程序環境的適當部分flagsclone_process()返回時,將do_clone()喚醒新創建的程序並安排它執行。

引用自:https://unix.stackexchange.com/questions/87551