clone() - Unix 和 Linux 系統呼叫 - 技術教學
Tutorials Point


  Unix 初學者指南
  Unix Shell 程式設計
  高階 Unix
  Unix 有用參考
  Unix 有用資源
  精選閱讀

版權所有 © 2014 tutorialspoint



  首頁     參考     討論論壇     關於 TP  

clone() - Unix 和 Linux 系統呼叫


previous next AddThis Social Bookmark Button

廣告

名稱

clone, __clone2 - 建立子程序

語法

#include <sched.h> 

int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *pid, struct user_desc *tls ", pid_t *" ctid " */ );"

int __clone2(int (*fn)(void *), void *child_stack_base, size_t stack_size, int flags, void *arg, ... /* pid_t *pid, struct user_desc *tls ", pid_t *" ctid " */ );"

描述

clone() 建立一個新的程序,其方式類似於 fork(2)。它實際上是一個構建在底層 clone() 系統呼叫之上的庫函式,以下簡稱 sys_clonesys_clone 的描述在本頁的末尾給出。

fork(2) 不同,這些呼叫允許子程序與其呼叫程序共享其執行上下文的一部分,例如記憶體空間、檔案描述符表和訊號處理程式表。(請注意,在本手冊頁中,“呼叫程序”通常對應於“父程序”。但請參見下面 CLONE_PARENT 的描述。)

clone() 的主要用途是實現執行緒:程式中多個併發執行在共享記憶體空間中的控制執行緒。

當使用 clone() 建立子程序時,它會執行函式應用 fn(arg)。(這與 fork(2) 不同,在 fork(2) 呼叫中,子程序從呼叫點繼續執行。)fn 引數是指向一個函式的指標,該函式在子程序執行開始時由子程序呼叫。arg 引數傳遞給 fn 函式。

fn(arg) 函式應用返回時,子程序終止。fn 返回的整數是子程序的退出程式碼。子程序也可以透過呼叫 exit(2) 或在接收到致命訊號後顯式終止。

child_stack 引數指定子程序使用的堆疊位置。由於子程序和呼叫程序可能會共享記憶體,因此子程序無法在與呼叫程序相同的堆疊中執行。因此,呼叫程序必須為子程序堆疊設定記憶體空間,並將指向此空間的指標傳遞給 clone()。在所有執行 Linux 的處理器上(除了 HP PA 處理器),堆疊都向下增長,因此 child_stack 通常指向為子程序堆疊設定的記憶體空間的最頂端地址。

flags 的低位元組包含子程序死亡時傳送到父程序的 終止訊號 的編號。如果此訊號指定為除 SIGCHLD 之外的任何內容,則父程序必須在使用 wait(2) 等待子程序時指定 __WALL__WCLONE 選項。如果未指定訊號,則子程序終止時不會向父程序傳送訊號。

.

flags 還可以按位或運算零個或多個以下常量,以便指定呼叫程序和子程序之間共享的內容

標籤描述
CLONE_PARENT (自 Linux 2.3.12 起)如果設定了 CLONE_PARENT,則新子程序的父程序(由 getppid(2) 返回)將與呼叫程序的父程序相同。

如果未設定 CLONE_PARENT,則(與 fork(2) 一樣)子程序的父程序是呼叫程序。

請注意,當子程序終止時,由 getppid(2) 返回的父程序會收到訊號,因此,如果設定了 CLONE_PARENT,則呼叫程序的父程序(而不是呼叫程序本身)將收到訊號。

CLONE_FS 如果設定了 CLONE_FS,則呼叫程序和子程序共享相同的檔案系統資訊。這包括檔案系統的根目錄、當前工作目錄和 umask。呼叫程序或子程序執行的任何 chroot(2)、chdir(2) 或 umask(2) 呼叫也會影響另一個程序。

如果未設定 CLONE_FS,則子程序會在 clone() 呼叫時使用呼叫程序的檔案系統資訊的副本進行工作。稍後由其中一個程序執行的 chroot(2)、chdir(2)、umask(2) 呼叫不會影響另一個程序。

CLONE_FILES 如果設定了 CLONE_FILES,則呼叫程序和子程序共享相同的檔案描述符表。呼叫程序或子程序建立的任何檔案描述符在另一個程序中也都有效。類似地,如果其中一個程序關閉檔案描述符或更改其關聯的標誌(使用 fcntl(2) F_SETFD 操作),則另一個程序也會受到影響。

如果未設定 CLONE_FILES,則子程序會繼承在 clone() 呼叫時在呼叫程序中開啟的所有檔案描述符的副本。(子程序中複製的檔案描述符引用與呼叫程序中相應檔案描述符相同的開啟檔案描述(請參見 open(2))。)稍後由呼叫程序或子程序執行的開啟或關閉檔案描述符或更改檔案描述符標誌的操作不會影響另一個程序。

CLONE_NEWNS (自 Linux 2.4.19 起)在新的名稱空間中啟動子程序。

每個程序都存在於一個名稱空間中。程序的 名稱空間 是描述該程序所見的檔案層次結構的資料(掛載集)。在 fork(2) 或 clone(2) 之後(其中未設定 CLONE_NEWNS 標誌),子程序與父程序位於相同的名稱空間中。系統呼叫 mount(2) 和 umount(2) 會更改呼叫程序的名稱空間,因此會影響位於同一名稱空間中的所有程序,但不會影響不同名稱空間中的程序。

clone(2) 之後(其中設定了 CLONE_NEWNS 標誌),克隆的子程序將在新的名稱空間中啟動,該名稱空間使用父程序名稱空間的副本進行初始化。

只有特權程序(擁有 CAP_SYS_ADMIN 功能的程序)才能指定 CLONE_NEWNS 標誌。不允許在同一個 clone() 呼叫中同時指定 CLONE_NEWNSCLONE_FS

CLONE_SIGHAND 如果設定了 CLONE_SIGHAND,則呼叫程序和子程序共享相同的訊號處理程式表。如果呼叫程序或子程序呼叫 sigaction(2) 來更改與訊號關聯的行為,則另一個程序中的行為也會更改。但是,呼叫程序和子程序仍然具有不同的訊號掩碼和掛起訊號集。因此,其中一個程序可以使用 sigprocmask(2) 阻塞或解除阻塞某些訊號,而不會影響另一個程序。

如果未設定 CLONE_SIGHAND,則子程序會在呼叫 clone() 時繼承呼叫程序的訊號處理程式的副本。稍後由其中一個程序執行的 sigaction(2) 呼叫不會影響另一個程序。

自 Linux 2.6.0-test6 起,如果指定了 CLONE_SIGHAND,則 flags 還必須包含 CLONE_VM

CLONE_PTRACE 如果指定了 CLONE_PTRACE,並且正在跟蹤呼叫程序,則也跟蹤子程序(請參見 ptrace(2))。
CLONE_UNTRACED (自 Linux 2.5.46 起)如果指定了 CLONE_UNTRACED,則跟蹤程序無法在此子程序上強制執行 CLONE_PTRACE
CLONE_STOPPED (自 Linux 2.6.0-test2 起)如果設定了 CLONE_STOPPED,則子程序最初處於停止狀態(就像它收到了 SIGSTOP 訊號一樣),並且必須透過傳送 SIGCONT 訊號來恢復它。
CLONE_VFORK 如果設定了 CLONE_VFORK,則呼叫程序的執行將暫停,直到子程序透過呼叫 execve(2) 或 _exit(2) 釋放其虛擬記憶體資源(與 vfork(2) 一樣)。

如果未設定 CLONE_VFORK,則呼叫程序和子程序在呼叫後都是可排程的,並且應用程式不應依賴於以任何特定順序發生的執行。

CLONE_VM 如果設定了 CLONE_VM,則呼叫程序和子程序在相同的記憶體空間中執行。特別是,呼叫程序或子程序執行的記憶體寫入在另一個程序中也可見。此外,子程序或呼叫程序使用 mmap(2) 或 munmap(2) 執行的任何記憶體對映或取消對映也會影響另一個程序。

如果未設定 CLONE_VM,則子程序會在 clone() 呼叫時在呼叫程序的記憶體空間的單獨副本中執行。由其中一個程序執行的記憶體寫入或檔案對映/取消對映不會影響另一個程序,就像 fork(2) 一樣。

