PILE读书笔记_文件I/O

Posted abc_begin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PILE读书笔记_文件I/O相关的知识,希望对你有一定的参考价值。

open函数

1 int open(const char *pathname, int flags, mode_t mode);

参数说明:

(1)pathname: 表示要打开的文件路径

(2)flags: 用于指示打开文件的选项,常用的有O_RDONLYO_WRONLYO_RDWR,还有一些选项如下:

  • O_APPEND: 每次进行写操作时, 内核都会先定位到文件尾, 再执行写操作
  • O_ASYNC: 使用异步I/O模式
  • O_CLOEXEC: 在打开文件的时候, 就为文件描述符设置FD_CLOEXEC标志。 这是一个新的选项, 用于解决在多线程下fork与用fcntl设置FD_CLOEXEC的竞争问题。 某些应用使用fork来执行第三方的业务, 为了避免泄露已打开文件的内容, 那些文件会设置FD_CLOEXEC标志。 但是fork与fcntl是两次调用, 在多线程下, 可能会在fcntl调用前, 就已经fork出子进程了, 从而导致该文件句柄暴露给子进程
  • O_CREAT: 当文件不存在时, 就创建文件
  • O_DIRECT: 对该文件进行直接I/O, 不使用VFS Cache
  • O_DIRECTORY: 要求打开的路径必须是目录
  • O_EXCL: 该标志用于确保是此次调用创建的文件, 需要与O_CREAT同时使用; 当文件已经存在时, open函数会返回失败
  • O_LARGEFILE: 表明文件为大文件
  • O_NOATIME: 读取文件时, 不更新文件最后的访问时间
  • O_NONBLOCKO_NDELAY: 将该文件描述符设置为非阻塞的( 默认都是阻塞的) 
  • O_SYNC: 设置为I/O同步模式, 每次进行写操作时都会将数据同步到磁盘, 然后write才能返回
  • O_TRUNC: 在打开文件的时候, 将文件长度截断为0, 需要与O_RDWRO_WRONLY同时使用。在写文件时, 如果是作为新文件重新写入, 一定要使用O_TRUNC标志, 否则可能会造成旧内容依然存在于文件中的错误

(3)mode: 只在创建文件时需要, 用于指定所创建文件的权限位( 还要受到umask环境变量的影响) 

 

lseek函数

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

该函数用于将fd的文件偏移量设置为以whence为起点, 偏移为offset的位置。 其中whence可以为三个值: SEEK_SET、 SEEK_CUR和SEEK_END, 分别表示为“文件的起始位置”、 “文件的当前位置”和“文件的末尾”, 而offset的取值正负均可。 lseek执行成功后, 会返回新的文件偏移量。文件偏移是基于某个打开文件来说的, 一般情况下, 读写操作都会从当前的偏移位置开始读写( 所以read和write都没有显式地传入偏移量) , 并且在读写结束后更新偏移量。

返回值:当lseek执行成功时, 它会返回最终以文件起始位置为起点的偏移位置。 如果出错,则返回-1, 同时errno被设置为对应的错误值。也就是说, 一般情况下, 对于普通文件来说, lseek都是返回非负的整数, 但是对于某些设备文件来说, 是允许返回负的偏移量。 因此要想判断lseek是否真正出错, 必须在调用lseek前将errno重置为0,然后再调用lseek, 同时检查返回值是否为-1及errno的值。 只有当两个同时成立时, 才表明lseek真正出错了。

 

read函数

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

read尝试从fd中读取count个字节到buf中, 并返回成功读取的字节数, 同时将文件偏移向前移动相同的字节数。 返回0的时候则表示已经到了“文件尾”。 read还有可能读取比count小的字节数。

使用read进行数据读取时, 要注意正确地处理错误, 也是说read返回-1时, 如果errno为EAGAIN、EWOULDBLOCK或EINTR, 一般情况下都不能将其视为错误。 因为前两者是由于当前fd为非阻塞且没有可读数据时返回的, 后者是由于read被信号中断所造成的。 这两种情况基本上都可以视为正常情况。

 

