Linux fcntl 函数全解

本文详细介绍了Linux系统中的fcntl函数,包括cmd的可选值如F_DUPFD、F_GETFD、F_SETFL等,以及文件锁F_SETLK、F_SETLKW、F_GETLK的使用。内容涵盖了文件描述符复制、文件状态标志的修改和文件锁的管理,是理解Linux系统编程的重要参考资料。

一、函数声明

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

int fcntl(int fd, int cmd, ... /* arg */ );
//当函数执行失败时返回-1
1.1 cmd可选值
  1. Duplicating a file descriptor:(复制文件描述符)
    • F_DUPFD
    • F_DUPFD_CLOEXEC
  2. File descriptor flags:(文件描述符标志)
    • F_GETFD
    • F_SETFD
  3. File status flags(文件状态标志)
    • F_GETFL
    • F_SETFL

可以通过F_SETTFL修改的只有O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, O_NONBLOCK、O_DSYNC 、O_SYNC最后这两个可能会修改失败(从https://man7.org/linux/man-pages/man2/fcntl.2.html中得知,具体原因暂未了解)。

注意:

1.文件状态标志并不包括O_CREAT(还有其他三个在下面列出来)。这只是open,打开不存在的文件时,告诉open应该创建该文件。(O_CREAT是为了避免两次系统调用,open和creat非原子性造成的一些问题,而产生)

O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC

2.而且并不是所有的文件状态标志都可以改变,例如

O_RDONLY, O_WRONLY, O_RDWR这三个就不可以修改

(参考自 https://man7.org/linux/man-pages/man2/fcntl.2.html )
Set the file status flags to the value specified by arg. File
access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation
flags (i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are
ignored. On Linux, this command can change only the O_APPEND,
O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags. It is not
possible to change the O_DSYNC and O_SYNC flags; see BUGS,
below
.

  1. Advisory record locking(文件锁相关)

    • F_SETLK
    • F_SETLKW
    • F_GETLK
  2. Managing signals (信号管理)

    • F_GETOWN
    • F_SETOWN

F_GETOWN, F_SETOWN, F_GETOWN_EX, F_SETOWN_EX, F_GETSIG and F_SETSIG
are used to manage I/O availability signals:

二、实例

2.1 F_DUPFD / F_DUP_CLOEXEC(Duplicating a file descriptor)
fcntl(fd, F_DUPFD, newfd);

这里只简单演示使用,F_DUPFD和F_DUP_CLOEXEC的区别在于新产生的文件描述符,是否有CLOEXEC描述符标志。(关于CLOEXEC的作用,可以看我的这篇文章Linux fcntl中FD_CLOEXEC 作用

2.2 F_GETFD / F_SETFD(File descriptor flags)
...
int old_fd_flags, new_fd_flags;
old_fd_flags = fcntl(fd, F_GETFD);
new_fd_flags = FD_CLOEXEC; // 启用(如果要关闭CLOEXEC则取反即可 !FD_CLOEXEC)
fcntl(fd, F_SETFD, new_fd);

这里因为文件描述符标志,只有CLOEXEC这一种,就不再写实例。

2.3 F_GETFT / F_SETFT (File status flags)
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **args)
{ 
    int fl_init = O_RDWR|O_CREAT|O_APPEND;
    int fd = open("test", fl_init, S_IRWXU);
    int fl_get = 0;
    fl_get = fcntl(fd, F_GETFL);
    
    // o_creat is not in the fl.
    assert((fl_get & O_ACCMODE) == O_RDWR);
    assert(fl_get&O_APPEND);

    if (write(fd, "xxxxxkk", strlen("xxxxxkk")) < 0) {
        perror("write xxxxxkk to file with fail");
        exit(-1);
    }

    // try to move the file cur to the start of the file
    lseek(fd, 0, SEEK_SET);

    if (write(fd, "test1", strlen("test1")) < 0) {
        perror("write test1 to file with fail");
        exit(-1);
    }

    // cancel the O_APPEND fl for the file
    if (fcntl(fd, F_SETFL, fl_get & !O_APPEND) < 0) {
        perror("fcntl F_SETFL with fail");
        exit(-1);
    }

    lseek(fd, 0, SEEK_SET);

    if (write(fd, "test2", strlen("test2")) < 0) {
        perror("write test2 to file with fail");
        exit(-1);
    }

    close(fd);
    return 0;
}

程序运行后,test文件的内容为

test2kktest1

流程:

  • 首先输出:xxxxxkk
  • 然后因为lseek失败,继续从尾部增加,kk后面接着输出test1。
  • 然后改变文件状态标志,去掉了O_APPEND,随后的lseek成功。
  • 从文件开头写入test2

可以看出,当设置了O_APPEND的时候,通过lseek去改变当前文件操作偏移量是无效的。所以,test1还是输出在了kk后面。而通过fcntl取消O_APPEND之后lseek设置成功。test2输出在了文件头。

2.4 F_SETLK / F_SETLKW / F_GETLK (Advisory record locking)

该部分内容非常重要。与文件加锁有关。

struct flock {
     ...
     short l_type;    /* Type of lock: F_RDLCK,
                         F_WRLCK, F_UNLCK */
     short l_whence;  /* How to interpret l_start:
                         SEEK_SET, SEEK_CUR, SEEK_END */
     off_t l_start;   /* Starting offset for lock */
     off_t l_len;     /* Number of bytes to lock */
     pid_t l_pid;     /* PID of process blocking our lock
                         (set by F_GETLK and F_OFD_GETLK) */
     ...
 };

这里是从ubuntu 20.4版本的man fcntl中得到的。

// fcntl-linux.h 文件中的节选
/* For old implementation of BSD flock.  */
#ifndef F_EXLCK
# define F_EXLCK		4	/* or 3 */
# define F_SHLCK		8	/* or 4 */
#endif

从fcntl-linux.h头文件中可以看到l_type除了以上的三种,还有F_SHLCK(共享锁)和F_EXLCK(排他锁)
这里也说了,这两个宏定义只是为了兼容BSD的老的实现。所以,其实F_EXLCK(排他锁)就是F_WRLCK(写锁),F_SHLCK(共享锁)就是F_RDLCK(读锁)。

2.4.1 更多关于文件锁的内容可以查看我的另一篇博客

Linux fcntl与文件锁

TODO:关于文件信号管理的内容未补充

2.5 F_GETOWN / F_SETOWN

// TODO 这里内容未补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值