CLONE_PID (已過時)如果設定了 CLONE_PID,則子程序將使用與呼叫程序相同的程序 ID 建立。這對於破解系統很有用,但在其他情況下用途不大。自 2.3.21 起,此標誌只能由系統引導程序(PID 0)指定。它在 Linux 2.5.16 中消失了。
CLONE_THREAD (自 Linux 2.4.0-test8 起)如果設定了 CLONE_THREAD,則子程序將放置在與呼叫程序相同的執行緒組中。為了使 CLONE_THREAD 的其餘討論更易於閱讀,術語“執行緒”用於指代執行緒組中的程序。

執行緒組是 Linux 2.4 中新增的功能,用於支援 POSIX 執行緒的概念,即共享單個 PID 的一組執行緒。在內部,此共享 PID 是執行緒組的執行緒組識別符號 (TGID)。自 Linux 2.4 起,對 getpid(2) 的呼叫返回呼叫者的 TGID。

執行緒組中的執行緒可以透過其(系統範圍的)唯一執行緒 ID (TID) 來區分。新執行緒的 TID 可作為返回給 clone() 呼叫者的函式結果獲得,執行緒可以使用 gettid(2) 獲取自己的 TID。

當在不指定 CLONE_THREAD 的情況下呼叫 clone() 時,生成的執行緒將放置在一個新的執行緒組中,該執行緒組的 TGID 與執行緒的 TID 相同。此執行緒是新執行緒組的 領導者

使用 CLONE_THREAD 建立的新執行緒與 clone() 的呼叫者的父程序相同(即,類似於 CLONE_PARENT),以便對 getppid(2) 的呼叫對執行緒組中的所有執行緒返回相同的值。當 CLONE_THREAD 執行緒終止時,使用 clone() 建立它的執行緒不會收到 SIGCHLD(或其他終止)訊號;也不能使用 wait(2) 獲取此類執行緒的狀態。(據說執行緒是 分離 的。)

當執行緒組中的所有執行緒都終止時,執行緒組的父程序會收到一個SIGCHLD(或其他終止)訊號。

如果執行緒組中的任何執行緒執行execve(2),則除了執行緒組領導者之外的所有執行緒都將終止,並且新程式將線上程組領導者中執行。

如果執行緒組中的一個執行緒使用fork(2)建立了一個子程序,則該組中的任何執行緒都可以使用wait(2)等待該子程序。

從 Linux 2.5.35 開始,如果指定了CLONE_THREAD,則flags也必須包含CLONE_SIGHAND

可以使用kill(2)將訊號傳送到整個執行緒組(即 TGID),或使用tgkill(2)傳送到特定執行緒(即 TID)。

訊號的處置和操作是程序範圍內的:如果未處理的訊號傳遞給一個執行緒,則它將影響(終止、停止、繼續、忽略)執行緒組中的所有成員。

每個執行緒都有自己的訊號掩碼,由sigprocmask(2)設定,但訊號可以處於以下兩種掛起狀態:對於整個程序(即可以傳遞給執行緒組中的任何成員),當使用kill(2)傳送時;或者對於單個執行緒,當使用tgkill(2)傳送時。呼叫sigpending(2)返回一個訊號集,該訊號集是整個程序掛起的訊號和呼叫執行緒掛起的訊號的並集。

如果使用kill(2)將訊號傳送到執行緒組,並且執行緒組已為該訊號安裝了處理程式,則處理程式將線上程組中恰好一個任意選擇的未阻塞該訊號的成員中被呼叫。如果組中的多個執行緒正在使用sigwaitinfo(2)等待接受相同的訊號,核心將任意選擇其中一個執行緒來接收使用kill(2)傳送的訊號。

CLONE_SYSVSEM(從 Linux 2.5.10 開始)如果設定了CLONE_SYSVSEM,則子程序和呼叫程序共享一個 System V 訊號量撤銷值列表(參見semop(2))。如果沒有設定此標誌,則子程序將擁有一個單獨的撤銷列表,該列表最初為空。
CLONE_SETTLS(從 Linux 2.5.32 開始)newtls引數是新的 TLS(執行緒區域性儲存)描述符。(參見set_thread_area(2)。)
CLONE_PARENT_SETTID(從 Linux 2.5.49 開始)在父程序和子程序的記憶體中,將子執行緒 ID 儲存在parent_tidptr指向的位置。(在 Linux 2.5.32-2.5.48 中,有一個名為 CLONE_SETTID 的標誌執行此操作。)
CLONE_CHILD_SETTID(從 Linux 2.5.49 開始)在子程序的記憶體中,將子執行緒 ID 儲存在child_tidptr指向的位置。
CLONE_CHILD_CLEARTID(從 Linux 2.5.49 開始)當子程序退出時,擦除子程序記憶體中child_tidptr指向位置的子執行緒 ID,並在該地址處的 futex 上執行喚醒操作。相關地址可以透過set_tid_address(2)系統呼叫更改。這被執行緒庫使用。

