溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么注入I/O故障

發(fā)布時(shí)間:2022-01-14 17:49:27 來(lái)源:億速云 閱讀:143 作者:iii 欄目:云計(jì)算

這篇“怎么注入I/O故障”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“怎么注入I/O故障”文章吧。

在生產(chǎn)環(huán)境中,時(shí)常會(huì)因?yàn)榇疟P(pán)故障、誤操作等原因出現(xiàn)文件系統(tǒng)的錯(cuò)誤。Chaos Mesh 很早就提供了注入文件系統(tǒng)錯(cuò)誤的能力。用戶(hù)只需要添加一個(gè) IOChaos 資源,就能夠讓對(duì)指定文件的文件系統(tǒng)操作失敗或返回錯(cuò)誤的數(shù)據(jù)。在 Chaos Mesh 1.0 之前,使用 IOChaos 需要對(duì) Pod 注入 sidecar 容器,并且需要改寫(xiě)啟動(dòng)命令;哪怕沒(méi)有注入錯(cuò)誤,被注入 sidecar 的容器也總是有較大的性能開(kāi)銷(xiāo)。隨著 Chaos Mesh 1.0 的發(fā)布,提供了運(yùn)行時(shí)注入文件系統(tǒng)錯(cuò)誤的功能,使得 IOChaos 的使用和其他所有類(lèi)型的 Chaos 一樣簡(jiǎn)單方便。

前置

本文的內(nèi)容假定你已經(jīng)掌握以下知識(shí)。當(dāng)然,你不必在此時(shí)就去閱讀;但當(dāng)遇到?jīng)]見(jiàn)過(guò)的名詞的時(shí)可以回過(guò)頭來(lái)搜索學(xué)習(xí)。

我會(huì)盡我所能提供相關(guān)的學(xué)習(xí)資料,但我不會(huì)將它們提煉和復(fù)述,一是因?yàn)檫@些知識(shí)通過(guò)簡(jiǎn)單的 Google 就能學(xué)到;二是因?yàn)榇蟛糠謺r(shí)候?qū)W習(xí)一手的知識(shí)效果遠(yuǎn)比二手要好,學(xué)習(xí) n 手的知識(shí)效果遠(yuǎn)比(n+1)手的要好。

  1. FUSE. Wikipedia, man(4)

  2. mount_namespaces. man, k8s Mount propagation

  3. x86 assembly language. Wikipedia

  4. mount. man(2) 特別是 MS_MOVE

  5. Mutating admission webhooks. k8s Document

  6. syscall. man(2) 注意瀏覽一下調(diào)用約定

  7. ptrace. man(2)

  8. Device node, char devices Device names, device nodes, and major/minor numbers

閱讀與 TimeChaos 相關(guān)的 文章 對(duì)理解本文也有很大的幫助,因?yàn)樗鼈兪褂弥嗨频募夹g(shù)。

此外,我希望在閱讀這份文檔時(shí),讀者能夠主動(dòng)地思考每一步的原因和效果。這之中沒(méi)有復(fù)雜的需要頭腦高速運(yùn)轉(zhuǎn)的知識(shí),只有一步一步的(給計(jì)算機(jī)的)行動(dòng)指南。也希望你能夠在大腦里不斷地構(gòu)思“如果我自己要實(shí)現(xiàn)運(yùn)行時(shí)文件系統(tǒng)注入,應(yīng)該怎樣做?”,這樣這篇文章就從單純的灌輸變?yōu)榱艘?jiàn)解的交流,會(huì)有趣很多。

錯(cuò)誤注入

尋找錯(cuò)誤注入方式的一個(gè)普遍方法就是先觀察未注入時(shí)的調(diào)用路徑:我們?cè)?TimeChaos 的實(shí)現(xiàn)過(guò)程當(dāng)中,通過(guò)觀察應(yīng)用程序獲取時(shí)間的方式,了解到大部分程序會(huì)通過(guò) vDSO 訪問(wèn)時(shí)間,從而選取了修改目標(biāo)程序 vDSO 部分內(nèi)存來(lái)修改時(shí)間的方式。

那么在應(yīng)用程序發(fā)起 read, write 等系統(tǒng)調(diào)用,到這些請(qǐng)求到達(dá)目標(biāo)文件系統(tǒng),這之間是否存在可供注入的突破口呢?事實(shí)上是存在的,你可以使用 bpf 的方式注入相關(guān)的系統(tǒng)調(diào)用,但它無(wú)法被用于注入延遲。另一種方式就是在目標(biāo)文件系統(tǒng)前再加一層文件系統(tǒng),我們暫且稱(chēng)之為 ChaosFS:

ChaosFS 以本來(lái)的目標(biāo)文件系統(tǒng)作為后端,接受來(lái)自操作系統(tǒng)的寫(xiě)入請(qǐng)求,使得整個(gè)調(diào)用鏈路變?yōu)?Targer Program syscall -> Linux Kernel -> ChaosFS -> Target Filesystem. 由于我們可以自定義 ChaosFS 文件系統(tǒng)的實(shí)現(xiàn),所以可以任意地添加延遲、返回錯(cuò)誤。

如果你在此時(shí)已經(jīng)開(kāi)始構(gòu)思自己的文件系統(tǒng)錯(cuò)誤注入實(shí)現(xiàn),聰明的你一定已經(jīng)發(fā)現(xiàn)了一些問(wèn)題:

  1. ChaosFS 如果也要往目標(biāo)文件系統(tǒng)里讀寫(xiě)文件,這意味著它的掛載路徑與目標(biāo)文件夾不同。因?yàn)閽燧d路徑幾乎是訪問(wèn)一個(gè)文件系統(tǒng)唯一的方式了。

即,如果目標(biāo)程序想要寫(xiě)入 /mnt/a,于是 ChaosFS 也得掛載于 /mnt/a,那么目標(biāo)文件夾就不能是 /mnt/a 了!但是 pod 的配置里寫(xiě)了要把目標(biāo)文件系統(tǒng)掛載在 /mnt 呀,這可怎么辦。

  1. 這不能滿足運(yùn)行時(shí)注入的要求。因?yàn)槿绻繕?biāo)程序已經(jīng)打開(kāi)了一些原目標(biāo)系統(tǒng)的文件,那么新掛載的文件系統(tǒng)只對(duì)新 open 的文件有效。(更何況還有上述文件系統(tǒng)路徑覆蓋的問(wèn)題)。想要能夠?qū)δ繕?biāo)程序注入文件系統(tǒng)錯(cuò)誤,必須得在目標(biāo)進(jìn)程啟動(dòng)之前將 ChaosFS 掛載好。

  2. 還得想辦法把文件系統(tǒng)給掛載進(jìn)目標(biāo)容器的 mnt namespace 中去。

對(duì)于這三個(gè)問(wèn)題,原初的 IOChaos 都是使用 Mutating Webhook 來(lái)達(dá)成的:

  1. 使用 Mutating Webhook 在目標(biāo)容器中先運(yùn)行腳本移動(dòng)目錄。比如將 /mnt/a 移動(dòng)至 /mnt/a_bak。這樣一來(lái) ChaosFS 的存儲(chǔ)后端就可以是 /mnt/a_bak 目錄,而自己掛載在 /mnt/a 下了。

  2. 使用 Mutating Webhook 修改 Pod 的啟動(dòng)命令,比如本身啟動(dòng)命令是 /app,我們要將它修改成 /waitfs.sh /app,而我們提供的 waitfs.sh 會(huì)不斷檢查文件系統(tǒng)是否已經(jīng)掛載成功,如果已經(jīng)成功就再啟動(dòng) /app 。

  3. 自然的,我們依舊使用 Mutating Webhook 來(lái)在 Pod 中多加入一個(gè)容器用于運(yùn)行 ChaosFS。運(yùn)行 ChaosFS 的容器需要與目標(biāo)容器共享某個(gè) volume,比如 /mnt。然后將它掛載至目標(biāo)目錄,比如 /mnt/a。同時(shí)開(kāi)啟適當(dāng)?shù)?mount propagation ,來(lái)讓 ChaosFS 容器的 volume 中的掛載穿透(share)至 host,再由 host 穿透(slave)至目標(biāo)。(如果你了解 mnt namespace 和 mount ,那么一定知道 share 和 slave 是什么意思)。

