英文版本书地址
http://chuquanl.com/?page_id=55
可用的文件IO函数:打开文件,读文件,写文件等
绝大多数文件IO只需要用到5个函数:open,read,write,lseek,以及close
文件描述符
文件描述符是一个非负整数,打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,使用文件描述符标识该文件,作为参数传递给read或者write函数。
在文件描述符中 幻数0,1,2已经被标准化,但是应当把他们替换成符号常量STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO以提高可读性,常量在 unistd.h 中定义
函数open和openat
打开或者创建一个文件
#include<fcntl.h>
int open(const char *path,int oflags,.../* mode_t mode */);
int openat(int fd,const char *path,int oflag,.../* mode_t mode */);
//成功返回文件描述符,出错返回-1
path 参数是要被打开或创建文件的名字
oflag 参数用来说明此函数的多个选项。用下列一个或多个常量进行‘或’运算构成oflag参数,常量在 fcntl.h 中定义
O_RDONLY :Open for reading only.
O_WRONLY :Open for writing only.
O_RDWR :Open for reading and writing.
Most implementations define O_RDONLY as 0, O_WRONLY as 1, and O_RDWR as 2, for compatibility with older programs.
O_EXEC :Open for execute(执行) only.
O_SEARCH :Open for search only (applies to directories).
以上五个常量中必须指定一个且只能指定一个
下列常量则是可选的
O_APPEND :Append to the end of file on each write.
O_CLOEXEC :Set the FD_CLOEXEC file descriptor flag. 把FD_CLOEXEC常量作为文件描述符标志
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. 文件不存在则创建,使用此参数需要同时说明 mode 参数
O_DIRECTORY : Generate an error if path doesn’t refer to a directory. 如果path不是目录 出错
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. 如果指定了O_CREAT并且文件存在,出错,可用于测试文件是否存在
O_NOCTTY : If path refers to a terminal device, do not allocate the device as thecontrolling terminal for this process.
O_NOFOLLOW : Generate an error if path refers to a symbolic link(符号链接).
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.
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. 等待物理io操作完成
O_TRUNC : If the file exists and if it is successfully opened for either write-only or read–write, truncate its length to 0.如果文件存在并且可写,将其长度截断为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(规格).
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.
由open和openat函数返回的文件描述符一定是最小的未用描述符数值。
fd参数把open和openat分开的3种可能性
1. path参数指定的是绝对路径名,这种情况下,fd参数被忽略,openat函数相当于open函数。
2. path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始位置。fd参数是通过打开相对路径名所在目录来获取。
3. path参数指定了相对路径名,fd参数具有特殊值 AT_FDCWD 。此时,路径名从当前工作目录中获取,openat在操作上和open函数相似
函数create
创建一个新文件
#include<fctl.h>
int create(const char *path,mode_t mode);
//成功返回为 只写 打开的文件描述符,出错返回-1
//函数等效于
open(path,O_WRONLY | O_CREAT | O_TRUNC ,mode);
以只写方式打开所创建文件,如果要先写再读,必须先 creat 后 close 再open ,但现在open新版本可直接实现 这一流程
函数close
关闭一个打开文件
#include<unistd.h>
int close(int fd);
//成功返回0 出错返回-1
函数lseek
设置打开文件偏移量
#include<unistd.h>
off_t lseek(int fd,off_t offset,int whence);
//成功返回新的文件偏移量,出错返回-1
参数 whence 值有下面几个
* SEEK_SET 将文件偏移量设置为距文件开始处offset个字节
* SEEK_CUR 将文件偏移量设置为当前值加 offset ,offset可正可负
* SEEK_END 将文件偏移量设置为文件长度加 offset , offset可正可负
有些特殊文件允许偏移量为负值,故测试的时候需要谨慎,不要测试小于0,而要测试是否等于-1
函数read
从打开文件中读取数据
#include<unistd.h>
ssize_t read(int fd,void *buf,ssize_t nbytes);
//成功返回读到的字节数,若已经读到文件尾,返回0;出错返回-1
函数write
向打开文件写数据
#include<unistd.h>
ssize_t write(int fd,const void *buf,size_t nbytes);
//成功返回已写字节数,出错返回-1
文件共享
unix环境编程第59页
原子操作
追加到一个文件
两个进程同事追加到一个文件,如果不使用 append 参数,则一个进程写的时候可能覆盖另一个进程所写,此问题出现的原因是两个分开的函数调用,解决问题的方法是原子操作
Unix提供了一种原子操作方法,即在打开文件时设置 O_APPEND 标志。
函数pread 和 pwrite
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
// Returns: number of bytes read, 0 if end of file, −1 on error
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
//Returns: number of bytes written if OK, −1 on error
Calling pread is equivalent to calling lseek followed by a call to read, with the following exceptions.
• There is no way to interrupt the two operations that occur when we call pread.
• The current file offset is not updated.
Calling pwrite is equivalent to calling lseek followed by a call to write, with similar exceptions.
函数dup dup2
下面两个函数都可以用来复制一个文件描述符
c
#include <unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
//Both return: new file descriptor if OK, −1 on error
dup返回的新文件描述符一定是当前可用文件描述符中的最小数值,dup()用来复制参数oldfd所指的文件描述词,并将它返回。此新的文件描述词和参数oldfd指的是同一个文件,共享所有的锁定、读写位置和各项权 限或旗标。例如,当利用lseek()对某个文件描述词作用时,另一个文件描述词的读写位置也会随着改变。
dup和dup2两个函数都可以用来复制打开的文件描述符,复制成功后和复制源共享同一个文件表。
dup2()用来复制参数oldfd所指的文件描述词,并将它拷贝至参数newfd后一块返回。若参数newfd为一已打开的文件描述词,则newfd所指的文件会先被关闭。dup2()所复制的文件描述词,与原来的文件描述词共享各种文件状态。
## 函数 sync fsync fdatasync
当我们向文件写入数据的时候,内核通常先将数据复制到缓冲区,然后排入队列,晚些时候写入磁盘。这种方式被称为延迟写。
当内核需要重用缓冲区来存放其他磁盘数据块时,会把所有延迟写的数据块写入磁盘为了保证实际文件系统与缓冲区内容的一致性,Unix提供了这三个函数
“`c
#include
函数fcntl
fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* int arg */ );
//Returns: depends on cmd if OK (see following), −1 on error
函数的五种功能:
根据cmd的值不同而不同
1.复制一个现有的描述符(cmd=F_DUPFD 或 F_DUPFD_CLOEXEC).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
函数ioctl
IO操作的杂物箱,不能用其他函数的操作就用这个,最多的地方用于终端IO
#include <unistd.h>
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
//Returns: −1 on error, something else if OK
dev/fd
其目录项都是名为 0 1 2等文件,打开文件/dev/fd/n 等效于复制描述符 n(假定打开)