sys_clone

sys_clone系統呼叫與fork(2)更相似,因為子程序中的執行從呼叫的位置繼續。因此,sys_clone只需要flagschild_stack引數,這兩個引數與clone()中的含義相同。(注意,這兩個引數的順序與clone()不同。)

sys_clone的另一個區別是child_stack引數可以為零,在這種情況下,寫時複製語義確保當任一程序修改堆疊時,子程序獲得堆疊頁面的單獨副本。在這種情況下,為了正確操作,不應指定CLONE_VM選項。

從 Linux 2.5.49 開始,系統呼叫具有五個引數。這兩個新引數是parent_tidptr,它指向在父程序和子程序記憶體中子執行緒 ID 將寫入的位置(如果指定了 CLONE_PARENT_SETTID),以及child_tidptr,它指向在子程序記憶體中子執行緒 ID 將寫入的位置(如果指定了 CLONE_CHILD_SETTID)。

返回值

成功時,子程序的執行緒 ID 將在呼叫者的執行緒執行中返回。失敗時,將在呼叫者的上下文中返回 -1,不會建立子程序,並且errno將被適當地設定。

錯誤

標籤描述
EAGAIN 已經有太多程序正在執行。
EINVAL 指定了CLONE_SIGHAND,但未指定CLONE_VM。(從 Linux 2.6.0-test6 開始。)
EINVAL 指定了CLONE_THREAD,但未指定CLONE_SIGHAND。(從 Linux 2.5.35 開始。)
EINVAL flags中同時指定了CLONE_FSCLONE_NEWNS
EINVAL 當為child_stack指定零值時,由clone()返回。
ENOMEM 無法分配足夠的記憶體來為子程序分配任務結構,或複製需要複製的呼叫者上下文的那些部分。
EPERM 非根程序(沒有 CAP_SYS_ADMIN 許可權的程序)指定了CLONE_NEWNS
EPERM 程序 0 以外的程序指定了CLONE_PID

版本

libc5 中沒有clone()的條目。glibc2 提供瞭如本手冊頁所述的clone()。

符合標準

clone() 和 sys_clone 呼叫是 Linux 特定的,不應在旨在可移植的程式中使用。

備註

在核心 2.4.x 系列中,CLONE_THREAD 通常不會使新執行緒的父程序與呼叫程序的父程序相同。但是,對於核心版本 2.4.7 到 2.4.18,CLONE_THREAD 標誌暗示了CLONE_PARENT 標誌(如核心 2.6 中)。

曾經存在CLONE_DETACHED(在 2.5.32 中引入):父程序不希望收到子程序退出訊號。在 2.6.2 中,需要將其與CLONE_THREAD一起提供的需求消失了。此標誌仍然已定義,但沒有效果。在 x86 上,不應透過 vsyscall 呼叫clone(),而應直接透過int $0x80呼叫。在 IA-64 上,使用不同的系統呼叫。

int __clone2(int (*fn)(void *), void *child_stack_base, size_t stack_size, int flags, void *arg, ... /* pid_t *pid, struct user_desc *tls ", pid_t *" ctid " */ );"

__clone2() 系統呼叫的操作方式與clone()相同,只是child_stack_base指向子程序堆疊區域的最低地址,而stack_size指定child_stack_base指向的堆疊的大小。

錯誤

包含 NPTL 執行緒庫的 GNU C 庫版本包含一個用於getpid(2)的包裝函式,該函式執行 PID 的快取。在與這些庫連結的程式中,即使執行緒不是使用CLONE_THREAD建立的(因此不在同一執行緒組中),呼叫getpid(2)也可能返回相同的值。要獲得真實值,可能需要使用如下程式碼

#include <syscall.h>

pid_t mypid;

mypid = syscall(SYS_getpid);

參見



previous next Printer Friendly

廣告


  

廣告



廣告
© . All rights reserved.