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

Linux 系統編程:fcntl 函數族完整詳解(文件描述符標志、狀態標志、異步 I/O、記錄鎖)

     編輯:關於C語言

fcntl 是 Unix/Linux 系統編程中「瑞士軍刀」般的系統調用,通過 cmd 參數控制文件描述符屬性、文件狀態標志、異步 I/O 所有權、記錄鎖等五大類功能。本文系統梳理 F_DUPFD、F_GETFL/F_SETFL、F_GETOWN/F_SETOWN、F_GETLK/F_SETLK/F_SETLKW 等核心命令,附帶代碼實例與避坑指南。

函數原型

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);

五大功能類別速查表

類別命令用途第三參數
描述符複製F_DUPFD複製 fd,返回 ≥ arg 的最小可用描述符long arg (起始值)
F_DUPFD_CLOEXEC同上,並設置 FD_CLOEXEClong arg
描述符標志F_GETFD獲取 FD_CLOEXEC 標志無
F_SETFD設置 FD_CLOEXEClong arg (0/1)
文件狀態標志F_GETFL獲取 O_RDONLY、O_APPEND、O_NONBLOCK 等無
F_SETFL設置 O_APPEND、O_NONBLOCK、O_ASYNC 等long arg (標志位或)
—不可改 O_RDONLY/W/RDWR(open 時確定)—
異步 I/O 所有權F_GETOWN獲取接收 SIGIO/SIGURG 的進程/組 ID無
F_SETOWN設置接收進程/組 ID(負值=組)long arg (pid 或 -pgid)
記錄鎖F_GETLK測試是否有鎖阻塞,結果覆蓋 flock 結構struct flock *
F_SETLK設置/釋放鎖(非阻塞),失敗返回 EAGAINstruct flock *
F_SETLKW設置/釋放鎖(阻塞等待)struct flock *

關鍵結構體 struct flock

struct flock {
    short l_type;   // F_RDLCK(讀鎖), F_WRLCK(寫鎖), F_UNLCK(解鎖)
    short l_whence; // SEEK_SET/SEEK_CUR/SEEK_END
    off_t l_start;  // 相對 l_whence 的起始偏移
    off_t l_len;    // 鎖定字節數,0=到文件末尾
    pid_t l_pid;    // 持有鎖的進程 PID (F_GETLK 返回)
};

實戰範例:非阻塞讀寫 + 記錄鎖保護關鍵區

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int set_nonblock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int lock_region(int fd, short type, off_t start, off_t len, bool wait) {
    struct flock fl = {
        .l_type = type,
        .l_whence = SEEK_SET,
        .l_start = start,
        .l_len = len
    };
    int cmd = wait ? F_SETLKW : F_SETLK;
    return fcntl(fd, cmd, &fl);
}

int main(void) {
    int fd = open("data.db", O_RDWR | O_CREAT, 0644);
    if (fd < 0) { perror("open"); return 1; }

    // 1. 設置非阻塞
    if (set_nonblock(fd) < 0) { perror("nonblock"); return 1; }

    // 2. 獲取寫鎖保護 0-100 字節
    if (lock_region(fd, F_WRLCK, 0, 100, true) < 0) {
        perror("lock");
        return 1;
    }
    printf("獲得寫鎖,進行關鍵寫入...\n");
    write(fd, "hello", 5);

    // 3. 釋放鎖
    lock_region(fd, F_UNLCK, 0, 100, false);
    close(fd);
    return 0;
}

建議性鎖 vs 強制性鎖

  • 建議性鎖:默認。依賴進程協作遵守,內核**不強制**攔截違規讀寫。fcntl、flock、lockf 均屬此類。
  • 強制性鎖:掛載文件系統時加 mand 選項,且文件屬組執行位關閉、Set-GID 位開啟。內核**強制**檢查每次讀寫,違規返回 EAGAIN。非 POSIX 標準,移植性差。

避坑清單

  1. 先獲取再修改:flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK); —— 直接 F_SETFL 會清除原有標志。
  2. 鎖與文件生命周期:進程關閉文件描述符時,**所有**該進程持有的建議性鎖自動釋放(即使同一文件其他描述符仍開啟)。
  3. NFS 陷阱:早期 NFS 不支持記錄鎖,現代 NFSv4 支持但需確保客戶端/服務端鎖管理器運行。
  4. 信號中斷:F_SETLKW 等待期間若捕獲信號且未設置 SA_RESTART,會返回 EINTR 需重試。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved