APUE读书笔记-文件I/O

时间:2021-01-17 10:04:16

引言

大多数文件I/O只需用到5个函数:

  • open
  • read
  • write
  • lseek
  • close

文件描述符

当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。
当读、写一个文件时,使用open或create返回的文件描述符标识该文件,将其作为参数传送给read或write。

文件描述符的符号常量:(输入0、输出1、错误2)

  • STDIN_FILENO
  • STDOUT_FILENO
  • STDERR_FILENO

文件描述符的变化范围为0~OPEN_MAX-1,现在很多系统将OPEN_MAX定为64

函数open和openat

作用:打开或创建一个文件
两个函数的返回值:

  • 若成功,返回文件描述符(一定是最小的未用描述符数值)
  • 若出错,返回-1

open

int open(const char *path,int oflag),... /*mode_t mode */);
  • path为要打开或创建文件的名字
  • oflag用来说明此函数的多个选项(读,写,执行等)

openat

int openat(int fd,const char *path,int oflag),... /*mode_t mode */);

openat多了个fd参数,有三种可能性:

  • path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,openat函数相当于open
  • path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取
  • path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。在这种情况下,路径名在当前工作目录中获取,openat函数再操作上与open函数类似。

函数creat

作用:创建一个新文件(只写方式)

int creat(const char *path,mode_t mode);

等价于:open(path, O_WRONLY | O_CREAT | O_TRUNC,mode);
mode为访问权限
creat函数的返回值:

  • 若成功,返回只写方式打开的文件描述符(一定是最小的未用描述符数值)
  • 若出错,返回-1

函数close

作用:关闭一个打开文件

int close (int fd);

函数lseek

先了解一个概念,当前文件偏移量:度量从文件开始处计算的字节数,通常用来从文件中找到所用数据。

lseek函数就是用来设置偏移量的

off_t lseek(int fd,off_t offset,int whence);

返回值:

  • 成功:返回新的文件偏移量
  • 出错:返回-1

whence有三个选项:

  • SEEK_SET:相对文件开始处+offset字节
  • SEEK_CUR:相对当前位置+offset字节
  • SEEK_END:相对文件长度+offset字节

文件偏移量可以大于文件当前长度,这时,下一次写该文件的时候会加长该文件,并在文件中构成一个空洞,这是允许的,位于文件中但没有写过的字节都读为0

文件中的空洞部分不占磁盘上的存储区,具体处理方式与文件系统的实现有关。

Ex:
apue.h的解决方法请参照:
http://blog.csdn.net/u011391629/article/details/69677019
myerror.h的解决办法是
http://blog.chinaunix.net/uid-29179071-id-4117644.html

#include "apue.h"
#include <fcntl.h>
#include <myerror.h>
int main(void)
{
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int fd;
if((fd = creat("file.hole", FILE_MODE)) < 0 )
err_sys("creat error");
if(write(fd, buf1, 10) != 10)
err_sys("buf1 write error");
/* offset now = 10 */

if(lseek(fd, 16384, SEEK_SET) == -1)
err_sys("lseek error");
/* offset now = 16384 */

if(write(fd, buf2, 10) != 10)
err_sys("buf2 write error");
/* offset = 16394 */

exit(0);
}

用od -c file.hole 观察实际内容

0000000 a b c d e f g h i j \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0

0040000 A B C D E F G H I J
0040012

无空洞文件file.hole占用8个磁盘块,再创建一个与file.hole同样长度但无空洞的文件file.nohole,占用20个磁盘块。

函数read

ssize_t read(int fd,void *buf,size_t nbytes);

返回值:

  • 读到的字节数(若已到文件尾,返回0)
  • 出错,返回0

函数write

ssize_t write(int fd,const void *buf,size_t nbytes);

返回值:

  • 成功:返回已写字节数(通常和nbytes的值相同)
  • 出错:返回-1(通常是磁盘已写满,或超过了一个给定进程的文件长度限制)