一、文件描述符
对于内核而言所有打开的文件都是通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读或者写一个文件时使用open或creat返回文件描述符标识该文件,将作为参数传递给read或write
UNIX系统shell把文件描述符0与进程的标准输入关联,文件描述符1与标准输出关联,文件描述符2与标准错误关联。这是各种shell以及很多程序使用的惯例,与UNIX内核无关。
在符合POSIX.1的应用程序中。幻数0、1、2以及被标准化。但是建议使用符号常量:STDIN_FILENO、STDOUT_FILENO 、STDERR_FILENO以提高可读性。这些常量定义在头文件<unistd.h>中。
文件描述符的变化范围是0~OPEN_MAX-1。早期可以同时使用的文件描述符是20个(既可以同时打开20个文件),现在多是64。可以使用函数sysconf来查看OPEN_MAX的值。
long sysconf(int _SC_OPEN_MAX)
二、文件操作
1、文件创建与打开
(1)open
1)函数原型
#include <fcntl.h>
int open(const char*path,int oflag,.../*mode_t mode*/);
2)参数说明
path是要打开或创建文件的名字。
最后一个参数写为...表明余下的参数的数量及类型是可变的。仅当创建新文件时才使用最后这个参数mode.用于指定文件的访问权限位。
oflag参数可以用来说明此函数的多个选项。用下列一个或多个常量进行“或”运算构成oflag参数
O_RDONLY 只读模式
O_WRONLY 只写模式
O_RDWR 读写模式
O_EXEC 只执行打开
O_SEARCH 只搜索打开(应用与目录)
打开/创建文件时,至少得使用上述五个常量中的一个。以下常量是选用的:
O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件。使用此选项时要同时说明open函数的第三个参mode,用mode指定该新文件的权限位。
O_EXCL 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)
以下三个常量同样是选用的,它们用于同步输入输出
O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
O_RSYNC read 等待所有写入同一区域的写操作完成后再进行
O_SYNC 等待物理 I/O 结束后再 write,包括更新文件属性的 I/O
3)返回值
open 函数返回的文件描述符一定是最小的未用的描述符值。
(2)openat
int openat(int fd, const char* path,int oflag,.../*mode_t mode*/);
与open 不同的是openat函数可以使用相对路径名打开目录中的文件,而不再只打开当前工作目录。
path参数是绝对路径名时,fd参数被忽略,openat函数就相当于open函数;path参数是相对路径名时,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取;若fd参数的值为AT_FDCWD则,路径名会在当前工作目录中获取。
(3)creat
#include<fcntl.h>
int create(const char* path,mode_tmode);
等价于
open(path,O_WRONLY | O_CREAT | O_TRUNC, mode);
create 的不足之处是它一只写的方式打开所创建的文件。
2、文件关闭
#include<unistd.h>
int close(int fd);
关闭一个文件时还会释放该进程加在该文件上的所有记录锁。
当一个进程结束时,内核会自动关闭它所有打开的文件。
3、读文件
(1)read
1)函数原型
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t nbytes)
2)返回值
read 成功时,返回读到的字节数。如已到达文件的尾端,则返回0;若出错,返回-1。
3)说明
多种情况会使实际读到的字节数少于要求读的字节数。例如:在读到要求的字节数之前已经到达了文件的尾端;从终端设备读时,通常一次最多读一会等
4、写文件
(1)write
#include <unistd.h>
ssize_t write(int fd,const void* buf,size_t nbytes);
write函数向打开的文件写数据。若成功返回已写的字节数,若出错返回-1.
5、其它
(1)使用int dup(int fd)或int dup2(int fd,int fd2)函数进行文件描述符复制
(2) 使用int fsync(int fd)或void sync(void);将缓冲区数据写至磁盘
(3)使用fcntl函数gaibian已经打开的文件属性
(4)使用off_t lseek(int fd,off_t offset,int whence);函数显示设置一个已经打开文件的偏移量
三、与标准C函数库的区别
标准C库是对系统文件I/O接口的封装。标准I/O库处理了很多细节,如缓冲区分配、以优化的块长度执行I/O等。
1、不带缓冲
不带缓冲指:每个read和write都调用内核中的一个系统调用
2、文件描述符与流
对于内核所有的文件操作都是围绕文件描述符来进行的;但是标准C I/O库是围绕流(FILE*)进行的。