首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

APUE 學習筆記(三):深入 Linux 文件與目錄元數據操作

     編輯:關於C語言

APUE 讀書筆記第 3 章:文件系統元數據完整指南

前兩篇介紹了文件 I/O 基礎操作,本章聚焦文件系統為每個文件維護的元數據——即 struct stat 結構體中的屬性信息。掌握這些操作是系統級程式設計、檔案工具開發、權限管理的基石。

1. 核心 API:stat 家族

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *path, struct stat *buf);   // 依路徑獲取,會展開符號鏈接
int fstat(int fd, struct stat *buf);            // 依已打開文件描述符獲取
int lstat(const char *path, struct stat *buf);  // 依路徑獲取,不展開符號鏈接

命名慣例:fun() 基礎版、ffun() 接受 fd、lfun() 不展開符號鏈接。此模式貫穿 Linux 系統調用(如 chmod/fchmod/lchmod、chown/fchown/lchown)。

2. struct stat 關鍵字段詳解

字段類型含義
st_devdev_t包含文件的設備 ID
st_inoino_tinode 號(文件唯一標識)
st_modemode_t文件類型 + 權限位圖(見下文)
st_nlinknlink_t硬鏈接計數
st_uid/st_giduid_t/gid_t擁有者用戶/組 ID
st_rdevdev_t設備文件的主/次設備號
st_sizeoff_t文件位元組大小(邏輯大小)
st_blksizeblksize_t文件系統塊大小(通常 4KB)
st_blocksblkcnt_t佔用的 512B 塊數(實際磁盤佔用 = st_blocks × 512)
st_atime/st_mtime/st_ctimetime_t最後訪問/修改/元數據修改時間

關鍵洞見:文件實際磁盤佔用看 st_blocks,而非 st_size。稀疏文件(如 lseek 跳過再寫入)st_size 可達 GB 級,但 st_blocks 僅佔幾塊。

3. 文件類型判斷(st_mode 位圖)

Linux 7 種文件類型:

  • 目錄 d、字符設備 c、塊設備 b、普通文件 -、符號鏈接 l、套接字 s、管道 p

對應提取宏(S_IFMT 遮罩 + S_IFREG 等):

struct stat info;
lstat("file.txt", &info);
if (info.st_mode & S_IFMT == S_IFREG)  // 普通文件
if (S_ISREG(info.st_mode))             // 推薦:直接用判斷宏

七個判斷宏:S_ISREG、S_ISDIR、S_ISCHR、S_ISBLK、S_ISFIFO、S_ISLNK、S_ISSOCK,傳入 st_mode 回傳 1/0。

4. 權限位詳解(st_mode 低 9 位)

三組權限:Owner(位 6-8)、Group(位 3-5)、Other(位 0-2),各含 r(4)/w(2)/x(1)。

常用提取宏:

  • S_IRWXU (0700) 擁有者全權遮罩
  • S_IRUSR (0400)、S_IWUSR (0200)、S_IXUSR (0100)
  • Group 對應 S_IRWXG (0070)、S_IRGRP (0040)…
  • Other 對應 S_IRWXO (0007)、S_IROTH (0004)…

5. 實用系統調用速查

函數功能關鍵點
access(path, mode)檢查實際用戶權限F_OK 存在性、R/W/X_OK 讀寫執行;不展開符號鏈接
umask(mask)設定進程文件創建掩碼返回舊值;mode & ~umask 為最終權限
chmod/fchmod修改文件權限支援 S_ISUID/S_ISGID/S_ISVTX
chown/fchown/lchown修改文件擁有者僅 root 可用(防配額繞過)
truncate/ftruncate截斷/擴展文件到指定長度擴展時以 \0 填充(建立稀疏文件)
link/unlink創建/刪除硬鏈接硬鏈接不跨分區、不可對目錄
symlink/readlink創建/讀取符號鏈接符號鏈接有獨立 inode、可跨分區
rename重命名/移動文件原子操作(同分區)
utime修改時間戳常用於備份工具保留原時間
mkdir/rmdir創建/刪除目錄rmdir 僅能刪空目錄
chdir/fchdir改變工作目錄守護進程常用 chdir("/")
getcwd獲取當前工作目錄絕對路徑需提供緩衝區

6. 目錄遍歷兩大流派

A. glob() 模式匹配

glob_t g;
glob("/home/user/*", 0, NULL, &g);      // 非隱藏文件
glob("/home/user/.*", GLOB_APPEND, NULL, &g); // 隱藏文件(追加)
for (size_t i = 0; i < g.gl_pathc; i++) puts(g.gl_pathv[i]);
globfree(&g);
  • 支援通配符、遞歸需自行實現(注意跳過 ./.. 避免循環)。
  • 動態分配記憶體,用完必須 globfree()。

B. opendir/readdir/closedir 串流式

DIR *dp = opendir(path);
struct dirent *ep;
while ((ep = readdir(dp)) != NULL) {
    if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) continue;
    // 處理 ep->d_name
}
closedir(dp);
  • 輕量、標準、適合遞歸遍歷(配合 d_type 判斷 DT_DIR)。
  • telldir/seekdir 可記錄/恢復讀取位置,較少用。

7. 實戰範例:簡易 du 實現(計算目錄實際磁盤佔用)

兩種版本分別用 glob 與 readdir 實現,核心邏輯:

  1. lstat 獲取 st_blocks。
  2. 若為目錄,遞歸累加子項 st_blocks(除 2 換算 KB)。
  3. 跳過 ./.. 防循環。
int64_t mydu(const char *path) {
    struct stat buf;
    lstat(path, &buf);
    int64_t sum = buf.st_blocks;
    if (S_ISDIR(buf.st_mode)) {
        DIR *dp = opendir(path);
        struct dirent *ep;
        char subpath[1024];
        while ((ep = readdir(dp))) {
            if (strcmp(ep->d_name, ".")==0 || strcmp(ep->d_name, "..")==0) continue;
            snprintf(subpath, sizeof(subpath), "%s/%s", path, ep->d_name);
            sum += mydu(subpath);
        }
        closedir(dp);
    }
    return sum;
}

8. UFS 文件系統結構補充

  • Inode 表:存文件屬性、15 個塊指針(12 直接 + 1 一級間接 + 1 二級間接 + 1 三級間接),支援超大文件。
  • 塊位圖 / Inode 位圖:加速空閒塊/Inode 查找。
  • 弱點:小文件過多易耗盡 Inode 而塊仍剩餘(因 Inode 數遠少於數據塊)。

掌握文件元數據操作,是從「寫應用」進階到「寫系統工具、中間件、容器運行時」的關鍵門檻。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved