楊 倩,楊明趙
(1.重慶理工大學(xué),重慶 400054;2.云從科技集團(tuán)股份有限公司,重慶 401120)
Linux遵循傳統(tǒng)UNIX系統(tǒng)“一切皆文件”的理念,進(jìn)程已打開的文件,稱為“文件描述符(File Descriptor,簡(jiǎn)稱:FD)”,將文件描述符從一個(gè)進(jìn)程傳遞到另外一個(gè)進(jìn)程,對(duì)應(yīng)的使用文件描述符所關(guān)聯(lián)資源的能力也就被傳遞到了新的進(jìn)程。在軟件設(shè)計(jì)中,這種方式可以實(shí)現(xiàn)在一組進(jìn)程中的不同進(jìn)程對(duì)同一資源不同階段共享訪問,可以利用進(jìn)程的相互隔離,帶來安全性、 穩(wěn)定性和健壯性等方面的好處, 文件描述符的進(jìn)程間傳遞是Linux操作系統(tǒng)的一種重要的進(jìn)程間能力傳遞手段,本文將對(duì)其底層原理和典型應(yīng)用場(chǎng)景進(jìn)行分析。
現(xiàn)代操作系統(tǒng)的進(jìn)程一般工作在虛擬內(nèi)存地址空間通過MMU地址轉(zhuǎn)換映射到物理內(nèi)存,進(jìn)程無(wú)法訪問其他進(jìn)程的虛擬內(nèi)存,這樣就達(dá)到了相互隔離的安全目的。 進(jìn)程之間的交互操作只能通過比如將不同進(jìn)程的某一部分虛擬地址區(qū)間映射到相同的物理內(nèi)存塊,達(dá)到共享內(nèi)存的目的;也可以通過進(jìn)程處于內(nèi)核特權(quán)模式上下文中,將數(shù)據(jù)從一個(gè)進(jìn)程的內(nèi)存拷貝到另外一個(gè)進(jìn)程的內(nèi)存,達(dá)到內(nèi)存間消息交互的目的。文件描述符通常通過整數(shù)表示,該整數(shù)代表在文件描述符表(File Descriptor Table)中的索引,進(jìn)程每次打開文件,會(huì)從該表空閑項(xiàng)中分配一項(xiàng)。 文件描述符是進(jìn)程操作該進(jìn)程描述符所關(guān)聯(lián)資源的句柄,有了進(jìn)程描述符也就具備了操作這些資源的能力(Capability)。如果能在進(jìn)程之間傳遞文件描述符,也就能夠?qū)崿F(xiàn)能力的傳遞而不是直接傳輸數(shù)據(jù),這樣可以減少數(shù)據(jù)的拷貝,提高數(shù)據(jù)使用效率。但是,文件描述符表為各個(gè)進(jìn)程私有,互相之間無(wú)法訪問。要實(shí)現(xiàn)進(jìn)程之間文件描述符的傳遞,實(shí)際上是要實(shí)現(xiàn)該文件描述符所關(guān)聯(lián)資源在不同進(jìn)程的內(nèi)存空間的重新映射。文件描述符的映射關(guān)系如圖1所示[1]。
圖1 文件描述符和進(jìn)程的關(guān)系
文件描述符的傳遞目前在不同的Linux衍生系統(tǒng)上有不同的實(shí)現(xiàn),原生的Linux系統(tǒng)使用基于UNIX域套接字的sendmsg()和recvmsg()系統(tǒng)調(diào)用可以實(shí)現(xiàn)上述能力,從Linux內(nèi)核5.6版本開始, 引入了一種新的系統(tǒng)調(diào)用叫做pidfd_getfd(),使得進(jìn)程間傳遞文件描述符更加安全可靠,并且使得開發(fā)相關(guān)應(yīng)用更加簡(jiǎn)潔方便;而安卓系統(tǒng),為了使其應(yīng)用框架更加簡(jiǎn)潔,讓不同的進(jìn)程可以通過RPC的方式進(jìn)行交互,在內(nèi)核層面添加了Binder IPC機(jī)制,Binder也支持文件描述符的傳遞。后面的章節(jié)將對(duì)這種系統(tǒng)能力的實(shí)際應(yīng)用場(chǎng)景進(jìn)行一些分析。
多數(shù)操作系統(tǒng)圖形子系統(tǒng)采用客戶端/服務(wù)端模型,負(fù)責(zé)操作顯存的進(jìn)程作為服務(wù)端,稱為“顯示服務(wù)器”,其他具有圖像界面的應(yīng)用進(jìn)程作為客戶端。 所有客戶端將繪制完成自身圖像數(shù)據(jù)(比如GUI)傳輸?shù)椒?wù)端,由服務(wù)端對(duì)圖像進(jìn)行轉(zhuǎn)換、裁剪、混合等操作后,將最終圖像送到顯存進(jìn)行顯示[2]。
圖2 圖形系統(tǒng)客戶端/服務(wù)端模型
圖像數(shù)據(jù)具有數(shù)據(jù)量大的特點(diǎn),采用共享內(nèi)存池實(shí)現(xiàn)數(shù)據(jù)零拷貝必不可少,然而,這里的服務(wù)端和眾多客戶端進(jìn)程通常并沒有進(jìn)程上的父子關(guān)系,進(jìn)程之間的內(nèi)存共享只能通過文件描述符傳遞來實(shí)現(xiàn)。
以安卓及桌面Linux系統(tǒng)所采用的Wayland架構(gòu)為例,客戶端在啟動(dòng)階段會(huì)和服務(wù)端建立會(huì)話,會(huì)話通道不同的操作系統(tǒng)有所不同,比如安卓采用其特有的Binder IPC、 Wayland采用UNIX域套接字,所采用的這些通道底層都提供了文件描述符的傳遞能力,通過會(huì)話一端將內(nèi)存句柄以文件描述符的形式傳遞給對(duì)端,這樣就完成了共享內(nèi)存池的建立。 后續(xù)客戶端完成一幀操作后,通過會(huì)話通道喚醒服務(wù)端。服務(wù)端進(jìn)行后續(xù)處理??梢钥吹剑^程是在共享的內(nèi)存中進(jìn)行的,IPC機(jī)制只是起到處理雙方同步訪問機(jī)制的作用,保證一方開始讀的時(shí)候另外一方已經(jīng)寫入完成[3]。
一般操作系統(tǒng)需要對(duì)權(quán)限進(jìn)行管理,系統(tǒng)資源需要特殊的權(quán)限才能進(jìn)行訪問,如果這些特權(quán)進(jìn)程發(fā)生錯(cuò)誤或者用戶不受信任代碼運(yùn)行于特權(quán)進(jìn)程,將給不法攻擊者帶來可乘之機(jī)。安卓操作系統(tǒng)對(duì)這些應(yīng)用使用系統(tǒng)資源進(jìn)行了限制,采用了授權(quán)訪問機(jī)制,如果應(yīng)用需要訪問這些設(shè)備,需要向系統(tǒng)申請(qǐng)對(duì)應(yīng)的權(quán)限,系統(tǒng)通過對(duì)話框提示用戶同意后,才能進(jìn)行進(jìn)一步操作[4]。
這類需求一般實(shí)現(xiàn)方式是,通過系統(tǒng)服務(wù)作為代理,介于用戶進(jìn)程和設(shè)備之間做訪問限制,帶來效率和實(shí)時(shí)性上的問題。所以,安卓在架構(gòu)設(shè)計(jì)上使用了文件描述符傳遞機(jī)制,首先限制設(shè)備文件節(jié)點(diǎn)只能由特定系統(tǒng)服務(wù)訪問,確保了安全性。經(jīng)過用戶允許的程序,可以由系統(tǒng)服務(wù)將所涉及到的設(shè)備節(jié)點(diǎn)打開,然后將文件描述符通過Binder機(jī)制發(fā)送到用戶程序,從而用戶程序得到了訪問該設(shè)備的“能力”。在這種場(chǎng)景下,通過文件描述符的傳遞,用戶進(jìn)程與設(shè)備之間的交互完全沒有第三方的參與,在不破壞設(shè)備訪問協(xié)議、不增加額外的數(shù)據(jù)拷貝開銷以及不增加數(shù)據(jù)延遲的情況下,很簡(jiǎn)潔地實(shí)現(xiàn)了訪問權(quán)限限制和授權(quán)訪問[5]。
互聯(lián)網(wǎng)基礎(chǔ)設(shè)施需要保證互聯(lián)網(wǎng)請(qǐng)求都能及時(shí)得到響應(yīng)。其中負(fù)載均衡器需要管理來自用戶的大量TCP連接和數(shù)據(jù)分發(fā),如果過程中需要升級(jí)程序或者更新配置,則需要重新啟動(dòng)該服務(wù),這種情況下,已經(jīng)建立的TCP連接需要被強(qiáng)制RESET,以及重啟過程中客戶端發(fā)起的SYN請(qǐng)求也會(huì)被拒絕。這樣短暫的中斷在一些關(guān)鍵的互聯(lián)網(wǎng)基礎(chǔ)設(shè)施是不被允許的,所以提出了“零停機(jī)運(yùn)維”的概念。
“零停機(jī)運(yùn)維”要求服務(wù)即使在不得不重啟的情況下,仍然保持連接活躍,不讓客戶端請(qǐng)求無(wú)響應(yīng)或者響應(yīng)錯(cuò)誤。這種條件下,文件描述符進(jìn)程間傳遞發(fā)揮了作用。知名開源負(fù)載均衡器Haproxy配置熱加載以及互聯(lián)網(wǎng)公司FaceBook提出的“Socket Takeover”均使用了這種技術(shù)。其核心原理是,服務(wù)新的實(shí)例先啟動(dòng),完成初始化,與即將關(guān)閉的舊實(shí)例建立連接,舊實(shí)例將所有活躍的TCP套接字文件描述符發(fā)送到新的實(shí)例,新的服務(wù)接管這些活躍的連接,然后舊服務(wù)實(shí)例退出,完成了整個(gè)重啟過程。過程中,活躍的連接狀態(tài)不會(huì)受到影響,客戶端的連接不會(huì)被中斷,從客戶端感覺不到服務(wù)端的重啟活動(dòng),即實(shí)現(xiàn)了“零停機(jī)”[6,7]。
本文章對(duì)文件描述符的跨進(jìn)程傳遞原理進(jìn)行了原理分析,并對(duì)已有的一些應(yīng)用場(chǎng)景進(jìn)行了描述。相信未來在其他的一些場(chǎng)景下,文件描述符的傳遞也會(huì)有更加廣泛的應(yīng)用場(chǎng)景。