c - 使用F_GETFL和F_SETFL

时间:2015-05-07 06:15:51

标签: c file-io fcntl

在尝试将fcntl()与命令F_GETFLF_SETFL一起使用时,我遇到了一些问题:

  1. 为什么fcntl(fd, F_GETFL)返回的标志只包含我在打开文件时设置的位的子集?它只显示可修改的那些吗?

  2. 当使用fcntl(fd, F_SETFL, flag)时,我应该如何传递标志参数,我是否需要首先通过fcntl(fd, F_GETFL)读取标志,然后修改它并传递它?或者在内部它只是用新的参数进行&操作?

  3. 在哪里可以找到32位(或更少)位的打开文件标志的完整列表?

  4. 代码 - [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
    

2 个答案:

答案 0 :(得分:4)

1)fcnl的返回是一个代码,描述了该函数是否已成功以及如何:

返回值

   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(int)

   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您有完整的说明。

答案 1 :(得分:4)

你问:

  

为什么fcntl(fd,F_GETFL)返回的标志只包含我在打开文件时设置的位的子集?它只显示可修改的那些吗?

没有;它只显示了记住的那些&#34;由系统,例如O_RDWR。这些可以真正称为&#34; flags&#34;。 OR oflag参数中的一些其他位更像是&#34;命令性指令&#34;到open系统调用:例如,O_CREAT说&#34;如果文件不存在,请创建此文件&#34;并且O_TRUNC说&#34;请截断它&#34;,它们都不是&#34;标志&#34;。在创建时被截断的文件与在创建时被截断的文件无法区分:它们只是&#34;文件&#34;。因此,在open完成创建或截断文件后,它不会打扰&#34;记住&#34;那段历史。它只记得&#34;重要的事情,比如文件是否可以阅读或写作。

编辑添加:这些不同类型的旗帜有半官方名称。 O_RDWR是&#34;访问模式&#34; (记住,不可修改); O_APPEND是一种&#34;运营模式&#34; (记住,通常可以修改); O_TRUNC是一个&#34;开放时间标志&#34; (属于open操作本身,而不属于文件描述符;因此不记得)。请注意&#34;访问模式&#34;不可修改 - 您无法使用fcntl将只读fd转换为只写fd。

  

当使用fcntl(fd, F_SETFL, flag)时,我应该如何传递标志参数,我是否需要首先通过fcntl(fd, F_GETFL)读取标志,然后修改它并传递它?或者在内部它只是用新的参数进行&操作?

F_SETFL完全覆盖你所传入的内容(尽管它会忽略你设置的那些微不足道的真正标志,例如O_TRUNC)。如果要设置特定标志并保持其他标志不变,则必须F_GETFL旧标志,|新标志,然后F_SETFL结果。此必须作为两个单独的系统调用完成;据我所知,没有原子或线程安全的方法来实现它。

  

在哪里可以找到32位(或更少)位的打开文件标志的完整列表?

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文件系统具有&#34;仅附加文件的概念&#34;在文件系统级别;如果您打开其中一个文件,然后尝试清除生成的描述符的O_APPEND标记,则会出现EPERM错误。可以使用chattr实用程序在文件系统级别控制文件的&#34;仅附加&#34; -

这是您的测试程序的更系统化版本。你可能不感兴趣,但我通过写它来学到了东西,所以我把它留在这里。 :)

#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