這樣一來(lái),就完成了對(duì)目標(biāo)程序 IO 過(guò)程的注入。但它是如此的不好用:

  1. 只能對(duì)某個(gè) volume 的子目錄注入,而無(wú)法對(duì)整個(gè) volume 注入。

  2. 要求 Pod 中明文寫(xiě)有 command,而不能是隱含使用鏡像的 command 。因?yàn)槿绻褂苗R像隱含的 command 的話,/waitfs.sh 就不知道在掛載成功之后應(yīng)該如何啟動(dòng)應(yīng)用了。

  3. 要求對(duì)應(yīng)容器有足夠的 mount propagation 的配置。當(dāng)然我們可以在 Mutating Webhook 里偷偷摸摸加上,但動(dòng)用戶(hù)的容器總是不太妙的(甚至可能引發(fā)安全問(wèn)題)。

  4. 注入配置要填的東西太多啦!配置起來(lái)真麻煩。而且在配置完成之后還得新建 pod 才能被注入。

  5. 無(wú)法在運(yùn)行時(shí)撤出 ChaosFS,所以哪怕不施加延遲或錯(cuò)誤,仍然對(duì)性能有不小的影響。

其中第一個(gè)問(wèn)題是可以克服的,只要用 mount move 來(lái)代替 mv(rename),就可以移動(dòng)目標(biāo) volume 的掛載點(diǎn)。而后面幾個(gè)問(wèn)題就不那么好克服了。

運(yùn)行時(shí)注入錯(cuò)誤

結(jié)合使用你擁有的其他知識(shí)(比如 namespace 的知識(shí)和 ptrace 的用法),重新審視這兩點(diǎn),就能找到解決的辦法。我們完全依賴(lài) Mutating Webhook 來(lái)構(gòu)造了這個(gè)實(shí)現(xiàn),但大部分的糟糕之處也都是由 Mutating Webhook 的方法帶來(lái)的。(如果你喜歡,可以管這種方法叫做 Sidecar 的方法。很多項(xiàng)目都這么叫,但是這種稱(chēng)呼將實(shí)現(xiàn)給隱藏了,也沒(méi)省太多字,我不是很喜歡)。接下來(lái)我們將展示如何不使用 Mutating Webhook 來(lái)達(dá)到以上目的。

侵入命名空間

我們使用 Mutating Webhook 添加一個(gè)用于運(yùn)行 ChaosFS 的容器的目的是為了通過(guò) mount propagation 的機(jī)制將文件系統(tǒng)掛載至目標(biāo)容器內(nèi)。而要達(dá)到這個(gè)目的并非只有這一種選擇 —— 我們還可以直接使用 Linux 提供的 setns 系統(tǒng)調(diào)用來(lái)修改當(dāng)前進(jìn)程的 namespace。事實(shí)上在 Chaos Mesh 的大部分實(shí)現(xiàn)中都使用了 nsenter 命令、setns 系統(tǒng)調(diào)用等方式來(lái)進(jìn)入目標(biāo)容器的 namespace,而非向 Pod 中添加容器。這是因?yàn)榍罢咴谑褂脮r(shí)更加方便,開(kāi)發(fā)時(shí)也更加靈活。

也就是說(shuō)可以先通過(guò) setns 來(lái)讓當(dāng)前線程進(jìn)入目標(biāo)容器的 mnt namespace,然后在這個(gè) namespace 中調(diào)用 mount 等系統(tǒng)調(diào)用完成 ChaosFS 的掛載。

假定我們需要注入的文件系統(tǒng)是 /mnt

  1. 通過(guò) setns 讓當(dāng)前線程進(jìn)入目標(biāo)容器的 mnt namespace;

  2. 通過(guò) mount --move 將 /mnt 移動(dòng)至 /mnt_bak;

  3. 將 ChaosFS 掛載至 /mnt,并以 /mnt_bak 為存儲(chǔ)后端。

