LWN:glibc里面創(chuàng)建進(jìn)程可以不用擔(dān)心race condition了!
關(guān)注了就能看到更多這么棒的文章哦~
Race-free process creation in the GNU C Library
By Jonathan Corbet
September 1, 2023
ChatGPT assisted translation
https://lwn.net/Articles/943022/
pidfd API 近年被添加到內(nèi)核中,希望提供一個(gè)無(wú)競(jìng)爭(zhēng)的方式(race-free)來(lái)讓進(jìn)程相互引用。雖然 GNU C 庫(kù)(glibc)在 2022 年的 2.36 版本中增加了基本的 pidfd 支持,但它針對(duì)無(wú)競(jìng)爭(zhēng)的進(jìn)程創(chuàng)建仍然缺乏完整解決方案。來(lái)自 Adhemerval Zanella 的一組 patch set 似乎有望在不久的將來(lái)填補(bǔ)這個(gè)空白,具體的實(shí)現(xiàn)方式是擴(kuò)展 posix_spawn() API。
Unix 系統(tǒng)通過(guò)一個(gè)整數(shù) ID(稱(chēng)為“processID”簡(jiǎn)稱(chēng) PID)來(lái)指代某個(gè)進(jìn)程,這個(gè) ID 在進(jìn)程創(chuàng)建時(shí)就分配好。PID 的問(wèn)題在于它們隨著時(shí)間的推移會(huì)被重復(fù)回收利用;一旦具有某個(gè)特定 PID 的進(jìn)程退出并且 PID 被回收了,那么就可能會(huì)分配給一個(gè)新的無(wú)關(guān)進(jìn)程,于是導(dǎo)致這個(gè) PID 實(shí)際上可能不指向用戶(hù)期望的進(jìn)程。為了解決這個(gè)問(wèn)題,人們引入了 pidfd 概念;pidfd 是一個(gè)文件描述符,作為代表進(jìn)程的句柄。與 pidfds 相關(guān)的許多與 PID 有關(guān)的競(jìng)爭(zhēng)條件(race condition)在使用 pidfd 之后都不再存在,因?yàn)榕c pidfd 關(guān)聯(lián)的進(jìn)程是永遠(yuǎn)不會(huì)改變的。
當(dāng)前的 glibc 版本中包括對(duì)一些底層 pidfd 相關(guān)系統(tǒng)調(diào)用的封裝,包括 pidfd_open()、pidfd_getfd()等。但還缺少一個(gè)功能:即在創(chuàng)建新進(jìn)程時(shí)獲取該進(jìn)程的 pidfd。可以使用 pidfd_open()在創(chuàng)建后立即依據(jù) PID 獲取 pidfd,但仍然存在一個(gè)非常小的時(shí)間窗口期,會(huì)出現(xiàn)該 PID 代表的進(jìn)程可能會(huì)退出并被另一個(gè)進(jìn)程替代。要避免這個(gè)窗口引入問(wèn)題,需要在創(chuàng)建新進(jìn)程時(shí)就直接從內(nèi)核獲取一個(gè) pidfd,而 glibc 沒(méi)有提供這種方式。
這個(gè)功能可以通過(guò)為 clone3() 系統(tǒng)調(diào)用添加一個(gè)封裝來(lái)實(shí)現(xiàn),但有一些人反對(duì)這樣做。因此,Zanella 采取了增強(qiáng) posix_spawn() API 的方法,許多人認(rèn)為這是進(jìn)程創(chuàng)建該使用的更好的方法(并且后面緊跟著一個(gè) exec()調(diào)用),比以前的 Unix fork()模型要更好。于是出現(xiàn)了兩個(gè)新函數(shù):
int pidfd_spawn(int *restrict pidfd,
const char *restrict file,
const posix_spawn_file_actions_t *restrict facts,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict],
char *const envp[restrict]);
int pidfd_spawnp(int *restrict pidfd,
const char *restrict path,
const posix_spawn_file_actions_t *restrict facts,
const posix_spawnattr_t *restrict attrp,
char *const argv[restrict_arr],
char *const envp[restrict_arr]);
跟 posix_spawn()和 posix_spawnp()一樣,這些函數(shù)執(zhí)行了 clone()和 exec()的組合調(diào)用,從而創(chuàng)建了一個(gè)運(yùn)行由 file 或 path 所指代的程序的新進(jìn)程。不過(guò)不一樣的是,返回值是一個(gè)標(biāo)識(shí)已創(chuàng)建進(jìn)程的 pidfd,而不是 PID。
如果創(chuàng)建者需要知道新進(jìn)程的 PID,可以通過(guò) patch set 所添加的一個(gè)新函數(shù)來(lái)獲得:
pid_t pidfd_getpid(int pidfd);
這個(gè)函數(shù)通過(guò)查看給定 pidfd 的/proc 下的項(xiàng)目來(lái)獲取 PID。
這些新函數(shù)是使用 clone3()來(lái)實(shí)現(xiàn)的,從而可以實(shí)現(xiàn)在進(jìn)程創(chuàng)建期間就直接獲得 pidfd,不會(huì)有競(jìng)爭(zhēng)窗口。使用 clone3()還可以實(shí)現(xiàn)其他一些功能,具體來(lái)說(shuō),可以在與創(chuàng)建者的不同 cgroup 中創(chuàng)建新進(jìn)程。Zanella 還通過(guò)對(duì) posix_spawn() 的屬性機(jī)制(attribute mechanism)實(shí)現(xiàn)擴(kuò)展來(lái)提供了這個(gè)功能??梢詾?posix_spawn()和 pidfd_spawn()創(chuàng)建不同的 cgroup。
雖然許多人認(rèn)為 posix_spawn() 相較于 fork()和 exec()的組合是更具有優(yōu)勢(shì)的模型,但它并未提供所有功能。對(duì)于這個(gè) API 不足的情況,早期版本的 patch set 包括了一個(gè)名為 fork_np()的函數(shù),它是一個(gè)獨(dú)立的封裝,可以包在 clone3() 外面返回一個(gè)標(biāo)識(shí)新子進(jìn)程的 pidfd。然而,F(xiàn)lorian Weimer 抱怨說(shuō),這個(gè)接口與內(nèi)核提供的不一樣,而且“未來(lái)不具備可擴(kuò)展性”。他要求 Zanella 暫時(shí)將這個(gè)函數(shù)從這組 patch 中刪除,于是后續(xù)版本中已經(jīng)將其刪除。
相反,Rich Felker 對(duì)這整個(gè)理念都表示反對(duì),稱(chēng)任何與 PID 相關(guān)的競(jìng)爭(zhēng)都“純粹是程序員自己的錯(cuò)誤”,并且“為了解決程序員自己錯(cuò)誤所引起的問(wèn)題而創(chuàng)建一個(gè)新的、復(fù)雜的、高度非標(biāo)準(zhǔn)的接口,并將這種非標(biāo)準(zhǔn)和不具備可移植的模式引入主流軟件,具有負(fù)面價(jià)值”。他認(rèn)為,更好的做法是 fix 那些受此問(wèn)題影響的軟件。然而,Luca Boccassi 對(duì)此持不同意見(jiàn),認(rèn)為“這些都是真正存在的 race condition,無(wú)法以其他方式解決”。Weimer 也表示,引入 pidfd 功能具有價(jià)值。
盡管對(duì)這一個(gè)具體爭(zhēng)議尚未達(dá)成明確的解決方案,但事實(shí)上 PID 競(jìng)爭(zhēng)確實(shí)是一個(gè)問(wèn)題,而且有一些用戶(hù)(如 systemd)希望擁有這種類(lèi)型的 API 以避免這些競(jìng)爭(zhēng)。因此,pidfd_spawn()(盡管可能不包括 fork_np())很有可能最終被納入 glibc。
全文完
LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。
長(zhǎng)按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開(kāi)源社區(qū)的各種新近言論~
