2015-05-07 3 views
3

При попытке использовать fcntl() с командой F_GETFL и F_SETFL, у меня несколько вопросов:с - Использование F_GETFL и F_SETFL

  1. Почему флаг возвращаемый fcntl(fd, F_GETFL) включает только подмножество бит, что я установил, когда открыть файл? Он показывает только те, которые могут быть изменены?

  2. При использовании fcntl(fd, F_SETFL, flag), как мне передать флаг param, мне нужно сначала прочитать флаг через fcntl(fd, F_GETFL), а затем изменить его и передать? Или внутренне это просто делает операцию & с новым параметром?

  3. Где я могу найти полный список битов открытого файла 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 
+0

'' O_CREAT' и O_TRUNC' модификаторы, которые контролируют поведение сам 'открытый'; они не являются режимами, которые применяются к открытому файлу. –

ответ

1

Вы спросили:

Почему флаг возвращаемого FCNTL (Fd, F_GETFL) включает только подмножество бит, что я установил, когда открытый файл? Он показывает только те, которые могут быть изменены?

Нет; он отображает только те, которые «запоминаются» системой, например O_RDWR. Их действительно можно назвать «флагами». Некоторые из других бит ORed в параметр oflag более похожи на «императивные инструкции» на системный вызов open: например, O_CREAT говорит: «Пожалуйста, создайте этот файл, если он не существует», а O_TRUNC говорит «пожалуйста, укоротите его», ни из которых «флаги». Файл, который был усечен при создании, неотличим от файла, который был не усечен при создании: они оба являются просто «файлами». Поэтому после того, как open будет создан или обрезается файл, он не удосужится «запомнить» эту историю. Он только «запоминает» важные вещи, например, открыт ли файл для чтения или записи.

Отредактировано для добавления: Эти различные виды флагов имеют полуофициальные названия. O_RDWR - это «режим доступа» (запоминающийся, не изменяемый); O_APPEND - это «рабочий режим» (запоминающийся, обычно изменяемый); O_TRUNC является «флагом открытого времени» (относится к самой операции open, а не к файловому дескриптору, поэтому не запоминается). Обратите внимание, что «режим доступа» не модифицируется - вы не можете использовать fcntl, чтобы превратить fd только для чтения в fd только для записи.

При использовании fcntl(fd, F_SETFL, flag), как я должен передать флаг параметров, нужно ли мне читать флаг сначала через fcntl(fd, F_GETFL), а затем изменить его и передать его? Или внутренне это просто делает операцию & с новым параметром?

F_SETFL перезаписывает флаги с точно то, что вы передаете (хотя он будет игнорировать ваши жалкие попытки, чтобы установить биты Сущего aren't-действительно-флаги, такие как 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 имеют концепцию «файла только для присоединения» на уровне файловой системы; если вы откроете один из этих файлов, а затем попробуйте очистить флаг O_APPEND дескриптора, вы получите ошибку EPERM. Возможность «только для присоединения» файла может контролироваться на уровне файловой системы с помощью утилиты chattr.


Вот более систематическая версия вашей тестовой программы. Это может вас не заинтересовать, но я узнал что-то, написав его, поэтому я оставляю его здесь. :)

#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 
3

1) Возвращение fcnl представляет собой код, который описан, если функция succceded и как:

ВОЗВРАТ СТОИМОСТИ

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 у вас есть полное описание.

+0

Thx для ваших эффектов, но это действительно не отвечало на мои вопросы. –