可以看到,這時(shí)注入流程已經(jīng)大致完成了,目標(biāo)容器如果再次打開(kāi)、讀寫(xiě) /mnt 中的文件,就會(huì)通過(guò) ChaosFS,從而被它注入延遲或錯(cuò)誤。

而它還剩下兩個(gè)問(wèn)題:

  1. 目標(biāo)進(jìn)程已經(jīng)打開(kāi)的文件該怎么辦?

  2. 該如何恢復(fù)?畢竟在有文件被打開(kāi)的情況下是無(wú)法 umount 的。

后文將用同一個(gè)手段解決這兩個(gè)問(wèn)題:使用 ptrace 的方法在運(yùn)行時(shí)替換已經(jīng)打開(kāi)的 fd。(本文以 fd 為例,事實(shí)上除了 fd 還有 cwd,mmap 等需要替換,實(shí)現(xiàn)方式是相似的,就不單獨(dú)描述了)

動(dòng)態(tài)替換 fd

我們主要使用 ptrace 來(lái)對(duì) fd 進(jìn)行動(dòng)態(tài)地替換,在介紹具體的方法之前,不妨先感受一下 ptrace 的威力:

  1. 使用 ptrace 能夠讓 tracee(被 ptrace 的線程) 運(yùn)行任意系統(tǒng)調(diào)用這是怎么做到的呢?綜合運(yùn)用 ptrace 和 x86_64 的知識(shí)來(lái)看這個(gè)問(wèn)題并不算難。由于 ptrace 可以修改寄存器,同時(shí) x86_64 架構(gòu)中 rip 寄存器(instruction pointer)總是指向下一個(gè)要運(yùn)行的指令的地址,所以只需要將當(dāng)前 rip 指向的部分內(nèi)存修改為 0x050f (對(duì)應(yīng) syscall 指令),再依照系統(tǒng)調(diào)用的調(diào)用約定將各個(gè)寄存器的值設(shè)為對(duì)應(yīng)的系統(tǒng)調(diào)用編號(hào)或參數(shù),然后使用 ptrace 單步執(zhí)行,就能從 rax 寄存器中拿到系統(tǒng)調(diào)用的返回值。在完成調(diào)用之后記得將寄存器和修改的內(nèi)存都復(fù)原。

在以上過(guò)程中使用了 ptrace 的 POKE_TEXT,SETREGS,GETREGS,SINGLESTEP 等功能,如果不熟悉可以查閱 ptrace 的手冊(cè)。

  1. 使用 ptrace 能夠讓 tracee(指 ptrace 的目標(biāo)進(jìn)程) 運(yùn)行任意二進(jìn)制程序。

    運(yùn)行任意二進(jìn)制程序的思路是類(lèi)似的??梢耘c運(yùn)行系統(tǒng)調(diào)用一樣,將 rip 后一部分的內(nèi)訓(xùn)修改為自己想要運(yùn)行的程序,并在程序末尾加上 int3 指令以觸發(fā)斷點(diǎn)。在執(zhí)行完成之后恢復(fù)目標(biāo)程序的寄存器和內(nèi)存就好了。

    而事實(shí)上我們可以選用一種稍稍干凈些的方式:使用 ptrace 在目標(biāo)程序中調(diào)用 mmap,分配出需要的內(nèi)存,然后將二進(jìn)制程序?qū)懭胄路峙涑龅膬?nèi)存區(qū)域中,將 rip 指向它。在運(yùn)行結(jié)束之后調(diào)用 munmap 就能保持內(nèi)存區(qū)域的干凈。

在實(shí)踐中,我們使用 process_vm_writev 代替了使用 ptrace POKE_TEXT 寫(xiě)入,在寫(xiě)入大量?jī)?nèi)容的時(shí)候它更加穩(wěn)定高效一些。

