<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          LWN: copy_file_range()應(yīng)該處理哪些情況?

          共 4323字,需瀏覽 9分鐘

           ·

          2021-03-07 08:13

          關(guān)注了就能看到更多這么棒的文章哦~

          How useful should copy_file_range() be?

          By Jonathan Corbet
          February 18, 2021
          DeepL assisted translation
          https://lwn.net/Articles/846403/

          copy_file_range()系統(tǒng)調(diào)用看起來(lái)很容易理解:允許用戶空間要求內(nèi)核將一系列的數(shù)據(jù)從一個(gè)文件復(fù)制到另一個(gè)文件,希望在這一過(guò)程中能以比較優(yōu)化的方式來(lái)完成。實(shí)際上,這個(gè)調(diào)用看起來(lái)會(huì)很常用,但實(shí)際上并不是,這還是在 5.3 版本時(shí)對(duì)可用性方面進(jìn)行了一些改進(jìn)之后的結(jié)果。Go 語(yǔ)言的開(kāi)發(fā)者在使用 copy_file_range() 時(shí)就遇到了問(wèn)題,隨后進(jìn)行了長(zhǎng)時(shí)間的討論,討論這個(gè)系統(tǒng)調(diào)用應(yīng)該如何應(yīng)用、內(nèi)核是否應(yīng)該多做些工作來(lái)使它真正有用。

          copy_file_range()的定義是:

          ssize_t copy_file_range(int fd_in, loff_t *off_in,
          int fd_out, loff_t *off_out,
          size_t len, unsigned int flags);

          ssize_t copy_file_range(int fd_in, loff_t *off_in,
          int fd_out, loff_t *off_out,
          size_t len, unsigned int flags)。

          它的任務(wù)是將 fd_in 所代表的文件中的 len 長(zhǎng)度的數(shù)據(jù)復(fù)制到 fd_out 中,監(jiān)測(cè)兩邊的 offsett。flags 參數(shù)必須為 0。這個(gè)調(diào)用最早出現(xiàn)在 4.5 版本中。隨著時(shí)間的推移,人們發(fā)現(xiàn)它有許多令人不快的問(wèn)題,導(dǎo)致了很多長(zhǎng)期修復(fù)工作,也引起了很多抱怨。

          在 2019 年,Amir Goldstein 修復(fù)了許多問(wèn)題,并在此過(guò)程中刪除了一個(gè)重要的限制:在此之前,copy_file_range()不允許在不位于同一文件系統(tǒng)的文件之間進(jìn)行數(shù)據(jù)復(fù)制。這個(gè) patch 被合并之后(5.3),它可以在任何兩個(gè)文件之間進(jìn)行復(fù)制了。對(duì)于跨文件系統(tǒng)的情況,它會(huì)回退到使用 splice() 來(lái)完成。這樣一來(lái),似乎 copy_file_range() 終于成為了一個(gè)可靠而有用的系統(tǒng)調(diào)用。

          它確實(shí)成功吸引到了人們的注意,Go 的開(kāi)發(fā)者就決定將它用于他們標(biāo)準(zhǔn)庫(kù)中的 io.Copy() 函數(shù)。然后他們遇到了一個(gè)問(wèn)題:當(dāng)使用內(nèi)核生成的文件來(lái)作為 input 文件時(shí),copy_file_range()會(huì)復(fù)制 0 byte 數(shù)據(jù)并返回成功。/proc、tracefs 和其他大量的虛擬文件系統(tǒng)中的文件都是這種情況,如果直接使用像 stat()這樣的系統(tǒng)調(diào)用對(duì)其查詢文件長(zhǎng)度,一般都會(huì)顯示長(zhǎng)度為 0。copy_file_range()看到這個(gè)信息之后,就會(huì)認(rèn)為沒(méi)有數(shù)據(jù)需要復(fù)制,工作已經(jīng)完成,就返回成功值。

          但其實(shí)這種文件是有數(shù)據(jù)要讀的,只是在文件長(zhǎng)度上沒(méi)有體現(xiàn)出來(lái),原因也很簡(jiǎn)單在實(shí)際讀取文件之前往往無(wú)法知道其真實(shí)長(zhǎng)度。在內(nèi)核 5.3 之前,因?yàn)榻沽藢?duì)跨文件系統(tǒng)的 copy 操作,所以大多數(shù)這樣的操作都會(huì)返回一個(gè)錯(cuò)誤代碼,但這種情況也被被干凈地處理掉了。內(nèi)核很高興,但用戶可能感到出乎意料,他們畢竟是真的想要復(fù)制他們所要求獲取的數(shù)據(jù),于是感到很不滿意。

          Marking virtual filesytems

          于是 Nicolas Boichat 修改了 copy_file_range() 希望能讓這些用戶滿意。patch 中為虛擬文件系統(tǒng)的 file_system_type 結(jié)構(gòu)增加了一個(gè) flag (FS_GENERATED_CONTENT),設(shè)置了這個(gè) flag 就表示這個(gè)文件系統(tǒng)文件的長(zhǎng)度是無(wú)法提前獲取的。copy_file_range() 會(huì)根據(jù)這個(gè)標(biāo)志來(lái)返回一個(gè)錯(cuò)誤代碼,這會(huì)進(jìn)而導(dǎo)致 io.Copy() 退而求其次繼續(xù)使用主動(dòng) copy 動(dòng)作。這個(gè)改動(dòng)似乎解決了眼前的問(wèn)題,但它肯定無(wú)法被合入 mainline。

          人們對(duì)這個(gè) patch 有一些反對(duì)意見(jiàn),首先,它要求對(duì)所有的虛擬文件系統(tǒng)進(jìn)行特殊標(biāo)記,這個(gè) patch set 并未全部標(biāo)記好,并且開(kāi)發(fā)者在今后添加新文件系統(tǒng)時(shí)一定要將它們?nèi)繕?biāo)記正確。正如 Greg Kroah-Hartman 所說(shuō),"這樣就意味著不斷的審查工作,我不認(rèn)為有人愿意在接下來(lái)的 20 年內(nèi)保持做這個(gè)工作"。

          但更大的問(wèn)題是,這種行為是否應(yīng)該被看作是一個(gè) bug。Boichat 認(rèn)為這是一個(gè)質(zhì)量倒退。在 5.3 之前代碼邏輯會(huì)自動(dòng)回到正常 copy,而在這個(gè)改動(dòng)之后就會(huì)無(wú)法 copy 了,并且也不報(bào)錯(cuò)。不過(guò) Kroah-Hartman 并不認(rèn)為這是個(gè)質(zhì)量回退,他繼續(xù)說(shuō)道:

          首先,人們?yōu)槭裁匆诤?jiǎn)單的/proc 和/sys 文件上使用 copy_file_range 呢?這些文件無(wú)法進(jìn)行 seek 操作(至少大多數(shù)是不行的),所以這種感覺(jué)就像 "快看,這有一個(gè)新的 syscall,我們可以把所有地方都改成用它了!" 的問(wèn)題,user space 不應(yīng)該這樣改。

          Dave Chinner 講得就更加直接了:

          這是一個(gè)針對(duì)性的解決方案,只針對(duì)那些存儲(chǔ) persistent data 的文件系統(tǒng)中的“常規(guī)文件”,用來(lái)加速數(shù)據(jù)復(fù)制(如 clone、server side offload、hardware offload)。這不是作為一種 copy 機(jī)制(也就是將數(shù)據(jù)從隨便某個(gè)文件描述符復(fù)制到另一個(gè)文件描述符)。

          在 Go system library 中把它當(dāng)做普通的文件復(fù)制機(jī)制來(lái)用,這不對(duì)。這是一個(gè) user space bug。用戶空間做錯(cuò)了事,用戶空間自己才需要修正。

          Go 開(kāi)發(fā)者 Ian Lance Taylor 認(rèn)為,不是很容易搞清楚什么時(shí)候可以使用 copy_file_range()。他指出,在 copy_file_range()的 man 頁(yè)面中沒(méi)有提到這些限制,并且按這種做法的話,這個(gè)系統(tǒng)調(diào)用的實(shí)用性就大大降低了:

          從我的角度來(lái)說(shuō),也就是作為內(nèi)核的一個(gè)用戶而不是內(nèi)核的一個(gè)開(kāi)發(fā)者,像這樣的系統(tǒng)調(diào)用會(huì)對(duì)于某些文件直接無(wú)效并且沒(méi)有提醒,而且也沒(méi)有提供任何方法來(lái)幫助我們:1)要么能提前確定系統(tǒng)調(diào)用會(huì)失敗,或者 2)在調(diào)用后確定系統(tǒng)調(diào)用確實(shí)失敗。那么這種系統(tǒng)調(diào)用就沒(méi)有用了。我永遠(yuǎn)不能使用那個(gè)系統(tǒng)調(diào)用,因?yàn)槲也恢浪欠駮?huì)成功。

          Chinner 說(shuō),可以看看是不是除了 read()以外沒(méi)有辦法判定這個(gè)文件是否有數(shù)據(jù),如果沒(méi)法判定,那就不要使用這個(gè)系統(tǒng)調(diào)用。但文件系統(tǒng)專家 Darrick Wong 也說(shuō):"我不知道怎么做到這一點(diǎn),Dave:)" 還有一個(gè)有趣的轉(zhuǎn)折,正如 Boichat 所說(shuō)的:sysfs 中的文件,并沒(méi)有報(bào)告自己長(zhǎng)度為零,而是聲稱長(zhǎng)度為 4,096 字節(jié),而它們的真實(shí)長(zhǎng)度可能比這個(gè)大,也可能比這個(gè)小。Chinner 的判定方法就算能實(shí)現(xiàn),在這些文件上也是無(wú)效的。

          Toward a real fix

          Wong 繼續(xù)說(shuō),他贊同 Go 開(kāi)發(fā)者的觀點(diǎn):copy_file_range()要么能正常按照預(yù)期來(lái)完成任務(wù),要么就返回一個(gè)錯(cuò)誤,這樣用戶空間就可以知道應(yīng)該回到老路上來(lái)進(jìn)行普通的 copy。他還提出了幾種可能解決這個(gè)問(wèn)題的方法,其中第一種是回到以前的樣子,也就是明確禁止跨文件系統(tǒng) copy。如果做不到這一點(diǎn),我們可以將這種 copy 限制在那些明確宣稱支持這個(gè)操作的那種文件系統(tǒng)中。Luis Henriques 針對(duì)這一想法稍作修改之后實(shí)現(xiàn)了 patch,也就是如果兩個(gè)文件系統(tǒng)的類型相同,并且所涉及的文件系統(tǒng)明確地實(shí)現(xiàn)了 copy_file_range() 操作,那么跨文件系統(tǒng)的 copy 仍然是被允許的。

          不過(guò),Trond Myklebust 指出內(nèi)核的 NFS daemon 使用 copy_file_range()機(jī)制在不同類型的文件系統(tǒng)之間復(fù)制文件,這個(gè) patch 也只好中止了,因?yàn)榘催@種實(shí)現(xiàn)的話會(huì)破壞這些基本功能。這種使用模式在其他文件系統(tǒng)中也存在,比如 Ceph 和 FUSE。對(duì)此,Henriques 添加了一個(gè)新的標(biāo)志(COPY_FILE_SPLICE),可以在內(nèi)核中使用這個(gè)標(biāo)志來(lái)表示應(yīng)該進(jìn)行跨文件系統(tǒng)種類的 copy。有人提出,這個(gè)標(biāo)志是否應(yīng)該被提供給用戶空間,這樣用戶空間能夠有某種方式能知道操作會(huì)不會(huì)成功,但最終決定不會(huì)這么做。

          在寫(xiě)這篇文章的時(shí)候,patch 的最終版本還沒(méi)有發(fā)布,但修復(fù)的方式很清晰了。當(dāng)從用戶空間調(diào)用時(shí),copy_file_range()只有在兩個(gè)文件系統(tǒng)類型相同、并且該文件系統(tǒng)對(duì)這個(gè)系統(tǒng)調(diào)用顯式地聲明支持的情況下,才會(huì)嘗試跨文件系統(tǒng)復(fù)制一個(gè)文件(這樣寫(xiě)應(yīng)該是考慮到了所有可能的情況了)。否則,調(diào)用將會(huì)顯式地返回錯(cuò)誤碼報(bào)錯(cuò),這樣用戶空間就知道必須以其他方式來(lái) copy 數(shù)據(jù)。所以,copy_file_range() 永遠(yuǎn)不會(huì)是一個(gè)通用的文件復(fù)制機(jī)制,但至少可以在那些準(zhǔn)備好面對(duì)出錯(cuò)情況的代碼中可靠地使用。

          不過(guò)在 copy_file_range()中還潛伏著一個(gè)陷阱。同大多數(shù)與 I/O 相關(guān)的系統(tǒng)調(diào)用一樣,copy_file_range()可以允許復(fù)制的字節(jié)數(shù)少于請(qǐng)求的字節(jié)數(shù)的情況。用戶空間需要檢查返回值,來(lái)看看實(shí)際復(fù)制了多少字節(jié)。目前沒(méi)有辦法區(qū)分是因?yàn)樵诒蛔x的這一端被縮短的(比如是到了文件的末尾)還是在寫(xiě)端被停止(這很可能表明 write error)。目前還沒(méi)有人想出能真正解決這個(gè)問(wèn)題的方法。

          上面這些內(nèi)容應(yīng)該讓大家了解了一個(gè)看起來(lái)非常簡(jiǎn)單的接口是如何變得復(fù)雜的。copy_file_range() 存在的時(shí)間還不長(zhǎng),就已經(jīng)暴露出了不少此前意想不到的問(wèn)題。后續(xù)可能還會(huì)有新問(wèn)題暴露出來(lái)。因此,難怪內(nèi)核開(kāi)發(fā)者在時(shí)不時(shí)被責(zé)備之后,會(huì)強(qiáng)烈希望要盡可能地保持 kernel 實(shí)現(xiàn)盡量簡(jiǎn)單了。

          全文完
          LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。

          歡迎分享、轉(zhuǎn)載及基于現(xiàn)有協(xié)議再創(chuàng)作~

          長(zhǎng)按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開(kāi)源社區(qū)的各種新近言論~



          瀏覽 105
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲四虎在线观看 | 国产精品色婷婷综合 | 18禁网站免费看 | 亚洲AV无码国产精品夜色午夜 | 九九无码电影 |