文章目录
一、函数声明
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
//当函数执行失败时返回-1
1.1 cmd可选值
- Duplicating a file descriptor:(复制文件描述符)
- F_DUPFD
- F_DUPFD_CLOEXEC
- File descriptor flags:(文件描述符标志)
- F_GETFD
- F_SETFD
- 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.
-
Advisory record locking(文件锁相关)
- F_SETLK
- F_SETLKW
- F_GETLK
-
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 更多关于文件锁的内容可以查看我的另一篇博客
TODO:关于文件信号管理的内容未补充
2.5 F_GETOWN / F_SETOWN
// TODO 这里内容未补充
本文详细介绍了Linux系统中的fcntl函数,包括cmd的可选值如F_DUPFD、F_GETFD、F_SETFL等,以及文件锁F_SETLK、F_SETLKW、F_GETLK的使用。内容涵盖了文件描述符复制、文件状态标志的修改和文件锁的管理,是理解Linux系统编程的重要参考资料。
2万+

被折叠的 条评论
为什么被折叠?