在擁有以上手段之后,如果一個(gè)進(jìn)程自己有辦法替換自己的 fd,那么通過(guò) ptrace,就能讓它運(yùn)行同樣的一段程序來(lái)替換 fd。這樣一來(lái)問(wèn)題就簡(jiǎn)單了:我們只需要找到一個(gè)進(jìn)程自己替換自己的 fd 的方法。如果對(duì) Linux 的系統(tǒng)調(diào)用較為熟悉的話,馬上就能找到答案:dup2。

使用 dup2 替換 fd

dup2 的函數(shù)簽名是 int dup2(int oldfd, int newfd);,它的作用是創(chuàng)建一份 oldfd 的拷貝,并且這個(gè)拷貝的 fd 號(hào)是 newfd。如果 newfd 原本就有打開(kāi)著的 fd ,它會(huì)被自動(dòng)地 close。

假定現(xiàn)在進(jìn)程正打開(kāi)著 /var/run/__chaosfs__test__/a ,fd 為 1 ,希望替換成 /var/run/test/a,那么它需要做的事情有:

  1. 使用通過(guò) fcntl 系統(tǒng)調(diào)用獲取 /var/run/__chaosfs__test__/a 的 OFlags(即 open 調(diào)用時(shí)的參數(shù),比如 O_WRONLY );

  2. 使用 lseek 系統(tǒng)調(diào)用獲取當(dāng)前的 seek 位置;

  3. 使用 open 系統(tǒng)調(diào)用,以相同的 OFlags 打開(kāi) /var/run/test/a,假設(shè) fd 為 2;

  4. 使用 lseek 改變新打開(kāi)的 fd 2 的 seek 位置;

  5. 使用 dup2(2, 1) 用新打開(kāi)的 fd 2 來(lái)替換 /var/run/__chaosfs__test__/a 的 fd 1;

  6. 將 fd 2 關(guān)掉。

這樣之后,當(dāng)前進(jìn)程的 fd 1 就會(huì)指向 /var/run/test/a,任何對(duì)于它的操作都會(huì)通過(guò) FUSE,能夠被注入錯(cuò)誤了。

使用 ptrace 讓目標(biāo)進(jìn)程運(yùn)行替換 fd 的程序

那么只要結(jié)合“使用 ptrace 能夠讓 tracee 運(yùn)行任意二進(jìn)制程序”的知識(shí)和“使用dup2替換自己已經(jīng)打開(kāi)的fd”的方法,就能夠讓 tracee 自己把已經(jīng)打開(kāi)的 fd 給替換掉啦!

對(duì)照前文描述的步驟,結(jié)合 syscall 指令的用法,寫(xiě)出對(duì)應(yīng)的匯編代碼是容易的,你可以在這里看到對(duì)應(yīng)的源碼,使用匯編器可以將它輸出為可供使用的二進(jìn)制程序(我們使用的是 dynasm-rs)。然后用 ptrace 讓目標(biāo)進(jìn)程運(yùn)行這段程序,就完成了在運(yùn)行時(shí)對(duì) fd 的替換。

讀者可以稍稍思考如何使用類(lèi)似的方式來(lái)改換 cwd,替換 mmap 呢?它們的流程完全是類(lèi)似的。

注:實(shí)現(xiàn)中假定了目標(biāo)程序依照 Posix Thread,目標(biāo)進(jìn)程與它的線程之間共享打開(kāi)的文件,即 clone 創(chuàng)建線程時(shí)指定了 CLONE_FILES。所以將只會(huì)對(duì)一個(gè)線程組的第一個(gè)線程進(jìn)行 fd 替換。

流程總覽

在了解了這一切技術(shù)之后,實(shí)現(xiàn)運(yùn)行時(shí)文件系統(tǒng)的思路應(yīng)當(dāng)已經(jīng)逐漸清晰了起來(lái)。在這一節(jié)我將直接展示出整個(gè)注入實(shí)現(xiàn)的流程圖:

怎么注入I/O故障

