檔案系統是作業系統的核心組成部分,負責管理和組織儲存裝置上的檔案。理解檔案系統的運作方式對於開發高效且穩定的程式至關重要。本文將探討 UNIX 檔案系統的架構,並著重介紹核心如何管理已開啟檔案的資料結構,以及如何使用系統呼叫進行檔案操作。核心利用檔案表、檔案描述符表和 i-node 表等資料結構來追蹤和管理已開啟的檔案,這些結構相互關聯,共同維護檔案的狀態和存取資訊。透過這些核心資料結構,作業系統可以有效地管理多個程式對檔案的存取,確保資料一致性和系統穩定性。此外,本文也詳細介紹了常用的檔案作業系統呼叫,例如 open、creat、close、read、write 和 lseek,並搭配 C 語言程式碼範例,演示如何在程式中使用這些系統呼叫進行檔案的開啟、建立、關閉、讀取、寫入和檔案指標的移動等操作。這些系統呼叫提供了底層的檔案操作介面,讓開發者能夠更精確地控制檔案的存取方式。
檔案管理
檔案結構及意涵
檔案是一組相關資訊的集合,記錄在次要儲存裝置上。對於使用者來說,檔案是次要儲存裝置中最小的單位,用來提供輸入和接收輸出給電腦程式。在儲存媒體中,檔案只是一連串的位元、位元組或記錄,其邏輯意義由檔案擁有者定義。
管理檔案在資料儲存中的結構和邏輯規則稱為檔案系統。為了理解檔案系統的內部運作,必須瞭解檔案和目錄如何外部呈現給使用者,以及如何內部表示在系統中。儲存媒體可能有多個檔案系統存在於不同的分割槽。本章將討論 UNIX 檔案系統的結構及與檔案相關的系統呼叫來操作檔案。
主要內容
本章將涵蓋以下主題:
- 檔案輸入/輸出(I/O)的系統呼叫和函式庫函式
- 管理所有已開啟檔案的核心資料結構
- 檔案存取許可權
- 維持使用者密碼和年齡資訊的標準檔案
- UNIX 檔案系統的結構及操作
學習目標
閱讀完本章後,您將能夠:
- 瞭解 UNIX 作業系統如何在次要儲存裝置中組織檔案
- 理解與檔案相關的活動,如命名、取得和分享
- 學習 UNIX 作業系統中如何實作檔案保護策略
檔案輸入/輸出
檔案輸入/輸出(I/O)是任何作業系統中重要的功能。檔案輸入用於提供資訊給執行中的程式以操作現有資訊。檔案輸出用於永久儲存透過 UNIX 程式建立或更新的資訊。UNIX 使用一些核心資料結構來管理檔案 I/O。這些資料結構儲存所有已開啟檔案所需的後設資料。UNIX 檔案 I/O 可以使用 C 標準函式庫函式或 UNIX 系統呼叫來執行。
Kernel 資料結構用於檔案 I/O
以下三個資料結構用於管理 UNIX 檔案系統中的已開啟檔案:
- 核心檔案表:檔案表中包含每個由任何程式開啟的檔案的條目。它是一個全域性資料結構,跟蹤讀取和寫入操作期間的位元組偏移量;每個條目包含標誌以指示讀取/寫入存取、阻塞/不阻塞等,並包含指向該檔案 v-node 表條目的指標。核心檔案表包含參考計數,顯示該檔案已被開啟的次數。
- 每程式檔案描述符表:監控該程式中的所有已開啟檔案。每個條目包含指向核心檔案表的指標。檔案描述符用於讀取、寫入和其他系統呼叫是此表中的索引。檔案描述符是一個無號正整數,負值保留以表示「無值」或錯誤條件。值 0、1 和 2 分別保留用於「標準輸入」、「標準輸出」和標準錯誤。
- 記憶體中的 i-node 表:記憶體中的 i-node 表保持每個已開啟檔案 i-node 的記憶體副本。
graph TD; A[程式] --> B[每程式檔案描述符表]; B --> C[核心檔案表]; C --> D[v-node 表]; D --> E[i-node];
UNIX 檔案管理核心資料結構
小段落標題:詳細解說
- 程式(A):代表執行中的程式。
- 每程式檔案描述符表(B):這是每個程式專屬的一個表格,記錄該程式所開啟的所有檔案。
- 核心檔案表(C):這是全域性質的表格,記錄所有被任何程式開啟過的檔案。
- v-node 表(D):這是虛擬節點表格,它包含了檔案在磁碟上的具體位置和其他屬性。
- i-node(E):這是索引節點,記錄了真實磁碟上的位置。
功能解說:
每當一個程式開啟一個檔案時,會在每程式檔案描述符表中建立一個條目,並將該檔案 i-node 的副本複製到 v-node 表中。當一個程式關閉一個檔案時,核心會刪除參照該檔案從每程式檔案描述符表中的條目並減少核心檔案表中的參考計數。如果參考計數變為零,則核心會刪除該條目並關閉該檔案。
UNIX 檔案 I/O 的系統呼叫
UNIX 提供基本的檔案 I/O 作業系統呼叫,如開啟、建立、關閉、讀取和寫入等操作。
# open 函式範例
file_descriptor = open("example.txt", "r")
內容解密:
open
函式:這是 UNX 提供的一個基本系統呼叫,用來開啟或建立一個新的檔案。file_descriptor
:當成功開啟一個檔案時,open
函式會傳回一個唯一的整數值作為檔案描述符(file descriptor),這個值可以用來進行後續的讀取、寫入等操作。"example.txt"
:這是要開啟或建立的檔案名稱。"r"
:這是模式引數,表示以只讀模式開啟檄子。其他常見模式包括"w"
(寫入模式),"a"
(追加模式),"r+"
(讀寫模式)。
建立或開啟新異常情況:
- 許可權問題:如果使用者沒有足夠的許可權來開啟或建立該檄子,
open
函式會傳回 -1 作為錯誤狀態。 - 不存在:如果試圖以只讀模式開啟一個不存在的檄子時,也會傳回 -1 作為錯誤狀態。
資料結構詳細探討
graph TD; A[Process A] --> B[File Descriptor Table for Process A]; B --> C[Kernel File Table]; C --> D[v-node Table]; C --> E[File Descriptor Table for Process B]; E --> F[Process B]; G[In-core Table] --> D; subgraph Kernel Space C; D; G; end subgraph User Space A; B; E; F; end
多程式間分享同一檔案情況下 UNX 的資料結構
小段落標題:詳細解說
- Process A 和 Process B 分別代表兩個不同執行中的程式。
- File Descriptor Table for Process A 和 File Descriptor Table for Process B 是每個程式專屬的一個表格,記錄該程式所開啟過所有可用文擇。
- Kernel File Table 是全域性質且唯一的一張表格,記錄所有被任何過戶有開啟過的文擇。
- v-node Table 是虛擬節點表格,它包含了真實磁碟上的具體位置和其他屬性。
- In-core Table 是記憶體中的 i-node 的副本。
功能解說:
當兩個不同過戶有分別在不同時間開啟同一文擇時(例如 “example.txt”),兩者都會分別在各自 Oversee 中增加對應 file descriptor table 中增加相應條目;而在 kernel space 中只有唯一一份 Kernel File Table, v-node Table 和 In-core Table 用於管理對 “example.txt” 的操作。
Conclusion
以上所述便是 UNX 檢查系統中多程式對同一檔案時如何利用陣列結構來管理和控制該過程流轉以及丟擲錯誤資訊相關知識點內容完整總結介紹及其過程流轉圖示說明!
檔案操作的基本系統呼叫
在 Unix 和 Linux 系統中,檔案操作是程式開發中不可或缺的部分。本文將探討檔案操作的基本系統呼叫,包括 open
、close
、creat
、read
、write
和 lseek
。這些系統呼叫是進行檔案操作的基礎,掌握它們的使用方法對於開發高效且穩定的程式至關重要。
檔案開啟與建立:open 與 creat
在開始檔案操作之前,首先需要開啟或建立一個檔案。open
和 creat
是兩個常用的系統呼叫,用於這些操作。
open 系統呼叫
open
系統呼叫用於開啟一個現有的檔案進行讀寫操作。如果檔案不存在,可以根據需要建立一個新檔案。
int open(const char *pathname, int oflag, int mode);
- pathname:要開啟的檔名。
- oflag:控制檔案開啟模式的標誌。
O_RDONLY
:只讀模式。O_WRONLY
:只寫模式。O_RDWR
:讀寫模式。O_APPEND
:追加模式(將位置指標設定到檔案末尾)。O_CREAT
:如果檔案不存在則建立。O_TRUNC
:如果檔案存在且以寫入或讀寫模式開啟,則截斷其長度為 0。
- mode:僅在建立新檔案時使用,設定新檔案的許可權位。
creat 系統呼叫
creat
系統呼叫用於建立一個新的空檔案。它等同於 open
呼叫:
int creat(const char *pathname, mode_t mode);
這等同於:
open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode);
這樣建立的檔案只能以寫入模式開啟。
檔案關閉:close
當完成對檔案的操作後,應該關閉該檔案以釋放資源。關閉檔案後會釋放所有對該檔案的鎖定,並減少檔案表中的參照計數。
int close(int fd);
- fd:要關閉的檔案描述符。
檔案讀取與寫入:read 與 write
完成檔案開啟或建立後,就可以進行讀取和寫入操作了。這些操作由 read
和 write
系統呼叫來完成。
read 系統呼叫
ssize_t read(int fd, void *buf, size_t count);
- fd:檔案描述符。
- buf:儲存讀取資料的緩衝區。
- count:要讀取的位元組數。
write 系統呼叫
ssize_t write(int fd, const void *buf, size_t count);
- fd:檔案描述符。
- buf:包含要寫入檔案資料的緩衝區。
- count:要寫入的位元組數。
檔案指標移動:lseek
在進行讀寫操作時,可能需要移動檔案指標到特定位置。這可以透過 lseek
系統呼叫來實作。
off_t lseek(int fd, off_t offset, int whence);
- fd:檔案描述符。
- offset:相對位置偏移量。
- whence:起始位置:
SEEK_SET
:從檔案開始處。SEEK_CUR
:從當前位置。SEEK_END
:從檔案結尾處。
例項分析
以下是一個完整的範例,展示瞭如何使用這些系統呼叫來進行基本的檔案操作:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
int fd;
char buffer[80];
const char *message = "Hello, World!";
// 建立或開啟一個檔案
fd = open("example.dat", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("Error opening file");
return 1;
}
// 寫入資料到檔案
if (write(fd, message, sizeof(message)) == -1) {
perror("Error writing to file");
close(fd);
return 1;
}
// 還原到檔案開始處
if (lseek(fd, 0L, SEEK_SET) == -1) {
perror("Error seeking in file");
close(fd);
return 1;
}
// 從檔案讀取資料
if (read(fd, buffer, sizeof(message)) == sizeof(message)) {
printf("File contents: %s\n", buffer);
} else {
perror("Error reading from file");
}
// 關閉檔案
if (close(fd) == -1) {
perror("Error closing file");
return 1;
}
return 0;
}
內容解密:
- open: 用來開啟一個已存在的檔案或者建立一個新的檔案。根據提供的路徑名和標誌引數決定如何開啟或建立該檔案。成功時傳回一個非負整數(即檔案描述符),失敗時傳回 -1。
- write: 用來將資料從快取區寫入到已開啟的檔案中。成功時傳回實際寫入的位元組數,失敗時傳回 -1。
- lseek: 用來設定已開啟檔案內部的一個偏移量(即指標),以便後續讀取或寫入操作能夠從指定位置開始。成功時傳回新位置,失敗時傳回 -1。
- read: 用來從已開啟的檔案中讀取資料到快取區內。成功時傳回實際讀取到快取區中的位元組數,遇到檔案結尾時傳回0,失敗時傳回 -1。
- close: 用來關閉一個已開啟的檔案。成功時傳回0,失敗時傳回 -1。
這些基本操作是掌握檔案管理和資料存取技術最基本也最重要的一步。希望透過此文章能夠幫助你更好地理解和應用它們。
小段落標題
透過這些簡單但重要的系統呼叫,玄貓展示瞭如何有效地管理和操作文字資料存取流程。這些技術工具不僅適用於日常程式設計任務之中,更是大型應用程式架構與維護工作中不可或缺的一部分。未來將持續探討更多相關技術細節與實踐經驗。