Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 文件鎖:建議性鎖 vs 強制性鎖 核心差異與啟用指南

編輯:Linux 教學

前言

多進程並發訪問同一文件時,fcntl() 文件鎖是核心協調手段。但鎖分建議性與強制性兩種語意,差異巨大:前者靠程序員自覺,後者由內核強制攔截。本文深度對比兩者機制、fcntl/lockf 適用範圍、建議性鎖「關文件即釋放」陷阱、強制性鎖啟用條件(掛載選項 + 文件權限位)。

一、核心差異對照

維度建議性鎖 (Advisory)強制性鎖 (Mandatory)
執行主體應用程式自覺檢查內核 VFS 層強制攔截
生效條件進程主動呼叫 fcntl(F_SETLK) 並檢查返回值文件系統掛載 mand + 文件 setgid + !group_exec
攔截範圍僅攔截同樣用 fcntl 協作的進程所有 read/write/open 系統調用
預設行為Linux 預設啟用預設關閉,需顯式啟用
釋放時機文件描述符關閉即自動釋放(即使進程未解鎖)進程顯式解鎖或進程終結
典型用途數據庫、協作式應用、大多數場景強制隔離、非協作環境、安全合規

二、建議性鎖「關文件即釋放」陷阱

int fd = open("data.txt", O_RDWR);
fcntl(fd, F_SETLKW, &write_lock);  // 加寫鎖
// ... 業務邏輯 ...
close(fd);   // 【陷阱】鎖自動釋放!其他進程可立即搶入
// 甚至:
fcntl(fd, F_SETLKW, &write_lock);
close(fd);   // 鎖已釋放
open("data.txt", O_RDWR); // 重新打開,新 fd 無鎖保護

根因:建議性鎖綁定於 struct file(打開文件實例),而非 inode。close() 釋放 file 時內核自動清理該 file 關聯的所有建議性鎖。

對策:
1. 加鎖後持有 fd 直到業務完全結束。
2. 需跨函數/模組共享鎖時,傳遞 fd 或封裝 RAII 守衛。
3. 或改用強制性鎖(綁定 inode)。

三、fcntl 記錄鎖 API 速查

struct flock {
    short l_type;   // F_RDLCK, F_WRLCK, F_UNLCK
    short l_whence; // SEEK_SET, SEEK_CUR, SEEK_END
    off_t l_start;  // 起始偏移
    off_t l_len;    // 長度(0=到文件結尾)
    pid_t l_pid;    // 僅 F_GETLK 返回時填寫持有者 PID
};

fcntl(fd, F_SETLK, &flk);   // 非阻塞加鎖,失敗返回 -1/EAGAIN
fcntl(fd, F_SETLKW, &flk);  // 阻塞加鎖
fcntl(fd, F_GETLK, &flk);   // 檢測衝突鎖

lockf() 封裝 fcntl,僅支援建議性鎖、整文件/從當前位置到尾部,語法更簡潔。

四、啟用強制性鎖:兩步走

步驟 1:掛載文件系統加 mand 選項

mount -o remount,mand /dev/sda1 /data
# 或 /etc/fstab
/dev/sda1  /data  ext4  defaults,mand  0  0

注意:APUE 書中寫成 _omand 是誤植,正確是 mand。

步驟 2:目標文件設置權限位

chmod g+s, g-x  filename
# 即:set-group-ID 位 = 1,group execute 位 = 0
# 八進制:chmod 2644 filename (假設原 0644)
# ls -l 顯示:-rw-r-Sr--  (大寫 S 表示 setgid 但無組執行權)

原理:內核 VFS 在 __mnt_want_write() 路徑檢測到 (inode->i_mode & S_ISGID) && !(inode->i_mode & S_IXGRP) 且掛載點 MNT_MANDLOCK 標誌置位時,啟用強制性鎖檢查。

五、強制性鎖實測驗證

# 終端 1
$ cat > test_mand.c
#include 
#include 
int main() {
    int fd = open("data.txt", O_RDWR|O_CREAT, 0644);
    struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
    fcntl(fd, F_SETLKW, &fl);  // 加寫鎖
    write(fd, "locked", 6);
    sleep(30);                 // 持有 30 秒
    return 0;
}
$ gcc test_mand.c -o test_mand
$ chmod g+s,g-x data.txt
$ ./test_mand &

# 終端 2(鎖生效期間)
$ cat data.txt      # 將被阻塞 30 秒,直到終端 1 釋放鎖
$ echo "hack" > data.txt  # 同樣阻塞

若未啟用強制性鎖,終端 2 的 cat/echo 會無視建議性鎖直接讀寫成功。

六、總結

建議性鎖靠自覺、綁定 file、close() 即釋放,適合協作環境;
強制性鎖靠內核、綁定 inode、需 mount -o mand + chmod g+s,g-x,攔截所有 I/O,適合強隔離場景。
選型原則:能用建議性鎖解決就用建議性(簡單、標準、無額外依賴);必須防範非協作進程(如第三方二進制、容器逃逸、安全審計)時才啟用強制性鎖。

Copyright © Windows教程網 All Rights Reserved