在 Linux 系統開發中,理解檔案操作和系統呼叫至關重要。這些底層機制是程式與作業系統互動的橋樑,讓程式得以讀寫檔案、管理目錄、控制檔案許可權等。本文將深入解析 lseek、link、unlink、dup、stat 和 fstat 等關鍵系統呼叫,並探討檔案許可權、umask 和特殊許可權的設定與應用,幫助讀者更有效率地操作檔案和目錄。這些系統呼叫提供了對檔案指標位置調整、硬連結建立與刪除、檔案描述符複製以及檔案狀態查詢等功能。此外,瞭解檔案許可權的設定方式,包括 umask 的作用和特殊許可權(Set-UID、Set-GID 和 Sticky Bit)的應用,對於保障系統安全和資料完整性至關重要。文章中提供的程式碼範例和流程圖示,可以幫助讀者更直觀地理解這些概念,並在實際程式設計中靈活運用。
檔案操作與系統呼叫探討
在 Unix-like 系統中,檔案操作與系統呼叫是實作程式與作業系統之間互動的核心技術。以下將探討一些常見的檔案作業系統呼叫,包括 lseek、link、unlink、dup、以及 stat 和 fstat,並提供具體的程式碼範例和詳細解說。
檔案指標位置調整
lseek 是一個用於調整檔案指標位置的系統呼叫。這個功能對於需要在檔案中進行隨機存取的應用程式非常重要。以下是 lseek 的使用範例:
#include <stdio.h>
#include <fcntl.h>
int main()
{
int fd;
long position;
fd = open("datafile.dat", O_RDONLY);
if (fd != -1)
{
position = lseek(fd, 0L, 2); /* seek 0 bytes from end-of-file */
if (position != -1)
printf("The length of datafile.dat is %ld bytes.\n", position);
else
perror("lseek error");
}
else
printf("can’t open datafile\n");
close(fd);
}
內容解密:
這段程式碼展示瞭如何使用 lseek 來查詢檔案的長度。首先,使用 open 函式以只讀模式開啟名為 datafile.dat 的檔案。如果成功開啟檔案,則使用 lseek 將檔案指標移動到檔案結尾。這裡的 2 表示從檔案結尾開始計算偏移量。如果成功,則輸出檔案的長度;否則,輸出錯誤資訊。
建立硬連線
link 系統呼叫可以為現有的檔案建立一個新的硬連線。硬連線是指多個檔名指向同一個 i-node,因此對其中一個檔案進行修改會反映到所有連線上。
#include <stdio.h>
int main()
{
if ((link("file.old", "file.new")) == -1)
{
perror("ERROR");
exit(1);
}
exit(0);
}
內容解密:
這段程式碼展示瞭如何使用 link 系統呼叫為現有的 file.old 建立一個新的硬連線 file.new。如果操作成功,傳回 0;否則,傳回 -1 並輸出錯誤資訊。
刪除硬連線
unlink 系統呼叫可以刪除一個硬連線及其對應的目錄專案,並減少該檔案的連線計數。如果連線計數達到零,則會刪除檔案內容。
#include <stdio.h>
int main()
{
if ((unlink("file.old")) == -1)
{
perror("ERROR");
exit(1);
}
exit(0);
}
內容解密:
這段程式碼展示瞭如何使用 unlink 系統呼叫刪除名為 file.old 的硬連線。如果成功刪除,傳回 0;否則,傳回 -1 並輸出錯誤資訊。
副本檔案描述符
dup 系統呼叫可以複製一個現有的檔案描述符,使其指向同一個檔案表專案。這對於需要在不同描述符上操作同一個檔案時非常有用。
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int fd;
fd = open("file.dat", O_WRONLY | O_CREAT, S_IREAD | S_IWRITE);
if (fd == -1)
{
perror("FILE already opened");
exit(1);
}
close(1); /* close standard output */
dup(fd); /* fd will be duplicated */
close(fd); /* close the extra slot */
printf("Hello\n");
exit(0);
}
內容解密:
這段程式碼展示瞭如何使用 dup 系統呼叫複製一個檔案描述符。首先,以寫入模式建立或開啟名為 file.dat 的檔案。然後關閉標準輸出(描述符 1),並將剛剛建立的檔案描述符複製到標準輸出。最後,關閉原始檔案描述符並輸出 “Hello” 命令列。
查詢檔案狀態
stat 和 fstat 系統呼叫可以取得關於檔案狀態的資訊,如擁有者、i-node 編號、檔案型別、大小、許可權等。
#include <stdio.h>
#include <sys/stat.h>
int main()
{
struct stat fileStat;
if (stat("example.txt", &fileStat) == -1)
{
perror("ERROR");
return -1;
}
printf("File size: \t%ld bytes\n", fileStat.st_size);
printf("Number of links: \t%d\n", fileStat.st_nlink);
printf("File inode: \t%ld\n", fileStat.st_ino);
return 0;
}
內容解密:
這段程式碼展示瞭如何使用 stat 系統呼叫取得名為 example.txt 的檔案狀態資訊。首先,定義了一個 struct stat 型別的變數來儲存檔案狀態資訊。然後使用 stat 函式取得檔案資訊並進行錯誤處理。最後,輸出檔案大小、連線數和 i-node 編號。
基本許可權管理
在 Unix-like 系統中,每個檔案都有擁有者、群組和其他人的許可權設定。這些許可權決定了誰可以讀取、寫入或執行該檔案。以下是如何檢視和管理這些許可權的一些基本方法。
ls -l /usr/bin/new1
內容解密:
這條命令使用 ls -l 檢視 /usr/bin/new1 檔案的詳細資訊,包括許可權設定。第一列顯示的是檔案型別和許可權(例如 -rwxr-xr-x),表示擁有者具有讀寫執行許可權,群組具有讀執行許可權,其他人也具有讀執行許可權。
流程圖示
此圖示展示了不同系統呼叫之間的關係及其操作流程。
內容解密:
此圖示展示了各種檔案作業系統呼叫之間的關係及其操作流程。從開啟檔案開始,可以進行多種操作如查詢檔案長度、建立硬連線、刪除硬連線、複製檔案描述符以及取得檔案狀態資訊等。
Linux 檔案許可權及特殊許可權解密
在 Linux 系統中,檔案許可權的設定和管理是保護系統安全及資料完整性的重要部分。本文將探討 Linux 檔案許可權的基本概念、許可權表示法以及特殊許可權(如 Set-UID、Set-GID 和 Sticky Bit)的作用與應用。
檔案許可權的基本概念
Linux 系統中的每個檔案和目錄都有三種基本許可權:讀取(read)、寫入(write)和執行(execute)。這些許可權分別對應於檔案的擁有者(owner)、群組成員(group)和其他使用者(others)。
範例解析
以下是一個檔案許可權的範例:
-rwxr-xr-x 1 user1 user1 8632 Sep 19 2021 /usr/bin/new1
- 第一個字元
-:表示這是一個普通檔案。 - 接下來的九個字元
rwxr-xr-x:分別代表擁有者、群組成員和其他使用者的許可權。rwx:擁有者可以讀取、寫入和執行。r-x:群組成員和其他使用者只能讀取和執行。
許可權表示法
Linux 使用一個九位元向量來表示檔案許可權,每三位元對應於一種使用者型別(擁有者、群組、其他)。
r:讀取許可權,對應二進位制的100,即十進位制的4。w:寫入許可權,對應二進位制的010,即十進位制的2。x:執行許可權,對應二進位制的001,即十進位制的1。
例如,-rwxr-xr-x 對應於十進位制的 755:
- 擁有者:
rwx(4+2+1=7) - 群組成員:
r-x(4+0+1=5) - 其他使用者:
r-x(4+0+1=5)
特殊許可權
除了基本的讀取、寫入和執行許可權外,Linux 還提供了一些特殊許可權來滿足特定需求。這些特殊許可權包括 Set-UID、Set-GID 和 Sticky Bit。
Set-UID
Set-UID(Set User ID upon execution)是一種特殊許可權,當設定在可執行檔案上時,該檔案會以檔案擁有者的身份執行,而不是以執行該檔案的使用者身份執行。
例如,密碼變更程式通常會設定 Set-UID,因為它需要修改 /etc/passwd 檔案,而這個檔案只有 root 才有寫入許可權。
-rwsr-xr-x 1 root root 8632 Sep 9 2008 /usr/bin/passwd
在這個例子中,s 號代替了 x,表示該檔案具有 Set-UID 屬性。
Set-GID
Set-GID(Set Group ID upon execution)與 Set-UID 類別似,但它適用於群組。當設定在可執行檔案或目錄上時,該檔案或目錄會以擁有者所屬群組的身份執行或建立。
例如,某些分享目錄可能會設定 Set-GID 屬性,使得在該目錄中建立的所有新檔案都屬於同一個群組。
drwxr-s--- 2 user1 group1 4096 Oct 10 2023 /shareddir
在這個例子中,s 號代替了群組成員的 x,表示該目錄具有 Set-GID 屬性。
Sticky Bit
Sticky Bit 主要用於分享目錄,如 /tmp 或 /var/tmp。當設定在目錄上時,即使其他使用者具有寫入許可權,他們也無法刪除或重新命名由其他使用者建立的檔案。
drwxrwxrwt 5 root root 4096 Oct 10 2023 /tmp
在這個例子中,最後一位元為 t ,表示該目錄具有 Sticky Bit 屬性。
檔案建立時的 User ID 和 Group ID
當新檔案建立時,其 User ID 和 Group ID 的設定如下:
- User ID:新檔案的 User ID 與建立該檔案的程式的 Effective User ID 一致。
- Group ID:新檔案的 Group ID 則可能是:
- 建立該檔案程式的 Effective Group ID。
- 新檔案所在目錄的 Group ID(如果該目錄具有 Set-GID 屬性)。
效果 User ID 和 Group ID
大多數情況下,Effective User ID (EUID) 與 User ID (UID) 一致,Effective Group ID (EGID) 與 Group ID (GID) 一致。然而,當可執行檔具備 Set-UID 或 Set-GID 屬性時,EUID 或 EGID 則會變成該可執行檔擁有者或群組所屬之身份。
例如:
-rws--x--x 1 root root /usr/bin/passwd
當普通使用者執行 /usr/bin/passwd時 ,EUID 被設為root 的UID ,使得程式可以更新 /etc/passwd
檔案存取許可權與系統呼叫
在 Unix 及類別 Unix 系統中,檔案的存取許可權是透過檔案模式(mode)來控制的。這些許可權可以透過各種系統呼叫來修改或查詢,包括 chmod、chown、access 等。這些系統呼叫提供了對檔案及目錄的精細控制,確保資料的安全性和完整性。
檔案許可權與 umask
在 Unix 系統中,檔案的許可權是透過一個稱為 umask 的掩碼來設定的。umask 是一個八進位制數字,用來決定新建檔案或目錄的預設許可權。當建立新檔案或目錄時,系統會根據 umask 值來調整預設的許可權。
檔案存取許可權掩碼
st_mode 成員是 stat 結構的一部分,包含了檔案的許可權資訊:
S_IRWXU: 擁有者可讀、寫、執行S_IRUSR: 擁有者可讀S_IWUSR: 擁有者可寫S_IXUSR: 擁有者可執行S_IRWXG: 群組可讀、寫、執行S_IRGRP: 群組可讀S_IWGRP: 群組可寫S_IXGRP: 群組可執行S_IRWXO: 其他人可讀、寫、執行S_IROTH: 其他人可讀S_IWOTH: 其他人可寫S_IXOTH: 其他人可執行
umask() 呼叫
每個程式都有一個自己的 umask 值,這個值會從父程式繼承。umask() 函式用來設定程式的檔案建立掩碼並傳回之前的掩碼值。低階九位的掩碼會在每次建立檔案時使用。
Unix 核心在建立普通檔案時會設定預設許可權為 666,而對於目錄則是 777。當使用 open()、mkdir() 或其他系統呼叫建立檔案或目錄時,核心會將當前的 umask 值從預設許可權中減去,以設定最終的許可權。
範例
假設當前的 umask 值為 044,若要為一個普通檔案設定預設許可權為 666,核心會從 666 中減去 044,結果是 622。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int fd;
int old_mask;
old_mask = umask(0);
fd = open("f1", O_WRONLY | O_CREAT | O_TRUNC, 0666);
close(fd);
printf("created f1: 0666\n");
fd = open("f2", O_WRONLY | O_CREAT | O_TRUNC, 0200);
close(fd);
printf("created f2: 0200\n");
umask(022);
fd = open("f4", O_WRONLY | O_CREAT | O_TRUNC, 0777);
close(fd);
printf("created f4: %o\n", 0777 & ~022);
fd = open("f5", O_WRONLY | O_CREAT | O_TRUNC, 0200);
close(fd);
printf("created f5: %o\n", 0200 & ~022);
return 0;
}
在這個範例中,首先將 umask 設定為 0,使得所有建立的檔案都會使用在開啟時指定的許可權。然後將 umask 改為 022,這樣在建立檔案時會從指定的許可權中減去這個值。
access() 函式
access() 函式用來檢查指定路徑名稱的檔案是否具有特定的存取許可權。它接受兩個引數:路徑名稱和模式。
int access(const char *pathname, int mode);
如果成功完成,則傳回值為 0;否則傳回 -1,並設定全域變數 errno 指示錯誤。
改變所有者與模式
改變檔案所有者和模式是對 inode 的操作。進行這些操作的程式必須是超級使用者或該檔案的擁有者。
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int chown(const char *pathname, uid_t owner, gid_t group);
chmod() 函式
int chmod(const char *pathname, mode_t mode);
改變指定路徑名稱的檔案模式。
chown() 函式
int chown(const char *pathname, uid_t owner, gid_t group);
改變指定路徑名稱的檔案所有者和群組。
掛載與解除安裝檔案系統
掛載檔案系統使得檔案系統中的資料能夠像檔案一樣進行讀寫操作,而解除安裝則反之。掛載操作透過 mount() 函式完成,解除安裝透過 umount() 函式完成。
mount() 函式
int mount(const char *source, const char *target,
const char *filesystemtype,
unsigned long mountflags,
const void *data);
將 source 指定的檔案系統掛載到 target 指定的目錄下。
umount() 函式
int umount(const char *target);
解除安裝指定目錄下掛載的檔案系統。
錄錄相關系統呼叫
Unix 中一切皆為檔案(everything is a file),因此也有一系列與目錄相關的系統呼叫。這些呼叫用於建立、刪除和操作目錄內容。
mkdir() 函式
int mkdir(const char *path, mode_t mode);
建立一個新目錄並設定其模式。
#include <sys/stat.h>
int main() {
if (mkdir("new_directory", S_IRWXU | S_IRGRP | S_IXGRP) != 0) {
perror("mkdir");
return -1;
}
return 0;
}
此圖示展示了 mkdir 函式在建立新目錄時如何應用 umask 和預設模式:
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Linux 檔案操作與系統呼叫深度解析
package "圖論網路分析" {
package "節點層" {
component [節點 A] as nodeA
component [節點 B] as nodeB
component [節點 C] as nodeC
component [節點 D] as nodeD
}
package "中心性指標" {
component [度中心性
Degree Centrality] as degree
component [特徵向量中心性
Eigenvector Centrality] as eigen
component [介數中心性
Betweenness Centrality] as between
component [接近中心性
Closeness Centrality] as close
}
}
nodeA -- nodeB
nodeA -- nodeC
nodeB -- nodeD
nodeC -- nodeD
nodeA --> degree : 計算連接數
nodeA --> eigen : 計算影響力
nodeB --> between : 計算橋接度
nodeC --> close : 計算距離
note right of degree
直接連接數量
衡量局部影響力
end note
note right of eigen
考慮鄰居重要性
衡量全局影響力
end note
@enduml
說明:
- Create Directory: 使用者嘗試建立一個新目錄。
- Apply Default Mode: 應用預設模式(例如:正規檔案為
666, 目錄為777)。 - Check umask: 檢視當前程式的 umask 值。
- Adjust Mode: 若有 umask 值,則從預設模式中減去 umask 值。
- Create Directory with Adjusted Mode: 建立帶有調整後模式的新目錄。
玄貓提醒大家,「一切皆為檔案」不僅僅是一句口號,它實實在在地影響著你如何進行檔案存取與管理。瞭解這些細節將能幫助你更高效地處理各種檔案與目錄操作。