[apue] 文件中的空洞

来源:https://www.cnblogs.com/goodcitizen/archive/2022/07/26/hole_in_the_file.html
-Advertisement-
Play Games

空洞的概念 linux 上普通文件的大小與占用空間是兩個概念,前者表示文件中數據的長度,後者表示數據占用的磁碟空間,通常後者大於前者,因為需要一些額外的空間用來記錄文件的某些統計信息或附加信息、以及切分為塊的數據信息 (通常不會占用太多)。文件占用空間也可以小於文件尺寸,此時文件內部就存在空洞了。 ...


空洞的概念

linux 上普通文件的大小與占用空間是兩個概念,前者表示文件中數據的長度,後者表示數據占用的磁碟空間,通常後者大於前者,因為需要一些額外的空間用來記錄文件的某些統計信息或附加信息、以及切分為塊的數據信息 (通常不會占用太多)。文件占用空間也可以小於文件尺寸,此時文件內部就存在空洞了。

所謂空洞其實就是沒有分配存儲空間的數據塊,當訪問這些數據塊時,系統返回 0,就如同讀到空文件一般,當寫這些塊時,系統再實地分配對應的存儲空間。其實這個和記憶體中的虛址地址與物理地址的概念非常相似——操作系統可以預分配一大塊記憶體地址,這個地址只是一段連續的數字,用來保證虛擬地址不會被其它人占用,而對應的物理地址只在用到時才分配,這樣就避免了一下分配一大塊記憶體帶來的浪費問題。同理,如果抽象出一個文件地址和存儲地址來的話,完全可以套用上面的結論:連續的文件地址保證用戶可以訪問任意偏移的文件數據;文件中的空洞又避免了一下子分配太多的物理存儲帶來的浪費。

所以空洞不光針對文件,也可以針對記憶體,可以將虛址中的缺頁中斷理解為填補記憶體空洞的過程,文件中也有類似的機制。不過也有一些差異,例如記憶體因進程間共用而引入的 copy-on-write 機制,文件中就沒有。文件同一地址的數據如果被多個進程同時寫入時,只有最後一個寫入的會生效,前面的那些都會被覆蓋,因為文件是系統級別的概念,不像記憶體一樣專屬於某個進程。

空洞的產生

下麵分平臺說明。

Linux

所有的類 Unix 系統都差不多,方法比較簡單,滿足以下兩點即可:

  • 設置文件的偏移量 (lseek) 超過文件尾端
  • 並寫了某些數據後 (write)

此時原文件末尾到新文件末尾之間將標記為空洞。甚至都不需要寫一個程式,就可以驗證:

$ echo "this is a test" > test.txt
$ ls -lh test.txt
-rw-rw-r-- 1 yunh yunh 15 Oct 30 16:14 test.txt

$ stat test.txt
  File: test.txt
  Size: 15        	Blocks: 8          IO Block: 4096   regular file
Device: 805h/2053d	Inode: 35259462    Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    yunh)   Gid: ( 1000/    yunh)
Access: 2021-10-30 16:15:00.767760242 +0800
Modify: 2021-10-30 16:14:58.160147599 +0800
Change: 2021-10-30 16:14:58.160147599 +0800
 Birth: -

$ du -sh test.txt
4.0K	test.txt

$ truncate -s 1M test.txt
$ ls -lh test.txt
-rw-rw-r-- 1 yunh yunh 1.0M Oct 30 16:16 test.txt

$ stat test.txt
  File: test.txt
  Size: 1048576   	Blocks: 8          IO Block: 4096   regular file
Device: 805h/2053d	Inode: 35259462    Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    yunh)   Gid: ( 1000/    yunh)
Access: 2021-10-30 16:15:00.767760242 +0800
Modify: 2021-10-30 16:16:02.914508936 +0800
Change: 2021-10-30 16:16:02.914508936 +0800
 Birth: -

$ du -sh test.txt
4.0K	test.txt

