引言:
open/read/write/lseek/close
read/write 都是不带缓冲的IO,其都是的内核中的一个系统调用
1,文件描述符
Linux下一切皆文件,对于内核而言,所打开的文件都是通过文件描述符引用,文件描述符是一个【非负整数】。当打开或创建一个文件时,内核返回给进程一个文件描述符。
注:0,1,2三个文件描述符分别对应于:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。(标准)
文件描述符的变化范围可以是:0~OPEN_MAX-1 (进程可以打开的文件描述符个数,早期是OPEN_MAX = 19 也就是20个文件描述符)
2,创建和打开一个文件
1、
int open(const char * path , int flags, .... mode_t mode ); //创建和打开一个文件
int openat( int fd, const char * path , int flags, .... mode_t mode )
//mode 需要创建新文件时使用该参数 0666 0777
flags:
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
O_EXEC 只执行打开
O_SEARCH 只搜索打开(应用于目录)
以上几个只能选择一个,下面是可选项:
O_APPEND 追加打开
O_CLOEXEC 把FD_CLOEXEC常量设置为文件描述符标志
O_CREAT 文件不存在就创建(可使用mode 参数设置权限)
O_DIRECTORY 如果path引用的不是目录,则出错
O_EXCL 测试文件是否存在,一般与O_CREAT一起使用,存在就出错返回,
不存在就创建
O_NOCTTY 如果path是终端设备,则不将该设备分配为该进程的控制终端
O_NOFOLLOW 如果path是符号链接,则出错
O_NONBLOCK 如果path引用的是一个FIFO,字符、快设备文件则将后续的I/O设置
为非阻塞
O_SYNC 使每次write都等待物理I/O操作完成(会更改文件属性值)
O_TRUNC 如果文件存在,就长度截断为0,(只写打开和读-写打开)
。。。。
2、 int creat(const char *path, mode_t mode) //创建一个新文件
等价于
open( path,O_WRONLY | O_CREAT |O_TRUNC,mode );
缺陷:
creat 是以只写方式打开,当我们要读取时,需要close后,在读-写打开
3、close(fd) :关闭一个文件描述符
进程终止时,系统也会关闭进程打开的所有文件描述符
4、lseek 函数
lseek(int fd, int offset, int where) //文件描述符偏移量
where:
SEEK_SET 设置文件描述符在文件开头处offset个字节
SEEK_CUR 设置文件描述符在文件在当前处offset个字节(可正负)
SEEK_END 设置文件描述符在文件在末尾处offset个字节(可正负)
注:对于管道文件,lseek将返回错误,可以用来判断文件描述符是否可以设置偏移量
ret = lseek(fd , 0,SEEK_CUR); //ret = -1, errno = ESPIPE (管道文件不可设置)
文件的偏移量可以大于当前的文件长度,对文件的下次读写,将加长该文件,并在文件中构成一个空洞,但这是允许的,那些没有写的空洞会被置为零。
2、读---写操作
#include <unistd.h>
1、ssize_t read(int fd,const void *buf, size_t nbytes)
success : 读取的字节数 , 文件末尾返回 0
error : -1
注意:
从终端设备读取时,通常只能读取一行
网络设备读取时,由于缓冲机制,可能读取的小于设置的大小
从FIFO或管道读时,返回实际读取的字节数
当被信号中断时,实际读取多少,文件偏移量就好设置多少
2、ssize_t write(int fd,const void *buf, size_t nbytes)
success : 写入的字节数
error:-1
注意:
write的出错一般可能是磁盘写满,或超过了一个给定进程的文件长度限制
O_APPEND 参数打开的fd,调用write时,会先将文件偏移量设置为文件末尾,再调用write,当超过返回时,该文件偏移量增加实际写的字节数。
3、I/O的效率
大多数文件系统为改善性能都采用某种预读(read ahead)机制,当检测到系统试图尝试读入比应用要求的更多数据时,并假想应用很快会读取这些数据。
4、系统进程间文件共享
解释:
内核使用3种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程多另一个进程可能产生的影响。
每个进程在进程表中都有一个记录项,记录项包含一张打开的文件描述符表,每个描述符占用一项,与每个文件描述符相关的是:
- 文件描述符标志(close_on_exec)| 指向一个文件表项的指针
内核为所有打开的文件维持一张文件表,每个文件表项包含:
- 文件状态标志(读、写、添加、同步、非阻塞等)
- 当前文件偏移量
- 指向该文件v节点表项的指针
每个打开的文件或设备都有一个v节点,v节点包含文件类型和对该文件进行各种操作的函数指针,对于大多数文件,v节点还包含该文件的i-node节点信息,(这些信息在打开文件时从磁盘上读入到内存的,所以,文件的所有相关信息都是随时可用的),i节点包括文件所有者、文件长度、指向文件实际数据块在磁盘上所在的位置指针。
当两个进程同时打开一个文件,他们会分别指向不同的文件表项,但都使用同一个i节点信息
注:
- 在完成每一个write后,文件表项中的偏移量就增写入的字节数,如果导致了文件偏移量超过了当前的文件长度,则设置 i 节点表项中的文件长度为当前文件偏移量。
- O_APPEND标志打开的文件,其标志被设置到文件表项的文件状态中,每次write时,当前文件表项的文件偏移量会被先设置为i节点的中的文将偏移量。
- lseek函数修改文件表项中的当前文件偏移量,不进行任何IO操作(不会去设置i节点的文件偏移量,需要靠write、read进行IO操作),这与O_APPREND的打开操作是不同的。
5、dup 和dup2函数
//复制一个文件描述符
int dup(int fd);
int dup2(int fd1, int fd2);
return :
success: 返回新的最小的文件描述符
error : -1
解释:
dup:返回一个新的可用的最小的文件描述符。
dup2:
fd2已经打开,关闭fd2,返回fd2
fd2 == fd1 ,返回fd2,而不关闭它,否则fd2的FD_CLOEXEC文件描述符标志被清除。这样fd2在进程调用exec时是打开状态。