c - F_GETFL 和 F_SETFL 的用法
c - Usage of F_GETFL and F_SETFL
在尝试将 fcntl()
与命令 F_GETFL
和 F_SETFL
一起使用时,我遇到了一些问题:
为什么 fcntl(fd, F_GETFL)
返回的标志只包括我在打开文件时设置的位的子集?它只显示可修改的吗?
使用fcntl(fd, F_SETFL, flag)
时,flag参数应该如何传递,是否需要先通过fcntl(fd, F_GETFL)
读取flag,然后修改传递?或者在内部它只是用新参数做一点 &
操作?
在哪里可以找到 32 位(或更少)打开文件标志的完整列表?
代码 - [dup_fd_share.c]:
// prove duplicated fd shared file offset and open file status,
// TLPI exercise 5.5
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 100
void fd_share() {
char *fp = "/tmp/fd_share.txt";
char *buf = "abc\n";
int write_size = 4;
int fd, fd2;
off_t cur, cur2;
int open_flag, open_flag2;
// open
int flag = O_RDWR | O_CREAT | O_TRUNC | O_APPEND;
printf("file flag param: %x\n", flag);
fd = open(fp, flag, 0644);
// dup
fd2 = dup(fd);
// initial offset
cur = lseek(fd, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd, cur);
cur2= lseek(fd2, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd2, cur2);
// write, offset change,
write(fd, buf, 4);
printf("write %d chars\n", write_size);
// new offset
cur = lseek(fd, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd, cur);
cur2= lseek(fd2, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd2, cur2);
// get original open file flag,
open_flag = fcntl(fd, F_GETFL);
printf("fd[%d] open flag: %x\n", fd, open_flag);
open_flag2 = fcntl(fd2, F_GETFL);
printf("fd[%d] open flag: %x\n", fd2, open_flag2);
// change open file flag,
open_flag &= ~O_APPEND;
if(fcntl(fd, F_SETFL, open_flag) == -1) {
printf("failed to set flag\n");
return;
}
printf("change open file flag, remove %s\n", "O_APPEND");
open_flag = fcntl(fd, F_GETFL);
printf("fd[%d] open flag: %x\n", fd, open_flag);
open_flag2 = fcntl(fd2, F_GETFL);
printf("fd[%d] open flag: %x\n", fd2, open_flag2);
close(fd);
}
int main(int argc, char *argv[]) {
fd_share();
return 0;
}
输出:
file flag param: 642
fd[3] offset: 0
fd[4] offset: 0
write 4 chars
fd[3] offset: 4
fd[4] offset: 4
fd[3] open flag: 402
fd[4] open flag: 402
change open file flag, remove O_APPEND
fd[3] open flag: 2
fd[4] open flag: 2
1) fcnl的return是描述函数是否成功以及如何成功的代码:
RETURN 值
For a successful call, the return value depends on the operation:
F_DUPFD The new descriptor.
F_GETFD Value of file descriptor flags.
F_GETFL Value of file status flags.
F_GETLEASE
Type of lease held on file descriptor.
F_GETOWN Value of descriptor owner.
F_GETSIG Value of signal sent when read or write becomes possible, or
zero for traditional SIGIO behavior.
F_GETPIPE_SZ, F_SETPIPE_SZ
The pipe capacity.
F_GET_SEALS
A bit mask identifying the seals that have been set for the
inode referred to by fd.
All other commands
Zero.
On error, -1 is returned, and errno is set appropriately.
错误
EACCES or EAGAIN
Operation is prohibited by locks held by other processes.
EAGAIN The operation is prohibited because the file has been memory-
mapped by another process.
EBADF fd is not an open file descriptor
EBADF cmd is F_SETLK or F_SETLKW and the file descriptor open mode
doesn't match with the type of lock requested.
EBUSY cmd is F_SETPIPE_SZ and the new pipe capacity specified in arg
is smaller than the amount of buffer space currently used to
store data in the pipe.
EBUSY cmd is F_ADD_SEALS, arg includes F_SEAL_WRITE, and there
exists a writable, shared mapping on the file referred to by
fd.
EDEADLK
It was detected that the specified F_SETLKW command would
cause a deadlock.
EFAULT lock is outside your accessible address space.
EINTR cmd is F_SETLKW or F_OFD_SETLKW and the operation was
interrupted by a signal; see signal(7).
EINTR cmd is F_GETLK, F_SETLK, F_OFD_GETLK, or F_OFD_SETLK, and the
operation was interrupted by a signal before the lock was
checked or acquired. Most likely when locking a remote file
(e.g., locking over NFS), but can sometimes happen locally.
EINVAL The value specified in cmd is not recognized by this kernel.
EINVAL cmd is F_ADD_SEALS and arg includes an unrecognized sealing
bit.
EINVAL cmd is F_ADD_SEALS or F_GET_SEALS and the filesystem
containing the inode referred to by fd does not support
sealing.
EINVAL cmd is F_DUPFD and arg is negative or is greater than the
maximum allowable value (see the discussion of RLIMIT_NOFILE
in getrlimit(2)).
EINVAL cmd is F_SETSIG and arg is not an allowable signal number.
EINVAL cmd is F_OFD_SETLK, F_OFD_SETLKW, or F_OFD_GETLK, and l_pid
was not specified as zero.
EMFILE cmd is F_DUPFD and the process already has the maximum number
of file descriptors open.
ENOLCK Too many segment locks open, lock table is full, or a remote
locking protocol failed (e.g., locking over NFS).
ENOTDIR
F_NOTIFY was specified in cmd, but fd does not refer to a
directory.
EPERM Attempted to clear the O_APPEND flag on a file that has the
append-only attribute set.
EPERM cmd was F_ADD_SEALS, but fd was not open for writing or the
current set of seals on the file already includes F_SEAL_SEAL.
2)要设置的标志由您选择::
F_SETFL (整数)
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.
3) HERE 你有一个完整的描述。
你问过:
Why the flag returned by fcntl(fd, F_GETFL) only include a subset of bits of what I set when open file? Does it only show the ones that are modifiable?
没有;它只显示系统"remembered"的那些,例如O_RDWR
。这些真的可以称为"flags"。 oflag
参数中的一些其他位更像是 open
系统调用的 "imperative instructions":例如,O_CREAT
表示 "please create this file if it doesn't exist" 和 O_TRUNC
表示 "please truncate it",两者都不是 "flags"。创建时被截断的文件与创建时 未 截断的文件无法区分:它们都只是 "files"。因此,在 open
完成创建或截断文件后,它不会理会 "remember" 该历史记录。它只 "remembers" 重要的事情,例如文件是否打开以供读取或写入。
编辑添加: 这些不同种类的旗帜具有半官方名称。 O_RDWR
是 "access mode" (记住,不可修改); O_APPEND
是一个 "operating mode" (记住,通常可以修改); O_TRUNC
是一个 "open-time flag"(属于 open
操作本身,不属于文件描述符;因此不记得了)。请注意 "access mode" 是不可修改的 — 您不能使用 fcntl
将只读 fd 转换为只写 fd。
When use fcntl(fd, F_SETFL, flag)
, how should I pass the flag param, do I need to read flag via fcntl(fd, F_GETFL)
first, then modify it and pass it? Or internally it just do a bit &
operation with the new param?
F_SETFL
用 完全覆盖标志 你传入的内容(尽管它会忽略你设置位的微不足道的尝试 - 那不是 - 真正的标志,例如 O_TRUNC
)。如果你想设置一个特定的标志并保持其他标志不变,那么你必须 F_GETFL
旧标志,|
新标志,然后 F_SETFL
结果。 必须作为两个独立的系统调用完成;据我所知,没有原子或线程安全的方法来完成它。
Where can I find a full list of the 32 (or less) bits of open file flags?
在 fcntl.h
或其文档 (man fcntl
) 中。例如,在我的 MacBook 上,手册页显示:
The flags for the F_GETFL and F_SETFL commands are as follows:
O_NONBLOCK Non-blocking I/O; if no data is available to a read call, or if a write operation would block, the read or write
call returns -1 with the error EAGAIN.
O_APPEND Force each write to append at the end of file; corresponds to the O_APPEND flag of open(2).
O_ASYNC Enable the SIGIO signal to be sent to the process group when I/O is possible, e.g., upon availability of data to be
read.
换句话说,您可以在 OS X 上设置(或取消设置)三个位。而在 Linux 上,手册页显示 this:
On Linux this command can change only the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
顺便说一下,一些 Linux 文件系统在文件系统级别有 "append-only file" 的概念;如果您打开其中一个文件,然后尝试清除生成的描述符的 O_APPEND
标志,您将收到 EPERM
错误。可以使用 chattr
实用程序在文件系统级别控制文件的 "append-only"-ness。
这是您的测试程序的更系统的版本。你可能不感兴趣,但我通过写它学到了一些东西,所以我把它留在这里。 :)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("/tmp/fd_share.txt", O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0644);
// append to empty file
write(fd, "aaaaaaaaaa", 10);
off_t cur = lseek(fd, 1, SEEK_SET);
printf("offset after being set to 1: %ld\n", (long)cur);
// append
write(fd, "bbbbbbbb", 8);
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after appending bbbbbbbb: %ld\n", (long)cur);
cur = lseek(fd, 2, SEEK_SET);
printf("offset after being set to 2: %ld\n", (long)cur);
// now toggle "append mode" to FALSE
int open_flag = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, open_flag & ~O_APPEND) == -1) {
printf("failed to set flag\n");
return 0;
}
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after unsetting O_APPEND: %ld\n", (long)cur);
cur = lseek(fd, 3, SEEK_SET);
printf("offset after being set to 3: %ld\n", (long)cur);
// write without appending
write(fd, "cccc", 4);
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after writing cccc: %ld\n", (long)cur);
// now toggle "append mode" to TRUE
open_flag = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, open_flag | O_APPEND) == -1) {
printf("failed to set flag\n");
return 0;
}
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after unsetting O_APPEND: %ld\n", (long)cur);
// append
write(fd, "dd", 2);
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after appending dd: %ld\n", (long)cur);
close(fd);
}
这个程序在我的 MacBook 上的输出(因为它应该在任何 POSIX 系统 AFAIK 上)是:
offset after being set to 1: 1
offset after appending bbbbbbbb: 18
offset after being set to 2: 2
offset after unsetting O_APPEND: 2
offset after being set to 3: 3
offset after writing cccc: 7
offset after unsetting O_APPEND: 7
offset after appending dd: 20
在尝试将 fcntl()
与命令 F_GETFL
和 F_SETFL
一起使用时,我遇到了一些问题:
为什么
fcntl(fd, F_GETFL)
返回的标志只包括我在打开文件时设置的位的子集?它只显示可修改的吗?使用
fcntl(fd, F_SETFL, flag)
时,flag参数应该如何传递,是否需要先通过fcntl(fd, F_GETFL)
读取flag,然后修改传递?或者在内部它只是用新参数做一点&
操作?在哪里可以找到 32 位(或更少)打开文件标志的完整列表?
代码 - [dup_fd_share.c]:
// prove duplicated fd shared file offset and open file status,
// TLPI exercise 5.5
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define BUF_SIZE 100
void fd_share() {
char *fp = "/tmp/fd_share.txt";
char *buf = "abc\n";
int write_size = 4;
int fd, fd2;
off_t cur, cur2;
int open_flag, open_flag2;
// open
int flag = O_RDWR | O_CREAT | O_TRUNC | O_APPEND;
printf("file flag param: %x\n", flag);
fd = open(fp, flag, 0644);
// dup
fd2 = dup(fd);
// initial offset
cur = lseek(fd, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd, cur);
cur2= lseek(fd2, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd2, cur2);
// write, offset change,
write(fd, buf, 4);
printf("write %d chars\n", write_size);
// new offset
cur = lseek(fd, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd, cur);
cur2= lseek(fd2, 0, SEEK_CUR);
printf("fd[%d] offset: %ld\n", fd2, cur2);
// get original open file flag,
open_flag = fcntl(fd, F_GETFL);
printf("fd[%d] open flag: %x\n", fd, open_flag);
open_flag2 = fcntl(fd2, F_GETFL);
printf("fd[%d] open flag: %x\n", fd2, open_flag2);
// change open file flag,
open_flag &= ~O_APPEND;
if(fcntl(fd, F_SETFL, open_flag) == -1) {
printf("failed to set flag\n");
return;
}
printf("change open file flag, remove %s\n", "O_APPEND");
open_flag = fcntl(fd, F_GETFL);
printf("fd[%d] open flag: %x\n", fd, open_flag);
open_flag2 = fcntl(fd2, F_GETFL);
printf("fd[%d] open flag: %x\n", fd2, open_flag2);
close(fd);
}
int main(int argc, char *argv[]) {
fd_share();
return 0;
}
输出:
file flag param: 642
fd[3] offset: 0
fd[4] offset: 0
write 4 chars
fd[3] offset: 4
fd[4] offset: 4
fd[3] open flag: 402
fd[4] open flag: 402
change open file flag, remove O_APPEND
fd[3] open flag: 2
fd[4] open flag: 2
1) fcnl的return是描述函数是否成功以及如何成功的代码:
RETURN 值
For a successful call, the return value depends on the operation:
F_DUPFD The new descriptor.
F_GETFD Value of file descriptor flags.
F_GETFL Value of file status flags.
F_GETLEASE
Type of lease held on file descriptor.
F_GETOWN Value of descriptor owner.
F_GETSIG Value of signal sent when read or write becomes possible, or
zero for traditional SIGIO behavior.
F_GETPIPE_SZ, F_SETPIPE_SZ
The pipe capacity.
F_GET_SEALS
A bit mask identifying the seals that have been set for the
inode referred to by fd.
All other commands
Zero.
On error, -1 is returned, and errno is set appropriately.
错误
EACCES or EAGAIN
Operation is prohibited by locks held by other processes.
EAGAIN The operation is prohibited because the file has been memory-
mapped by another process.
EBADF fd is not an open file descriptor
EBADF cmd is F_SETLK or F_SETLKW and the file descriptor open mode
doesn't match with the type of lock requested.
EBUSY cmd is F_SETPIPE_SZ and the new pipe capacity specified in arg
is smaller than the amount of buffer space currently used to
store data in the pipe.
EBUSY cmd is F_ADD_SEALS, arg includes F_SEAL_WRITE, and there
exists a writable, shared mapping on the file referred to by
fd.
EDEADLK
It was detected that the specified F_SETLKW command would
cause a deadlock.
EFAULT lock is outside your accessible address space.
EINTR cmd is F_SETLKW or F_OFD_SETLKW and the operation was
interrupted by a signal; see signal(7).
EINTR cmd is F_GETLK, F_SETLK, F_OFD_GETLK, or F_OFD_SETLK, and the
operation was interrupted by a signal before the lock was
checked or acquired. Most likely when locking a remote file
(e.g., locking over NFS), but can sometimes happen locally.
EINVAL The value specified in cmd is not recognized by this kernel.
EINVAL cmd is F_ADD_SEALS and arg includes an unrecognized sealing
bit.
EINVAL cmd is F_ADD_SEALS or F_GET_SEALS and the filesystem
containing the inode referred to by fd does not support
sealing.
EINVAL cmd is F_DUPFD and arg is negative or is greater than the
maximum allowable value (see the discussion of RLIMIT_NOFILE
in getrlimit(2)).
EINVAL cmd is F_SETSIG and arg is not an allowable signal number.
EINVAL cmd is F_OFD_SETLK, F_OFD_SETLKW, or F_OFD_GETLK, and l_pid
was not specified as zero.
EMFILE cmd is F_DUPFD and the process already has the maximum number
of file descriptors open.
ENOLCK Too many segment locks open, lock table is full, or a remote
locking protocol failed (e.g., locking over NFS).
ENOTDIR
F_NOTIFY was specified in cmd, but fd does not refer to a
directory.
EPERM Attempted to clear the O_APPEND flag on a file that has the
append-only attribute set.
EPERM cmd was F_ADD_SEALS, but fd was not open for writing or the
current set of seals on the file already includes F_SEAL_SEAL.
2)要设置的标志由您选择::
F_SETFL (整数)
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.
3) HERE 你有一个完整的描述。
你问过:
Why the flag returned by fcntl(fd, F_GETFL) only include a subset of bits of what I set when open file? Does it only show the ones that are modifiable?
没有;它只显示系统"remembered"的那些,例如O_RDWR
。这些真的可以称为"flags"。 oflag
参数中的一些其他位更像是 open
系统调用的 "imperative instructions":例如,O_CREAT
表示 "please create this file if it doesn't exist" 和 O_TRUNC
表示 "please truncate it",两者都不是 "flags"。创建时被截断的文件与创建时 未 截断的文件无法区分:它们都只是 "files"。因此,在 open
完成创建或截断文件后,它不会理会 "remember" 该历史记录。它只 "remembers" 重要的事情,例如文件是否打开以供读取或写入。
编辑添加: 这些不同种类的旗帜具有半官方名称。 O_RDWR
是 "access mode" (记住,不可修改); O_APPEND
是一个 "operating mode" (记住,通常可以修改); O_TRUNC
是一个 "open-time flag"(属于 open
操作本身,不属于文件描述符;因此不记得了)。请注意 "access mode" 是不可修改的 — 您不能使用 fcntl
将只读 fd 转换为只写 fd。
When use
fcntl(fd, F_SETFL, flag)
, how should I pass the flag param, do I need to read flag viafcntl(fd, F_GETFL)
first, then modify it and pass it? Or internally it just do a bit&
operation with the new param?
F_SETFL
用 完全覆盖标志 你传入的内容(尽管它会忽略你设置位的微不足道的尝试 - 那不是 - 真正的标志,例如 O_TRUNC
)。如果你想设置一个特定的标志并保持其他标志不变,那么你必须 F_GETFL
旧标志,|
新标志,然后 F_SETFL
结果。 必须作为两个独立的系统调用完成;据我所知,没有原子或线程安全的方法来完成它。
Where can I find a full list of the 32 (or less) bits of open file flags?
在 fcntl.h
或其文档 (man fcntl
) 中。例如,在我的 MacBook 上,手册页显示:
The flags for the F_GETFL and F_SETFL commands are as follows:
O_NONBLOCK Non-blocking I/O; if no data is available to a read call, or if a write operation would block, the read or write
call returns -1 with the error EAGAIN.
O_APPEND Force each write to append at the end of file; corresponds to the O_APPEND flag of open(2).
O_ASYNC Enable the SIGIO signal to be sent to the process group when I/O is possible, e.g., upon availability of data to be
read.
换句话说,您可以在 OS X 上设置(或取消设置)三个位。而在 Linux 上,手册页显示 this:
On Linux this command can change only the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
顺便说一下,一些 Linux 文件系统在文件系统级别有 "append-only file" 的概念;如果您打开其中一个文件,然后尝试清除生成的描述符的 O_APPEND
标志,您将收到 EPERM
错误。可以使用 chattr
实用程序在文件系统级别控制文件的 "append-only"-ness。
这是您的测试程序的更系统的版本。你可能不感兴趣,但我通过写它学到了一些东西,所以我把它留在这里。 :)
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("/tmp/fd_share.txt", O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0644);
// append to empty file
write(fd, "aaaaaaaaaa", 10);
off_t cur = lseek(fd, 1, SEEK_SET);
printf("offset after being set to 1: %ld\n", (long)cur);
// append
write(fd, "bbbbbbbb", 8);
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after appending bbbbbbbb: %ld\n", (long)cur);
cur = lseek(fd, 2, SEEK_SET);
printf("offset after being set to 2: %ld\n", (long)cur);
// now toggle "append mode" to FALSE
int open_flag = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, open_flag & ~O_APPEND) == -1) {
printf("failed to set flag\n");
return 0;
}
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after unsetting O_APPEND: %ld\n", (long)cur);
cur = lseek(fd, 3, SEEK_SET);
printf("offset after being set to 3: %ld\n", (long)cur);
// write without appending
write(fd, "cccc", 4);
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after writing cccc: %ld\n", (long)cur);
// now toggle "append mode" to TRUE
open_flag = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, open_flag | O_APPEND) == -1) {
printf("failed to set flag\n");
return 0;
}
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after unsetting O_APPEND: %ld\n", (long)cur);
// append
write(fd, "dd", 2);
cur = lseek(fd, 0, SEEK_CUR);
printf("offset after appending dd: %ld\n", (long)cur);
close(fd);
}
这个程序在我的 MacBook 上的输出(因为它应该在任何 POSIX 系统 AFAIK 上)是:
offset after being set to 1: 1
offset after appending bbbbbbbb: 18
offset after being set to 2: 2
offset after unsetting O_APPEND: 2
offset after being set to 3: 3
offset after writing cccc: 7
offset after unsetting O_APPEND: 7
offset after appending dd: 20