上面的例子中,標稱 1MB 的 test.txt 文件只占用 4KB 空間。帶有空洞的文件複製後還有空洞嗎?這要看你用什麼方式複製了,如果是 cp 答案是有,如果是 cat + 重定向,沒有,請看下麵的例子:

$ cp test.txt foo.txt
$ cat test.txt > bar.txt
$ ls -lh *.txt
-rw-rw-r-- 1 yunh yunh 1.0M Oct 30 16:29 bar.txt
-rw-rw-r-- 1 yunh yunh 1.0M Oct 30 16:29 foo.txt
-rw-rw-r-- 1 yunh yunh 1.0M Oct 30 16:16 test.txt

$ stat *.txt
  File: bar.txt
  Size: 1048576   	Blocks: 2048       IO Block: 4096   regular file
Device: 805h/2053d	Inode: 35259560    Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    yunh)   Gid: ( 1000/    yunh)
Access: 2021-10-30 16:29:21.921709008 +0800
Modify: 2021-10-30 16:29:21.925707851 +0800
Change: 2021-10-30 16:29:21.925707851 +0800
 Birth: -
  File: foo.txt
  Size: 1048576   	Blocks: 8          IO Block: 4096   regular file
Device: 805h/2053d	Inode: 35259559    Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    yunh)   Gid: ( 1000/    yunh)
Access: 2021-10-30 16:29:16.751224261 +0800
Modify: 2021-10-30 16:29:16.755223068 +0800
Change: 2021-10-30 16:29:16.755223068 +0800
 Birth: -
  File: test.txt
  Size: 1048576   	Blocks: 8          IO Block: 4096   regular file
Device: 805h/2053d	Inode: 35259462    Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    yunh)   Gid: ( 1000/    yunh)
Access: 2021-10-30 16:19:51.460219249 +0800
Modify: 2021-10-30 16:16:02.914508936 +0800
Change: 2021-10-30 16:16:02.914508936 +0800
 Birth: -

$ du -sh *.txt
1.0M	bar.txt
4.0K	foo.txt
4.0K	test.txt

cp 後的文件保留了相同的空洞,cat + 重定向的則生成了沒有空洞的文件。從另一個側面說明讀取空洞時,系統是返回了 0 的。

Windows

與類 Unix 系統不同,windows 使用稀疏文件 (sparse) 來表示含有空洞的文件。不光是概念上有區別,實現上也有差別,例如使用類似 linux 的超出文件末尾寫策略,並不能生成一個稀疏文件。當然了,首先要保證文件系統是 NTFS,其次需要使用 windows 特定的 api 來完成這項工作。

  • SetFilePointer (lseek)
  • WriteFile (write)
  • SetEndOfFile (n/a)

並且需要在這樣做之前聲明文件為稀疏文件,系統才會為它生成空洞節省空間:

DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);

hFile 為打開的文件句柄。widnows 的空洞本質上是一種數據壓縮,將很多 0 壓縮在一起,不過確確實實起到了節省存儲空間的目的。

空洞的應用

下麵的腳本可以搜索文件系統中帶空洞的文件:

#! /bin/sh