write函数

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

write尝试从buf指向的地址, 写入count个字节到文件描述符fd中, 并返回成功写入的字节数, 同时将文件偏移向前移动相同的字节数。 write有可能写入比指定count少的字节数。

 

dup函数

1 int dup(int oldfd);
2 int dup2(int oldfd, int newfd);
3 int dup3(int oldfd, int newfd, int flags);

(1)dup:使用一个最小的未用文件描述符作为复制后的文件描述符。
(2)dup2:使用用户指定的文件描述符newfd来复制oldfd的。 如果newfd已经是打开的文件描述符, Linux会先关闭newfd, 然后再复制oldfd。
(3)dup3:只有定义了feature宏“_GNU_SOURCE”才可以使用, 它比dup2多了一个参数, 可以指定标志——不过目前仅仅支持O_CLOEXEC标志, 可在newfd上设置O_CLOEXEC标志。 定义dup3的原因与open类似, 可以在进行dup操作的同时原子地将fd设置为O_CLOEXEC, 从而避免将文件内容暴露给子进程。

在写daemon服务程序时, 基本上都有这样的流程: 首先关闭标准输出stdout、 标准出错stderr, 然后进行dup操作, 将stdout或stderr重定向。 但是在多线程程序成为主流以后,由于close和dup操作不是原子的, 这就造成了在某些情况下, 重定向会失败。 因此就引入了dup2将close和dup合为一个系统调用, 以保证原子性, 然而这依然有问题。 大家可以回顾1.2.2节中对O_CLOEXEC的介绍。 在多线程中进行fork操作时, dup2同样会有让相同的文件描述符暴露的风险, dup3也就随之诞生了。 这三个系统调用看起来有些冗余重复, 但实际上它们也是软件工程发展的结果。

 

sync函数

1 void sync(void);
2 int fsync(int fd);
3 int fdatasync(int fd);

(1)Linuxsync是阻塞调用 ,并不是说让所有修改过的缓存进入提交队列, 并不用等待这个工作完成,这一点和APUE上面是不同的,以这个结论为准

(2)fsync只同步fd指定的文件, 并且直到同步完成才返回,它不仅同步数据, 还会同步所有被修改过的文件元数据(包括文件的访问权限、 上次访问的时间戳、 所有者、 所有组、 文件大小等信息)

(3)fdatasync与fsync类似, 但是其只同步文件的实际数据内容, 不会影响后面数据操作的元数据

sync、 fsync和fdatasync只能保证Linux内核对文件的缓冲被冲刷了, 并不能保证数据被真正写到磁盘上, 因为磁盘也有自己的缓存。

 

stat函数

1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 int stat(const char *path, struct stat *buf);
5 int fstat(int fd, struct stat *buf);
6 int lstat(const char *path, struct stat *buf);

(1)stat得到路径path所指定的文件基本信息
(2)fstat得到文件描述符fd指定文件的基本信息
(3)lstat与stat则基本相同, 只有当path是一个链接文件时, lstat得到的是链接文件自己本身的基本信息而不是其指向文件的信息

 

truncate 函数

1 #include <unistd.h>
2 #include <sys/types.h>
3 int truncate(const char *path, off_t length);
4 int ftruncate(int fd, off_t length);

(1)truncate截断的是路径path指定的文件
(2)ftruncate截断的是fd引用的文件

“截断”给人的感觉是将文件变短, 即将文件大小缩短至length长度。 实际上, length可以大于文件本身的大小, 这时文件长度将变为length的大小, 扩充的内容均被填充为0。 需要注意的是, 尽管ftruncate使用的是文件描述符, 但是其并不会更新当前文件的偏移。

 

以上是关于PILE读书笔记_文件I/O的主要内容,如果未能解决你的问题,请参考以下文章

PILE读书笔记_基础知识

PILE读书笔记_进程环境

第七章读书笔记

《UNIX环境高级编程》读书笔记 —— 文件 I/O

TLPI读书笔记第63章:IO多路复用4

读书笔记第七章