%E4%B8%AD%E7%9A%84parent_tidptr%E5%92%8Cchild_tidptr%E6%98%AF%E4%BB%80%E9%BA%BC%EF%BC%9F.png)
就上述關聯parent_tidptr
創建新流程child_tidptr
是什麼意思?do_fork()
答案1
讓我們先查看原始系統呼叫介面。它因體系結構而略有不同,但在 x86-64 上是:
long clone(unsigned long flags, void *child_stack,
int *ptid, int *ctid,
unsigned long newtls);
ptid
是ctid
你的parent_tidptr
和child_tidptr
。現在讓我們看看是什麼clone(2)
手冊頁必須說:
CLONE_CHILD_CLEARTID (since Linux 2.5.49)
Erase the child thread ID at the location ctid in
child memory when the child exits, and do a wakeup on
the futex at that address.
CLONE_CHILD_SETTID (since Linux 2.5.49)
Store the child thread ID at the location ctid in the
child's memory.
CLONE_PARENT_SETTID (since Linux 2.5.49)
Store the child thread ID at the location ptid in the
parent's memory.
這些標誌主要是為了實現線程庫而設計的。如果我們看一下 NPTL 的實現pthread_create()
在 glibc 內部,我們最終找到了程式碼sysdeps/unix/sysv/linux/createthread.c
clone()
進行包含CLONE_PARENT_SETTID
and CLONE_CHILD_CLEARTID
in 的呼叫flags
。
在該clone()
呼叫中,我們還可以看到ptid
和ctid
參數指向同一個位址。 (請記住,POSIX 線程共享地址空間;這是透過標誌完成的clone()
CLONE_VM
。)
那麼,這裡發生的事情如下。
CLONE_PARENT_SETTID
用於確保核心執行緒 ID 儲存在使用者空間的某個位置。執行緒實作的使用者空間端需要知道該執行緒 ID。CLONE_CHILD_CLEARTID
當由建立的執行緒終止時,用於清除(即清零)相同位置clone()
。
讓我們再進一步...
ptid
通過/返回的線程IDctid
是不是與 POSIX 線程 ID ( ) 相同pthread_t
,儘管在 NPTL 等 1:1 線程實作中,內核線程 ID 和 POSIX 線程 ID 之間存在一一對應的關係。內核線程 ID 與您使用 Linux 獲得的 ID 相同gettid()
稱呼。它也clone()
作為系統呼叫返回值返回,這就提出了一個問題:為什麼我們需要ptid
/ ctid
?問題是從用戶空間方面來看,事情看起來像這樣:
tid = clone(...);
從使用者空間執行緒實現的角度來看,這裡存在競爭,因為對 的賦值tid
僅發生後 clone()
返回。這意味著如果用戶空間執行緒庫在新執行緒執行任何操作(例如終止)之前需要該訊息,則可能會遇到某些問題。使用CLONE_PARENT_SETTID
確保新的線程ID被放置在指向的位置ptid
前
clone()
返回,從而允許線程庫避免此類競爭條件。 (CLONE_CHILD_SETTID
也可以用於類似的效果。)
CLONE_CHILD_CLEARTID
使用清除ptid
/的原因ctid
是為了提供一種方法pthread_join()
呼叫另一個執行緒發現該執行緒已經終止。本質上,ptid
/ctid
位置被用作富泰克斯,以及futex()
系統呼叫用於阻塞,等待該位置的整數改變。 (細節有點複雜,但對於glibc 原始碼中和grep
的使用。最終,發生了一個操作。回想一下上面對目標位址進行 futex 喚醒。)lll_wait_tid
lll_futex_wait
FUTEX_WAIT
CLONE_CHILD_CLEARTID
答案2
tid
代表“線程 ID”。參數parent_tidptr
和child_tidptr
分別指向父進程位址空間和子進程位址空間中的使用者空間記憶體。新建立的線程的 id 儲存在指標指向的 int 變數中。
欲了解更多信息,請參閱clone(2)
線上說明頁。