文件描述符
文件描述符的早期上限为20 , 现在增到 63; 其变化范围为 0 ~ OPEN_MAX
open函数
int open(const char* filename , int oflag , ... /*mode_t mode*/);
filename : 要打开文件的名字
oflag : 打开方式
O_RDONLY 、 O_WRONLY 、O_RDWR 三个常量必须指定一个
O_APPEND 、 O_CREAT 、O_EXCL 、O_TRUNC等是可选择的
O_CREAT | O_EXCL 组和可用于判断文件是否存在,存在则打出错,否则创建新文件
/***********************************************
*function : check if a file exist
*params : @filename: pointer to the buffer
*return : return -1 if exist or -1 if not
************************************************/
int is_file_exist(const char* filename);
int is_file_exist(const char* filename)
{
int fd;
if(filename == NULL)
return 0;
fd = open(filename,O_RDONLY|O_EXCL|O_CREAT);
if(fd != -1){
close(fd);
remove(filename);
return 0;
}
return 1;
}
O_DSYNC :等待每次write操作的完成 , 但是不等待文件属性的更新 , 除非影响到写操作的完成
O_RSYNC :等待读操作的完成
O_SYNC :等待每次写操作的完成并且同步更新文件属性
open() 返回的文件描述符值一定是最小未用描述符值。这一特点常被用来重定向标准文件描述符:具体做法是先调用close关闭标准描述符
(如 STDIN_FILENO),然后调用open函数,就可以在标准输入上打开新文件。
creat(const char* filename , mode_t mode); 这个函数相当于 open(filename , O_RDWR|O_EXCL|O_CREAT, mode);
文件名和路径的截短
当文件名超过 NAME_MAX 或路径长度超过 PATH_MAX的时候,根据系统不同,结果也不相同。
可以测试 _POSIX_NO_TRUNC 宏
#ifdef _POSIX_NO_TRUNC
if( (fd = open(filename,O_RDWR|O_EXCL|O_CREAT) == -1 && errno == ENAMETOOLONG){
perror(filename);
#endif
close 函数
关闭打开的文件
lseek函数
设置打开文件的文件偏移量,默认情况下,打开一个文件时,除非O_APPEND标志被设置,否则文件偏移 为0
该函数不能用于操作 管道、套接字、FIFO , 函数的返回值是先前的文件偏移量
有些设备允许偏移量为 负 ,所以测试函数是否执行成功的时候 应该和 -1 比较 , 如果 ==-1 则失败 否则为成功
lseek 仅仅调整内核中保存的文件结构的偏移量,不会引起任何I/O操作
当文件偏移量大于文件长度的时候, lseek会在文件中形成文件空洞, 这些文件空洞不占用磁盘空间, 当 I/O 函数读写文件的时候,
读到的空洞部分的字节的值为 0
read函数
读到的字节数少于要求读取的字节数的原因
读普通文件 :到达文件尾端
读终端设备 :一次最多读取一行
网络 :缓冲机构可能造成返回值小于要读取的字节数
管道或FIFO :管道中所包含的字节数少于要读取的字节数,则返回管道中实际的字节数
由于信号造成中断
write函数
返回实际写入的字节数
I/O效率
在发生I/O操作的时候,一般缓冲区大小为 4096 的时候,I/O效率最高
大多数的文件系统为改善I/O效率 , 一般采用某种 预读(read ahead)技术。 当检测到正在进行顺序读取的时候,系统会试图读取比
要求的更多的数据,并假象应用程序很快会读取这些数据
文件共享
不同进程中的文件描述符表中的描述符只想相同的文件节点,就实现了所谓的文件共享
原子操作
原子操作要么不执行,要么一次执行完。
不能被系统中断的操作。 当多进程对同一个文件进行写操作的时候,写操作应该 保证为原子 操作。可以用 pread 和 pwrite这两个原子操作函数
来替代 read 和 write , 这俩函数执行的时候无法被中断。而且 pread 不更新文件指针
三种复制现有文件描述符的方法
1、int dup(int fd) 函数
2、int dup2(int fd, int fd2);
将 fd 复制为 指定的fd2 的描述符,如果fd2已经打开,则先关闭fd2
3、int fcntl(fd, F_DUPFD , fd2 )
冲洗缓冲区的三个函数
void sync(void) : 将所有修改过的块排入写入队列 , 然后就返回, 不等待写入操作的结束
int fsync(int fd) : 仅仅对单一文件的缓冲区冲洗,但是会等待写入操作的完成,并且同步更新文件属性
int fdatasync(int fd) : 同 fsync() , 但是不会更性文件属性,除非影响到写入操作
/dev/fd/
当描述符 n 是打开的时候, 此时打开 “/dev/fd/n” 相当于复制 描述符 n 。
但是打开后的权限不可更改 , 只能 是 描述度 n 的一个子集 。