function main()
{
    local path="."
    if [ $# -gt 0 ]; then 
        path="$1"
    fi

    echo "detect hole under ${path}"
    local size=0
    local space=0
    for file in $(find "${path}" -type f); do
        if [ -f "${file}" ]; then 
            size=$(stat -c "%s" "${file}")
            space=$(($(du -k "${file}" | awk '{print $1}')*1024))
            if [ ${size} -gt ${space} ]; then 
                echo "${file} has hole, space ${space}, size ${size}"
            fi
        else 
            # file-name has chinese character ?
            #echo "no ${file}"
            :
        fi
    done
    echo "done!"
}

main "$@"

在我的一臺筆記本設備上的確產生了輸出:

$ bash -f find_hole.sh /home 2>/dev/null
detect hole under /home
/home/yunh/snap/ohmygiraffe/common/.cache/mesa_shader_cache/index has hole, space 0, size 1310728
/home/yunh/.config/baidunetdisk/GPUCache/data_0 has hole, space 12288, size 45056
/home/yunh/.config/baidunetdisk/GPUCache/index has hole, space 45056, size 262512
/home/yunh/.config/baidunetdisk/GPUCache/data_3 has hole, space 98304, size 4202496
/home/yunh/.config/baidunetdisk/GPUCache/data_1 has hole, space 12288, size 270336
/home/yunh/.cache/mesa_shader_cache/index has hole, space 753664, size 1310728
/home/yunh/code/apue/04.chapter/foo.txt has hole, space 4096, size 1048576
/home/yunh/code/apue/04.chapter/test.txt has hole, space 4096, size 1048576
/home/yunh/code/apue/08.chapter/file.map has hole, space 20480, size 1048576
/home/yunh/.mozilla/firefox/g6azoga7.default-release/storage/default/https+++mail.126.com/cache/caches.sqlite has hole, space 86016, size 98304
/home/yunh/.mozilla/firefox/g6azoga7.default-release/storage/default/https+++126.com/cache/caches.sqlite has hole, space 86016, size 98304
/home/yunh/.mozilla/firefox/g6azoga7.default-release/storage/default/moz-extension+++d24d4498-4011-4423-805a-f6f4f5ace4f7^userContextId=4294967295/idb/3647222921wleabcEoxlt-eengsairo.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/g6azoga7.default-release/storage/default/https+++blog.csdn.net/cache/caches.sqlite has hole, space 61440, size 65536
/home/yunh/.mozilla/firefox/g6azoga7.default-release/cookies.sqlite has hole, space 98304, size 524288
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/moz-extension+++15f580b5-b741-4f58-b7b2-50144c678660^userContextId=4294967295/idb/3647222921wleabcEoxlt-eengsairo.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/http+++www.chinadegrees.cn/idb/3178482897EPkc.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++mail.126.com/cache/caches.sqlite has hole, space 176128, size 196608
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++translate.yandex.kz/idb/3977681304ystnro_ictoclel.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/moz-extension+++5cbf54b3-f5e8-493a-9096-76e3ad392e45^userContextId=4294967295/idb/3647222921wleabcEoxlt-eengsairo.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/http+++www.cdgdc.edu.cn/idb/3178482897EPkc.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++account.cnblogs.com/idb/1170976282GNEEEKROATNMDO.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/moz-extension+++f17a211a-4d0c-413c-8ced-df3b145f19ec^userContextId=4294967295/idb/3647222921wleabcEoxlt-eengsairo.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/moz-extension+++56a2ddfb-80ba-4bd7-913a-8ae756a8dc6f^userContextId=4294967295/idb/3647222921wleabcEoxlt-eengsairo.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/http+++www.chinadegrees.com.cn/idb/3178482897EPkc.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++126.com/cache/caches.sqlite has hole, space 122880, size 131072
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++126.com/idb/4197078560wnooriktbaorxi-pex.sqlite has hole, space 61440, size 65536
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++newtab.firefoxchina.cn/cache/caches.sqlite has hole, space 94208, size 98304
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++www.recaptcha.net/idb/548905059db.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/https+++blog.csdn.net/cache/caches.sqlite has hole, space 61440, size 65536
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/storage/default/moz-extension+++ae22fc49-8037-4c59-8299-2523bd5c1548^userContextId=4294967295/idb/3647222921wleabcEoxlt-eengsairo.sqlite has hole, space 45056, size 49152
/home/yunh/.mozilla/firefox/l7y3lhkj.default-esr/cookies.sqlite has hole, space 196608, size 524288

看起來像是用來做 cache 的,不明覺厲~

能想到的另一個應用場景就是下載大文件,例如一個 2GB 的文件,如果害怕因下載時間太長導致後面磁碟空間不足而失敗的情況,可以預先將文件擴展到 2GB,再分別填充其中的數據。不過這個更像是 windows 上的 SetEndOfFile 的應用場景,因為需要事先分配這麼多存儲空間,而不是像文件空洞那樣只給一個標稱的 2GB 文件而實際不分配存儲空間。從這個角度看,windows 確實有一定的優勢,因為在 linux 上占用 2GB 空間還真不是幾個調用就可以搞定的。

還能想到的一個場景就是分塊下載,這個和文件空洞確實可以產生一些化學反應。當大文件被切分為多個數據塊同時下載以提高速度時,傳統的方式是按塊號順序合併,如果中間有一個塊沒有下載完成,那麼之後的數據塊都不能合併到目標文件里去。如果使用文件空洞,哪個塊下載完了就可以先合併到目標文件,不存在合併順序的問題,從而解決上面的問題,防止太多塊文件留存在文件系統中。不過只要還有一個塊沒下載完,文件就是不完整的,肯定會影響後期的解壓、播放、載入,因此並沒有解決很大的問題。

最終結論就是,文件空洞並沒有記憶體空洞那麼有用,如果你遇到過它的應用場景,歡迎在評論區拍磚斧正~~

參考

[1]. lseek函數與文件空洞

[2]. windows稀疏文件


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在Saas系統下多租戶零腳本分表分庫讀寫分離解決方案 ## 介紹 本文ShardinfCore版本x.6.0.20+ 本期主角: - [`ShardingCore`](https://github.com/dotnetcore/sharding-core) 一款ef-core下高性能、輕量級針對分表 ...
  • 佈局(Panel族) WPF佈局使用的是Panel族佈局控制項,它們均派生自Panel抽象類,主要用於控制UI佈局。 WPF佈局容器 WPF常用的佈局容器及使用方法如下: Grid 網格,類似table表格,可靈活設置行列並放置控制項元素,比較常用 Grid的使用思路 - 聲明Grid容器 <Grid> ...
  • 1.簡介 jq 是一款非常強大的 JSON 命令行處理工具。其官網地址為:https://stedolan.github.io/jq/ 2.安裝 以CentOS為例: 1.線上安裝 yum install -y epel-release && yum install -y jq 2.離線安裝 訪問官 ...
  • Tampermonkey插件Mac版是一款瀏覽器腳本管理插件,支持大多常見瀏覽器,結合腳本大全網站 greasyfork,能夠方便的實現腳本旳一鍵安裝、自動更新、快速啟用等便捷功能,通過用戶腳本可以實現很多實用或有趣的功能。 詳情:Tampermonkey for Mac(油猴Safari瀏覽器插件 ...
  • 常用安全技術 3A: 認證:身份確認 授權:許可權分配 審計:監控做了什麼 安全通信 加密演算法和協議 對稱加密: 非對稱加密 單向加密:哈希(hash)加密 認證協議 對稱加密: 加密和解密使用的是同一個密鑰 是通過將原始數據分割成若幹塊來逐個進行加密 特點:效率高、速度快 缺點:加密解密使用的密鑰相 ...
  • magnet mac版是一款運行在蘋果電腦上的一款優秀的視窗大小控制工具,拖拽視窗到屏幕邊緣可以自動半屏,全屏或者四分之一屏幕,還可以設定快捷鍵完成分屏。這款專業的視窗管理工具當您每次將內容從一個應用移動到另一應用時,當您需要併排比較數據時,或是以其他方式進行多任務處理時,它都可以幫您妥善解決! 詳 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 kalibr標定板(棋盤格)用師兄的(長這樣) 步驟一:建立的ROS中的Kalibr的工作空間,建立一個名為:checkerboard.yaml的文件,內容為: target_type: 'checkerboard' targetCols: 11 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 從kubernetes 1.24開始,dockershim已經從kubelet中移除,但因為歷史問題docker卻不支持kubernetes主推的CRI(容器運行時介面)標準,所以docker不能再作為kubernetes的容器運行時了,即從ku ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...