儲存功能
| 儲存 |
|---|
| 檔案和目錄訪問 |
| 虛擬檔案系統 |
| 頁面快取 |
| 邏輯檔案系統 |
| 塊裝置 |
| 儲存驅動程式 |
儲存功能透過檔案和目錄提供對各種儲存裝置的訪問。大多數儲存都是永續性的,例如快閃記憶體、SSD 和傳統硬碟。另一種儲存是臨時的。檔案系統提供了一種抽象來將資訊組織成由唯一名稱標識的獨立資料塊(稱為檔案)。每個檔案系統型別定義自己的結構和邏輯規則,用於管理這些資訊組及其名稱。Linux 支援大量不同的檔案系統型別,包括本地和遠端、本地和來自其他作業系統的檔案系統。為了適應這種差異,核心定義了一個通用的頂層,即虛擬檔案系統(VFS)層。

四種基本檔案訪問系統呼叫
- man 2 open ↪ do_sys_open id - 按名稱開啟檔案並返回一個檔案描述符(fd)。以下函式對 fd 進行操作。
- man 2 close ↪ close_fd id
- man 2 read ↪ ksys_read id
- man 2 write ↪ ksys_write id
Linux 和 UNIX 中的檔案不僅僅是持久儲存上的物理檔案。檔案介面用於訪問管道、套接字和其他偽檔案。
🔧待辦事項
- man 2 readlink , man 2 symlink , man 2 link
- man 3 readdir ⇾ man 2 getdents
- man 7 path_resolution
- man 2 fcntl – 操作檔案描述符
⚙️ 檔案和目錄內部結構
📚 檔案和目錄參考
檔案鎖是允許程序協調對共享檔案的訪問的機制。這些鎖有助於防止多個程序或執行緒同時嘗試訪問同一個檔案時發生衝突。
💾歷史:強制鎖定功能在 Linux 5.15 及更高版本中不再受支援,因為該實現不可靠。
⚲ API
- man 8 lslocks – 列出本地系統鎖
- man 3 lockf – 對開啟的檔案應用、測試或刪除 POSIX 鎖
- man 2 flock – 對開啟的檔案應用或刪除 BSD 建議鎖
- man 2 fcntl – 操作檔案描述符
- F_SETLK id – 建議記錄鎖
- F_OFD_SETLK id – 開啟檔案描述符鎖
- flock id – 鎖引數
⚙️ 內部結構
🚀高階功能
AIO
- https://lwn.net/Kernel/Index/#Asynchronous_IO
- man 2 io_submit man 2 io_setup man 2 io_cancel man 2 io_destroy man 2 io_getevents
- uapi/linux/aio_abi.hinc
- fs/aio.csrc
- io/aio ltp
🌱自 2019 年 5 月釋出 5.1 版本以來的新功能
- https://blogs.oracle.com/linux/an-introduction-to-the-io_uring-asynchronous-io-framework
- https://thenewstack.io/how-io_uring-and-ebpf-will-revolutionize-programming-in-linux/
- io_uring_enter id io_uring_setup id io_uring_register id
- linux/io_uring.hinc
- uapi/linux/io_uring.hinc
- fs/.csrc
- https://lwn.net/Kernel/Index/#io_uring
- io_uring ltp
允許對多個檔案描述符進行非阻塞訪問。
高效事件輪詢epoll
⚲ API
- uapi/linux/eventpoll.hinc
- man 7 epoll
- man 2 epoll_create ↪ do_epoll_create id
- man 2 epoll_ctl ↪ do_epoll_ctl id
- man 2 epoll_wait ↪ do_epoll_wait id
⚙️ 內部結構
💾 歷史:select 和 poll 系統呼叫源於 UNIX
⚲ API
⚙️ 內部結構
🚀 高階特性
向量 I/O,也稱為分散/收集 I/O,是一種輸入輸出方法,透過該方法,單個過程呼叫可以按順序從多個緩衝區讀取資料並將其寫入單個數據流,或者從資料流讀取資料並將其寫入多個緩衝區,如緩衝區向量中定義的那樣。分散/收集指的是從給定緩衝區集中收集資料或將資料分散到給定緩衝區集中的過程。向量 I/O 可以同步或非同步執行。使用向量 I/O 的主要原因是效率和便捷性。
⚲ API
⚙️ 內部結構
📚 參考資料
- 快速分散/收集 I/O,GNU C 庫
- https://lwn.net/Kernel/Index/#Vectored_IO
- https://lwn.net/Kernel/Index/#Scattergather_chaining
The 虛擬檔案系統 (VFS) 是一個抽象層,位於具體的邏輯檔案系統之上。VFS 的目的是允許客戶端應用程式以統一的方式訪問不同型別的邏輯檔案系統。例如,VFS 可以用來透明地訪問本地和 網路儲存 裝置,而客戶端應用程式不會察覺到差異。它可以用來彌合 Windows、經典 Mac OS/macOS 和 Unix 檔案系統之間的差異,以便應用程式可以訪問這些型別本地檔案系統上的檔案,而無需知道它們正在訪問哪種型別的檔案系統。VFS 指定了核心和邏輯檔案系統之間的介面(或“契約”。因此,只需滿足契約,就可以輕鬆地為核心新增對新檔案系統型別的支援。
🔧 TODO: vfsmount id, vfs_create id, vfs_read id, vfs_write id
📚 VFS 參考資料
一個 檔案系統(或檔案系統)用於控制資料的儲存和檢索方式。如果沒有檔案系統,儲存區域中的資訊將是一個很大的資料主體,沒有辦法區分一個資訊片段的結束和下一個資訊片段的開始。透過將資料分成單獨的片段,並給每個片段一個名稱,可以輕鬆地分離和識別資訊。每個資料組被稱為“檔案”。用來管理資訊組及其名稱的結構和邏輯規則被稱為“檔案系統”。
有很多不同型別的檔案系統。每個檔案系統都有不同的結構和邏輯,以及速度、靈活度、安全性、大小等方面的特性。有些檔案系統是為特定應用程式而設計的。例如,ISO 9660 檔案系統專門為光碟而設計。
檔案系統可以在許多不同型別的儲存裝置上使用。每個儲存裝置都使用不同型別的介質。如今最常用的儲存裝置是 SSD。曾經使用過的其他介質包括硬碟、磁帶、光碟和 。在某些情況下,計算機的主記憶體(RAM)用於建立短期使用的臨時檔案系統。原始儲存被稱為塊裝置。
Linux 支援許多不同的檔案系統,但塊裝置上系統磁碟的常用選擇包括 ext* 系列(如 ext2、ext3 和 ext4)、XFS、ReiserFS 和 btrfs。對於沒有 快閃記憶體轉換層 (FTL) 或 記憶體技術裝置 (MTD) 的原始快閃記憶體,有 UBIFS、JFFS2 和 YAFFS 等等。 SquashFS 是一種常見的壓縮只讀檔案系統。NFS 和其他網路 FS 在段落 網路儲存 中有更詳細的描述。
⚲ Shell 介面
- cat /proc/filesystems
- ls /sys/fs/
- man 8 mount
- man 8 umount
- man 8 findmnt
- man 1 mountpoint
- man 1 df
基礎設施 ⚲ API 函式 register_filesystem id 註冊結構體 file_system_type id 並將它們儲存在連結列表中 ⚙️ file_systems id。函式 ext4_init_fs id 註冊 ext4_fs_type id。檔案系統開啟的操作被稱為掛載:ext4_mount id
⚙️ 內部結構
📚 參考資料
頁快取或磁碟快取是來自輔助儲存裝置(如硬碟驅動器)的記憶體頁的透明快取。作業系統在主記憶體的未使用部分中保留頁快取,從而可以更快地訪問快取頁的內容並提高整體效能。頁快取由核心實現,對應用程式來說大多是透明的。
通常,所有未直接分配給應用程式的物理記憶體都被作業系統用於頁面快取。由於記憶體否則將處於閒置狀態,並且在應用程式請求時可以輕鬆回收,因此通常沒有相關的效能損失,作業系統甚至可能會將此類記憶體報告為“空閒”或“可用”。頁面快取還有助於寫入磁碟。在將資料寫入磁碟期間已修改的主記憶體中的頁面被標記為“髒”,並且必須在它們可以被釋放之前重新整理到磁碟。當發生檔案寫入時,會查詢支援特定塊的頁面。如果它已經在頁面快取中找到,則寫入將完成到主記憶體中的該頁面。否則,如果寫入完全落在頁面大小邊界上,則該頁面甚至不會從磁碟讀取,而是被分配並立即標記為髒。否則,將從磁盤獲取該頁面(或這些頁面),並進行請求的修改。
並非所有快取的頁面都可以寫入,因為程式程式碼通常被對映為只讀或寫時複製;在後一種情況下,對程式碼的修改僅對程序本身可見,不會寫入磁碟。
⚲ API
- man 2 fsync ↪ do_fsync id
- man 2 sync_file_range ↪ ksys_sync_file_range id
- man 2 syncfs ↪ sync_filesystem id
📚 參考資料
更多
- DAX 的未來 - 直接訪問繞過快取
- Linux 核心 2.4 內部機制中的 Linux 頁面快取
🚀高階功能
將資料寫入儲存和讀取是非常佔用資源的操作。複製記憶體也是一項耗時且佔用 CPU 的操作。一組避免複製操作的方法被稱為零複製。零複製方法的目標是在系統內進行快速高效的資料傳輸。
第一個也是最簡單的方法是管道,由 shell 中的運算子 "|" 呼叫。而不是將資料寫入臨時檔案並讀取,而是透過管道有效地傳遞資料,繞過儲存。第二種方法是tee。
⚲ 系統呼叫
⚲ API 和 ⚙️ 內部機制
- man 2 pipe2 ↪ do_pipe2 id - 建立管道
- man 2 tee ↪ do_tee id- 複製管道內容
- 呼叫 link_pipe id
- man 2 sendfile ↪ do_sendfile id - 在檔案描述符之間傳輸資料,輸出可以是套接字。用於網路儲存 和伺服器。
- man 2 copy_file_range ↪ vfs_copy_file_range id - 在檔案之間傳輸資料
- man 2 splice ↪ do_splice id - 將資料拼接進/出管道。
- 關於哪一端是管道,有三種情況
- do_splice_from id - 只有輸入是管道
- do_splice_to id - 只有輸出是管道。
- splice_pipe_to_pipe id - 兩者都是管道
- man 2 vmsplice ↪
- vmsplice_to_pipe id – 將使用者頁面拼接進管道
- vmsplice_to_user id – 將管道拼接進使用者頁面
⚲ API
⚙️ 內部結構
🔧 TODO: zerocopy_sg_from_iter id 從 iov_iter 構建零複製 skb 資料報。用於 tap_get_user id 和 tun_get_user id.
📚 參考資料
- man 7 pipe
- man 7 fifo
- splice 和管道 doc
- splice(系統呼叫)
- LTP: pipe ltp, pipe2 ltp, tee ltp, sendfile ltp, copy_file_range ltp, splice ltp, vmsplice ltp
Linux 儲存基於塊裝置。
塊裝置提供對硬體的緩衝訪問,始終允許讀取或寫入任何大小的塊(包括單個字元/位元組),並且不受對齊限制的影響。它們通常用於表示諸如硬碟之類的硬體。
⚲ 介面
- linux/genhd.hinc
- linux/blk_types.hinc
- bio id – 塊層和下層的主要 I/O 單位
- linux/bio.hinc
- block_deviceid
- alloc_disk_node id 分配 gendisk id
- add_diskid
- block_device_operationsid
- register_blkdevid
- bio id - 塊層和下層(即驅動程式和堆疊驅動程式)的主要 I/O 單位
- requestid
- request_queueid
⚙️ 內部結構。
👁 示例
- drivers/block/brd.c src - 小型 RAM 支援的塊裝置驅動程式
- drivers/block/null_blksrc
裝置對映器是核心提供的框架,用於將物理塊裝置對映到更高級別的“虛擬塊裝置”。它構成了 LVM2、軟體 RAID 和 dm-crypt 磁碟加密的基礎,並提供額外的功能,例如檔案系統快照。
裝置對映器透過將資料從虛擬塊裝置(由裝置對映器本身提供)傳遞到另一個塊裝置來工作。資料也可以在傳輸過程中進行修改,例如,在裝置對映器提供磁碟加密的情況下進行修改。
需要建立新對映裝置的使用者空間應用程式透過 libdevmapper.so 共享庫與裝置對映器通訊,該庫反過來向 /dev/mapper/control 裝置節點發出 ioctl。
裝置對映器提供的功能包括線性、條帶化和錯誤對映,以及加密和多路徑目標。例如,兩個磁碟可以透過一對線性對映(每個磁碟一個)連線到一個邏輯卷中。另一個例子是,crypt 目標使用 Linux 核心的加密 API 對透過指定裝置的資料進行加密。
以下對映目標可用
- cache - 允許透過使用固態驅動器 (SSD) 作為硬碟驅動器 (HDD) 的快取來建立混合卷
- crypt - 使用 Linux 核心的加密 API 提供資料加密
- delay - 將讀取和/或寫入到不同的裝置延遲(用於測試)
- era - 以類似於線性目標的方式執行,同時跟蹤在使用者定義的時間段內寫入的塊
- error - 模擬所有對映塊的 I/O 錯誤(用於測試)
- flakey - 模擬週期性的不可靠行為(用於測試)
- linear - 將連續的塊範圍對映到另一個塊裝置
- mirror - 對映映象邏輯裝置,同時提供資料冗餘
- multipath - 透過使用其路徑組支援多路徑裝置的對映
- raid - 提供與 Linux 核心的軟體 RAID 驅動程式 (md) 的介面
- snapshot 和 snapshot-origin - 用於建立 LVM 快照,作為基礎複製寫入方案的一部分
- striped - 將資料跨物理裝置條帶化,以條帶數量和條帶化塊大小作為引數
- zero - 等效於
/dev/zero,所有讀取都返回零塊,寫入被丟棄
📚 參考資料
blk-mq API 透過利用多個佇列進行並行處理來提高 IO 效能,從而解決了傳統單佇列設計中的瓶頸。它使用軟體佇列來排程、合併和重新排序請求,並使用硬體佇列與裝置直接介面。如果硬體資源有限,請求將暫時排隊以供以後排程。
⚲ 介面
- linux/blk-mq.hinc
- blk_mq_hw_ctx id – 硬體排程佇列
⚙️ 內部結構
- block/blk-mq.hsrc
- blk_mq_ctx id – 軟體暫存佇列
- block/blk-mq.csrc
- ...
📖 參考資料
I/O 排程(或磁碟排程)是核心選擇的方法,用於決定以何種順序將塊 I/O 操作提交到儲存卷。I/O 排程通常需要處理硬碟驅動器,這些驅動器對放置在遠離磁碟磁頭當前位置的請求(此操作稱為查詢)的訪問時間很長。為了最大限度地減少對系統性能的影響,大多數 I/O 排程程式實現了電梯演算法的變體,該演算法重新排序傳入的隨機排序請求,以便以最小的機械臂/磁頭移動來訪問相關資料。
可以使用特定塊裝置的 sysfs 檔案系統中的 /sys/block/<block_device>/queue/scheduler 檔案在執行時切換特定塊裝置使用的 I/O 排程程式。某些 I/O 排程程式還具有可調引數,可以透過 /sys/block/<block_device>/queue/iosched/ 中的檔案進行設定。
⚲ 介面
⚙️ 內部結構
- block/elevator.csrc
- block/Kconfig.ioschedsrc
- block/bfq-iosched.csrc
- block/kyber-iosched.csrc
- block/mq-deadline.csrc
- include/trace/events/block.hinc
📖 參考資料
- I/O 排程
- 電梯演算法
- 切換排程程式 doc
- BFQ - 預算公平排隊 doc
- 截止日期 IO 排程程式可調引數 doc
- https://www.cloudbees.com/blog/linux-io-scheduler-tuning/
- https://wiki.ubuntu.com/Kernel/Reference/IOSchedulers
📖 參考資料
📚 進一步閱讀
🔧待辦事項
⚙️ 內部結構
- drivers/scsi src - 小型計算機系統介面
- drivers/virtiosrc
- drivers/sdio src - 安全數字輸入輸出
- drivers/nvmem src - 非易失性儲存裝置,如 EEPROM、Efuse
- drivers/mtd src - 用於🤖嵌入式裝置的記憶體技術裝置
NVM Express 驅動程式提供了對計算機 非易失性儲存器 的訪問。本地儲存透過 PCI Express 匯流排連線。PCI NVMe 裝置驅動程式的入口點是 nvme_init id。遠端儲存驅動程式稱為目標,本地 代理 驅動程式稱為主機。 結構 連線遠端目標和本地主機。結構可以基於 RDMA、TCP 或 光纖通道 協議。
⚲ API
⚙️ 內部實現:
⚲ 介面
- drivers/nvme/host/nvme.hsrc
- nvme_init_ctrl id 初始化 NVMe 控制器結構 nvme_ctrl id,包含操作 nvme_ctrl_ops id
- nvme_scan_work id 的一個子程式使用 device_add_disk id 新增一個新的磁碟。
- nvme_init_ctrl id 初始化 NVMe 控制器結構 nvme_ctrl id,包含操作 nvme_ctrl_ops id
- nvme_init id - 本地 PCI nvme 模組初始化。
- nvme_core_init id - 模組初始化。
結構
⚲ 介面
- drivers/nvme/host/fabrics.hsrc
- nvmf_init id - 結構模組初始化。
⚙️ 內部實現
- nvmf_init id - 結構模組初始化。
⚲ 介面: drivers/nvme/target/nvmet.h src
- nvmet_init id - 模組初始化。
- fcloop_init id - 環回測試模組初始化,可用於測試 NVMe-FC 傳輸介面。
👁 例如: nvme_loop_init_module id nvme 環回
- nvme_loop_transport id - 結構操作
- nvme_loop_ops id - 目標操作
附錄
[edit | edit source]🚀 高階
- man 1 pidstat – 報告任務統計資訊。
- /proc/self/io – 程序的 I/O 統計資訊(參見 man 5 proc)。
💾 歷史儲存驅動程式
📖 關於儲存的更多閱讀