如何获取文件描述符的模式?

How to get the mode of a file descriptor?

我的意思是使用fdopen

FILE *fdopen(int fd, const char *mode);

man pages中表示"The mode of the stream (one of the values "r", "r+", "w", "w+", "a", "a+") 必须与文件描述符的模式兼容。” 所以我必须首先知道 fd 的模式(我猜是 int)来为流选择合适的 const char *mode

我知道我应该使用 fcntl

int fcntl(int fd, int cmd);

到"manipulate file descriptor"(以下引用自this official source)。 它可以运行于:

File descriptor flags

The following commands manipulate the flags associated with a file descriptor.
...

File status flags

Each open file description has certain associated status flags, initialized by open(2)...

(我不知道两者之间的区别。 鉴于 fcntl 完全指的是文件描述符,我想第二个标题应该是 "File descriptor status flags",因此我们会有 "flags" 和 "status flags"... 让我感到困惑。我还没有看到这方面的任何规范)。我在这里顺便提一下,我正在就此提出一个具体问题。

从描述来看,我想我应该选择后者。 在这种情况下,当 cmd=F_GETFL 时,return 的值为 "the file access mode and the file status flags"。 "The file status flags and their semantics are described in open(2)".

现在我无法理解,在阅读了引用的资料后:

  1. fd

  2. 的所有可能模式(ints)是什么
  3. 因此,什么是所有组合 mode(fd) <-> mode(stream) "compatible".

我想应该可以将两个列表放在一起并用箭头连接。

相关:

Can I get the access mode of a `FILE*`?

(我问这个)

How to catch file mode?

I Wanna know the Internal Members of struct FILE, the latest ones

(我问这个)

https://www.gnu.org/software/libc/manual/html_node/Access-Modes.html

https://www.gnu.org/software/libc/manual/html_node/File-Status-Flags.html#File-Status-Flags


fopen 您可以检查它们与 w+ r 等的关联...

从此处和 中的答案和评论中学习后,我将代码放在一起。 从那里,我获得了有关文件描述符状态“words”的以下信息(我不想使用术语“flags”,请参阅下面的注释,取自 ) 和文件打开方式 modes.

*** Flag                       O_RDONLY =     0 =            0 = x0000
*** Flag                       O_WRONLY =     1 =            1 = x0001
*** Flag                         O_RDWR =     2 =           10 = x0002
*** Flag                        O_CREAT =    64 =      1000000 = x0040
*** Flag                        O_TRUNC =   512 =   1000000000 = x0200
*** Flag                       O_APPEND =  1024 =  10000000000 = x0400
*** Flag   O_WRONLY | O_CREAT | O_TRUNC =   577 =   1001000001 = x0241
*** Flag  O_WRONLY | O_CREAT | O_APPEND =  1089 =  10001000001 = x0441
*** Flag     O_RDWR | O_CREAT | O_TRUNC =   578 =   1001000010 = x0242
*** Flag    O_RDWR | O_CREAT | O_APPEND =  1090 =  10001000010 = x0442
*** Mode  r  F_GETFL -> 32768 = 1000000000000000 = x8000
*** Mode  w  F_GETFL -> 32769 = 1000000000000001 = x8001
*** Mode  a  F_GETFL -> 33793 = 1000010000000001 = x8401
*** Mode r+  F_GETFL -> 32770 = 1000000000000010 = x8002
*** Mode w+  F_GETFL -> 32770 = 1000000000000010 = x8002
*** Mode a+  F_GETFL -> 33794 = 1000010000000010 = x8402

三列中的数字是十进制、二进制和十六进制。 寻找 "strange" x8000,我在 fcntl-linux.h

中找到
# ifdef __USE_GNU
...
#  define AT_RECURSIVE      0x8000  /* Apply to the entire subtree.  */
...
# endif

所以,除了标志,出现在所有模式中,关联将是

r   <->  O_RDONLY
w   <->  O_WRONLY
a   <->  O_WRONLY | O_APPEND
r+  <->  O_RDWR
w+  <->  O_RDWR
a+  <->  O_RDWR | O_APPEND

现在这为我提供了一些有趣的发现:

  1. 列表与不一致。

  2. r+w+ 相同。 这给编码器带来了挑战,即当单词为 O_RDWR 时,使用哪种模式与 fdopenr+w+ 都可以)。 根据 this,我预计 w+ 也有 O_CREAT(如上面提到的 table)。 我也希望 w 拥有它。

  3. 要写完全portable的代码,貌似每次使用fdopen都要像我写的那样写代码自动寻找连接模式 <-> 单词。 (其实我做的部分工作是人工识别,还需要进一步的代码)

编辑: 根据评论对第 1 点和第 2 点的解释是 table 显示 fopen 模式和 open 标志之间的匹配,即在创建期间。 但是我用 fcntl 获得的是 after 创建的标志,而不是 during 创建时使用的标志。 正如 O_CREATO_TRUNC 所解释的,属于 文件创建标志 的类别,因此不是持久性的。 另一方面,O_APPEND 属于 文件状态标志 类别并且是持久的。 "The distinction between these two groups of flags is that the file creation flags affect the semantics of the open operation itself, while the file status flags affect the semantics of subsequent I/O operations."[ref]

注意man page for open(2)首先描述了文件访问模式,然后添加"In addition, zero or more file creation flags and file status flags can be bitwise-or'd in flags...."但是它(正确地)没有提到文件访问模式可以按位操作。对我来说,"flag" 这个词绝对是用词不当,而且具有误导性。


代码(任意函数to_binary获取二进制形式均可):

int main() {
    const char fname[100] = "test.txt";
    const char modes[][4] = { "r", "w", "a", "r+", "w+", "a+" };
    const size_t nmodes = sizeof(modes) / sizeof(modes[0]);
    const int flags[] = { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC, O_APPEND,
            O_WRONLY | O_CREAT | O_TRUNC,
            O_WRONLY | O_CREAT | O_APPEND,
            O_RDWR | O_CREAT | O_TRUNC,
            O_RDWR | O_CREAT | O_APPEND
    };
    const char flags_str[][100] = { "O_RDONLY", "O_WRONLY", "O_RDWR", "O_CREAT", "O_TRUNC", "O_APPEND",
            "O_WRONLY | O_CREAT | O_TRUNC",
            "O_WRONLY | O_CREAT | O_APPEND",
            "O_RDWR | O_CREAT | O_TRUNC",
            "O_RDWR | O_CREAT | O_APPEND"
    };
    const size_t nflags = sizeof(flags) / sizeof(flags[0]);
    for (size_t iflag = 0 ; iflag < nflags ; iflag++) {
        const int flag = flags[iflag];
        const char * flag_str = flags_str[iflag];
        char nbin[33];
        to_binary(flag, nbin);
        printf( "*** Flag %30s = %5d = %12s = x%04x\n", flag_str, flag, nbin, flag);
    }
    for (size_t imode = 0 ; imode < nmodes ; imode++) {
        const char * mode = modes[imode];
        FILE * fp1 = fopen(fname, mode);
        int fd1 = fileno(fp1);
        int retval = fcntl(fd1, F_GETFL);
        char nbin[33];
        to_binary(retval, nbin);
        printf( "*** Mode %2s  F_GETFL -> %5d = %12s = x%04x", mode, retval, nbin, retval);
        fclose(fp1);
    }
    return 0;
}