平行的數(shù)條線表示不同的線程,從左至右依照時(shí)間先后順序??梢钥吹綄?duì) “掛載/卸載文件系統(tǒng) ”和 “進(jìn)行 fd 等資源的替換” 這兩個(gè)任務(wù)進(jìn)行了較為精細(xì)的時(shí)間順序的安排,這是有必要的。為什么呢?如果讀者對(duì)整個(gè)過(guò)程的了解已經(jīng)足夠清晰,不妨試著自己思考它的答案。

細(xì)枝末節(jié)的問(wèn)題

mnt namespace 可能引發(fā)的 mmap 失效

在 mnt namespace 切換之后,已經(jīng)創(chuàng)建完成的 mmap 是否還有效呢?比如一個(gè) mmap 指向 /a/b,而在切換 mnt namespace 之后 /a/b 消失了,再訪問(wèn)這個(gè) mmap 時(shí)是否會(huì)造成意料之外的崩潰呢?值得注意的是,動(dòng)態(tài)鏈接庫(kù)全是通過(guò) mmap 載入進(jìn)內(nèi)存的,訪問(wèn)它們是否會(huì)有問(wèn)題呢?

事實(shí)上,是不會(huì)有問(wèn)題的。這涉及到 mnt namespace 的方式和目的。mnt namespace 只涉及到對(duì)線程可見(jiàn)性的控制,具體的做法,則是在調(diào)用 setns 時(shí)修改內(nèi)核中某一線程 task_struct 內(nèi) vfsmount 指針的修改,從而當(dāng)線程使用任何傳入路徑的系統(tǒng)調(diào)用的時(shí)候(比如 open、rename 等)的時(shí)候,Linux 內(nèi)核內(nèi)通過(guò) vfsmount 從路徑名查詢(xún)到文件(作為 file 結(jié)構(gòu)體),會(huì)受到 namespace 的影響。而對(duì)于已經(jīng)打開(kāi)的 fd(指向一個(gè) file 結(jié)構(gòu)體),它的 open、write、read 等操作直接指向?qū)?yīng)文件系統(tǒng)的函數(shù)指針,不會(huì)受到 namespace 的影響;對(duì)于一個(gè)已經(jīng)打開(kāi)的 mmap (指向一個(gè) address_space 結(jié)構(gòu)體),它的 writepage, readpage 等操作也直接指向?qū)?yīng)文件系統(tǒng)的函數(shù)指針,也不受到 namespace 的影響。

注入的范圍

由于在注入過(guò)程中,不可能將機(jī)器上運(yùn)行的所有進(jìn)程暫停并檢查它們已經(jīng)打開(kāi)的 fd 和 mmap 等資源,這樣做的開(kāi)銷(xiāo)不可接受。在實(shí)踐中,我們選擇預(yù)先進(jìn)入目標(biāo)容器的 pid namespace,并對(duì)這個(gè) namespace 中能看見(jiàn)的所有進(jìn)程進(jìn)行暫停和檢查。

所以注入和恢復(fù)的范圍是全部 pid namespace 中的進(jìn)程。而切換 pid namespace 意味著需要預(yù)先設(shè)定子進(jìn)程的 pid namespace 再 clone(因?yàn)?Linux 并不允許切換當(dāng)前進(jìn)程的 pid namespace ),這又將帶來(lái)諸多問(wèn)題。

切換 namespace 對(duì) clone flag 有些限制

切換 mnt namespace 將不允許 clone 時(shí)攜帶參數(shù) CLONE_FS。而預(yù)先設(shè)定好子進(jìn)程 pid namespace 的情況下,將不允許 clone 時(shí)攜帶參數(shù) CLONE_THREAD。為了應(yīng)對(duì)這個(gè)問(wèn)題,我們選擇修改 glibc 的源碼,能夠在 chaos-mesh/toda-glibc 中找到修改后的 glibc 的源碼。修改的只有 pthread 部分 clone 時(shí)傳入的參數(shù)。

