結合 Go 讀 APUE-檔案共享

zhaohu發表於2017-11-10

在公眾號 "別捉急" 上 同步了文章,並且可以點選原文連結閱讀:傳送門

檔案共享

UNIX 系統支援在不同程式間共享開啟檔案, 知識點:核心用於所有 I/O 的資料結構、原子操作。

概念性的 I/O 資料結構

核心用於所有 I/O 的資料結構,只是個概念性的,不一定適用,有個大體的輪廓就 OK。

  • 程式表 (process table entry) 中的記錄
  • 檔案表項 (file table entry)
  • v節點表項 (v-node table entry)

開啟檔案的核心資料結構

這是一個 開啟檔案的核心資料結構 圖。開啟檔案 這個操作是一個程式, 每個程式在程式表中都有一個記錄,而 開啟檔案程式記錄 中包含一張開啟檔案描述符表, 包括:

  • 檔案描述符標誌
  • 指向一個檔案表項的指標

檔案描述符表用 Go 抽象如下表示:

type fd struct {
    flags   int
    pointer *FileTableEntry
}

程式碼中 flags 的型別是隨便定義的(實際我沒查),由圖中看出 pointer 指向檔案表項 (file table entry), 核心為所有開啟檔案維持一張檔案表, 每個檔案表項包括:

  • 檔案狀態標誌 (讀、寫、添寫、同步和非阻塞)
  • 當前檔案偏移量
  • 指向該檔案 v 節點表項的指標

檔案表項用 Go 抽象如下表示:

type FileTableEntry struct {
    status  int
    offset  int
    pointer *VNodeTableEntry
}

由圖中看出 pointer 指向v節點表項 (v-node table entry), 每個開啟檔案都有一個 v 節點結構如下所示:

  • 檔案型別和對此檔案進行各種操作函式的指標,統稱為 v節點資訊
  • 該檔案的 i 節點: 檔案所有者、檔案長度、指向檔案實際資料塊在磁碟上所在位置的指標等

V 節點表項和 i 節點用 Go 抽象如下:

type VNodeTableEntry struct {
    information *Information
    vData       *INode
}

type INode struct {
    owner           *Owner
    length          int
    vNodeTableEntry *VNodeTableEntry
}

通過這種方式,來加深對 核心通用 I/O 資料結構 的理解。

如果兩個獨立程式各自開啟同一個檔案,則三者關係如下所示:

兩個獨立程式開啟同一個檔案

原子操作

一般而言,原子操作 (atomic operation) 指的是由多步組成的一個操作。如果該操作原子地執行,則要麼執行完所有步驟,要麼一步也不執行,不可能只執行所有步驟的一個子集。

函式 dup 和 dup2

下面兩個函式都可用來複制一個現有的檔案描述符。

#include <unistd.h>

int dup(int fd);

int dup2(int fd, int fd2);

上面函式中的引數:

  • fd 表示要複製的檔案描述符
  • fd2 表示複製後的檔案描述符

dup2 函式是可以指定複製後的檔案描述符,而 dup 是返回當前可用檔案描述符中的最小數值。

呼叫 dup(1) 函式後,程式表項,檔案表,v 節點表,之間的關係圖如下: dup(1) 後的核心資料結構

對於傳入的引數 fd2, 已經被開啟了,會先關閉。知道了這個點,就明白了,下面的操作中會先呼叫 close(fd2)

很不爽了,沒找到相應的 Go 的原始碼。複製一個描述符的另一種方法是使用 fcntl函式, dup2(fd, fd2) 等效於 close(fd2)fcntl(fd, F_DUPFD, fd2), 但不完全等效,因為 dup2(fd, fd2) 是個原子操作。

目前我先知道 fcntl函式 可以改變已經開啟檔案的屬性,就可以啦。

相關文章