文件I/O (File I/O)

3.1 Introduction

在unix系统中,大部分使用文件I/O的情况是这五个函数openreadwritelseekclose

 unbuffered I/O每一次调用read或write都深入到了unix的内核。unbuffered I/O函数不是ISO C的标准,但却是POSIX.1和Single Unix Specification 标准的一部分。

需要注意的是,当我们打开一个文件时,在多进程中,原子操作是很有必要的。函数dup、fcntl、sync、fsyncioctl 这些设计多进程的函数将会进一步表述

3.2 File Description

 文件描述符其实就是个非负整数,用来唯一定位和标识一个文件。Unix操作系统中通常默认0、1、2号文件表述符为标准输入、标准输出和标准错误输出,为了可读性,可以用宏定义的常量来表示,分别为STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。不要轻易该庙这3个文件表述符代表的意义,不然部分程序可能无法正常工作。

文件描述符(0 ~ OPEN_MAX-1),起初时,每个进程的可以使用的文件最大文件数是20(即OPEN_MAX==20),随后大量的系统开始增加到了64.使用我的CentOS7,利用sysconf(_SC_OPEN_MAX)的返回值是4096(前缀_SC_是system configuration的首字母),说明可以打开4096个文件。

3.3 Open and openat Functions

调用open或openat,可以打开或创建一个文件。

#include<fcntl.h>
int open(const char* path, int oflag/*, mode_t mode*/);
int openat(int fd, const char* path, int oflag/*, mode_t mode*/);
// Both return : file descriptor if OK, -1 on error

最后一个参数mode只有在文件是被创建时才被使用。path既可以是相对路径也可以是绝对路径。

path是被创建和打开的文件的路径,oflag参数选项。

下面的这五个选项是必选项:

O_RDONLY Open for reading only
O_WRONLY Open for writiing only
O_RDWR Open for reading and writing.
O_EXEC Open for execute only
O_SEARCH Open for search only(applies to directions)

*其中大部分系统为了兼容以前的版本,定义O_RDONLY = 0, O_WRONLY= 1, ORDWR = 2;

*O_SEARCH是用来评估一个被打开的目录的搜索权限。不过APUE这本书中所涉及的UNIX系统并不支持O_SEARCH。

下面这些选项是可选的:

O_APPEND Append to the end of file on each write.
O_CLOEXEC
Set the FD_CLOEXEC file descriptor flag.
O_CREAT
Create the file if it doesn’t exist.This option requires a third argument to the open function (a fourth argument to the openat function) — the mode, which specifies the access permission bits of the new file. (When we describe a file’s access permission bits in Section 4.5, we’ll see how to specify the mode and how it can be modified by the umask value of a process.)

O_DIRECTORY
Generate an error if path doesn’t refer to a directory.
O_EXCL
Generate an error if O_CREAT is also specified and the file already exists. This test for whether the file already exists and the creation of the file if it doesn’t exist is an atomic operation We describe atomic operations in more detail in Section 3.11.
O_NOCTTY
If path refers to a terminal device, do not allocate the device as the controlling terminal for this process. We talk about controlling terminals in Section 9.6.
O_NOFOLLOW
Generate an error if path refers to a symbolic link. We discuss symbolic links in Section 4.17
O_NONBLOCK
If path refers to a FIFO, a block special file, or a character special file, this option sets the nonblocking mode for both the opening of the file and subsequent I/O. We describe this mode in Section 14.2.
O_SYNC
Have each write wait for physical I/O to complete, including I/O necessary to update file attributes modified as a result of the write. We use this option in Section 3.14.
O_TRUNC
If the file exists and if it is successfully opened for either write-only or read–write, truncate its length to 0.
O_TTY_INIT
When opening a terminal device that is not already open, set the nonstandard termios parameters to values that result in behavior that conforms to the Single UNIX Specification. We discuss the termios structure when we discuss terminal I/O in Chapter 18.

下面的两个选线也是可选的,它们是Single UNIX Specification和POSIX.1中的同步输入输出选项中的的一部分:

O_DSYNC
Have each write wait for physical I/O to complete, but don’t wait for file attributes to be updated if they don’t affect the ability to read the data just written
O_RSYNC
Have each read operation on the file descriptor wait until any pending writes for the same portion of the file are complete.

*O_DSYNC 和 O_SYNC 标志位相似但却有一点微小的不同, O_DSYNC 被置位时,文件的属性不会被同步更新,只有需要更新时才更新。当O_SYNC被置位时,文件的属性是一直同步更新。比如当写入一个以O_DSYNC置位的方式打开的文件时,文件的时间不会被同步更新。相反,如果写入一个以O_SYNC置位的方式打开的文件时,在write return前,每次write这个文件,都会更新文件的时间,而不管我们是否覆盖原有字节的写入还是追加写入。

每次通过open和openat返回的文件表述符是最小的未使用的文件描述符。这种特性可以被应用程序设计为以标准输入、标准输出、标准错误的方式打开一个文件。当探讨dup2函数时,我们就可以知道如何根据特定的描述符来打开一个文件。

open和openat的区别在于参数fd(文件描述符)。有下列三种情况:

1.参数path指的是绝对路径,那么fd参数可以忽略,此时openat和open一样。

2.参数path是相对路径,fd参数则为在文件系统中,相对路径开始位置的文件描述符。我们可以通过打开目录的方式,获得fd的值。

3.参数path是相对路径,df参数取特殊值AT_FDCWD,在这种情况下,开始位置则为当前的工作目录。此时openat和open一样。

openat函数,被加入到最新的POSIX.1中,为了解决两个问题。

1.让线程可以通过非当前目录的相对路径来打开文件。

2.提供了一种避免time-of-check-to-time-of-use(TOCTTOU)的错误。

所谓TOCTTOU error, 为了正确理解意思,英文原文如下:

The basic idea behind TOCTTOU errors is that a program is vulnerable if it makes two file-based function calls where the second call depends on the results of the first call. Because the two calls are not atomic, the file can change between the two calls, thereby invalidating the results of the first call, leading to a program error. TOCTTOU errors in the file system namespace generally deal with attempts to subvert file system permissions by tricking a privileged program into either reducing permissions on a privileged file or modifying a privileged file to open up a security hole. Wei and Pu[2005] discuss TOCTTOU weaknesses in the UNIX file system interface.

关于文件名和文件路径名的截断问题:

如果NAME_MAX的值是14,而我们创建一个名长度为15的文件,那么最后一个字符将被丢弃。

在POSIX.1中,常量_POSIX_NO_TRUNC 决定了是否长文件名被截断或报错。我们可以使用fpathconf或者pathconf函数来获取NAME_MAX的值。

当_POSIX_NO_TRUNC 被置位时, 如果文件名超过了 NAME_MAXerrno is set to ENAMETOOLONG, and an error status is returned。 

 3.4 Creat Function

#include <fcntl.h>
int creat(const char *path, mode_t mode);
//Returns: file descriptor opened for write-only if OK, −1 on error
// this function is equivalent to the follow one
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);

creat函数是由于之前的open函数的标志位只有0,1,2即O_RDONLY、 O_WRONLY和ORDWR,那么当一个文件不存在时,就只能通过creat函数来创建一个函数,然后再close这个文件,然后载open文件。

不过现在open函数得到了扩展,已经可以通过O_CREAT来创建文件了。所以以后可以不适用creat函数了。只需用open就行了,open更加简介干脆。

3.5 close Function

未完待续。。。

 

猜你喜欢

转载自www.cnblogs.com/yb-blogs/p/12687747.html