在去掉 CLONE_THREADCLONE_FS 之后,pthread 的表現(xiàn)與原先有較大差異。其中最大的差異便是新建的 pthread 線程不再是原有進(jìn)程的 tasks,而是一個(gè)新的進(jìn)程,它們的 tgid 是不同的。這樣 pthread 線程之間的關(guān)系從進(jìn)程與tasks變成了進(jìn)程與子進(jìn)程。這又會(huì)帶來(lái)一些麻煩,比如在退出時(shí)可能需要對(duì)子進(jìn)程進(jìn)行額外的清理。

在更低版本的內(nèi)核中,也不允許不同 pid namespace 的進(jìn)程共享 SIGHAND,所以還需要把 CLONE_SIGHAND 去掉。

為什么不使用nsenter

在 chaos-daemon 中,很多需要在目標(biāo)命名空間中的操作都是通過(guò) nsenter 完成的,比如 nsenter iptables 這樣聯(lián)合使用。而 nsenter 卻無(wú)法應(yīng)對(duì) IOChaos 的場(chǎng)景,因?yàn)槿绻谶M(jìn)程啟動(dòng)時(shí)就已進(jìn)入目標(biāo) mnt namespace,那將找不到合適的動(dòng)態(tài)鏈接庫(kù)(比如 libfuse.so 和自制的 glibc)。

構(gòu)造 /dev/fuse

由于目標(biāo)容器中不一定有 /dev/fuse (事實(shí)上更可能沒(méi)有),所以在進(jìn)入目標(biāo)容器的 mnt namespace 后掛載 FUSE 時(shí)會(huì)遇到錯(cuò)誤。所以在進(jìn)入目標(biāo)的 mnt namespace 后需要構(gòu)造 /dev/fuse。這個(gè)構(gòu)造的過(guò)程還是很容易的,因?yàn)?fuse 的 major number 和 minor number 是固定的 10 和 229。所以只要使用 makedev 函數(shù)和 mknod 系統(tǒng)調(diào)用,就能夠創(chuàng)造出 /dev/fuse 。

去掉 CLONE_THREAD 之后等待子進(jìn)程死亡的問(wèn)題

在子進(jìn)程死亡時(shí),會(huì)向父進(jìn)程發(fā)送 SIGCHLD 信號(hào)通知自己的死亡。如果父進(jìn)程沒(méi)有妥善的處理這個(gè)信號(hào)(顯式地忽略或是在信號(hào)處理中 wait ),那么子進(jìn)程就會(huì)持續(xù)處于 defunct 狀態(tài)。

而在我們的場(chǎng)景下,這個(gè)問(wèn)題變得更加復(fù)雜了:因?yàn)楫?dāng)一個(gè)進(jìn)程的父進(jìn)程死亡之后,它的父進(jìn)程會(huì)被重新置為它所在的 pid namespace 的 1 號(hào)進(jìn)程。通常來(lái)說(shuō)一個(gè)好的 init 進(jìn)程(比如 systemd )會(huì)負(fù)責(zé)清理這些 defunct 進(jìn)程,但在容器的場(chǎng)景下,作為 pid 1 的應(yīng)用通常并沒(méi)有被設(shè)計(jì)為一個(gè)好的 init 進(jìn)程,不會(huì)負(fù)責(zé)處理掉這些 defunct 進(jìn)程。

為了解決這個(gè)問(wèn)題,我們使用 subreaper 的機(jī)制來(lái)讓一個(gè)進(jìn)程的父進(jìn)程死亡時(shí)并不是直接將父進(jìn)程置為 1,而是進(jìn)程樹(shù)上離得最近的 subreaper。然后使用 wait 來(lái)等待所有子進(jìn)程死亡再退出。

waitpid 在不同內(nèi)核版本下表現(xiàn)不一致

waitpid 在不同版本內(nèi)核下表現(xiàn)不一致,在較低版本的內(nèi)核中,對(duì)一個(gè)作為子線程(指并非主線程的線程)的 tracee 使用 waitpid 會(huì)返回 ECHILD ,還沒(méi)有確定這樣的原因是什么,也沒(méi)有找到相關(guān)的文檔。

以上就是關(guān)于“怎么注入I/O故障”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

i/o
AI