深入探究文件I/O
Posted zsd0101
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入探究文件I/O相关的知识,希望对你有一定的参考价值。
- open函数相关概念
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
mode是指open如果创建文件时的文件的访问权限,如果没有创建文件,则不需要mode参数;
flag相关常量有:
以上,flag分为了三组:
1)文件访问模式标志:O_RDONLY O_WRONLY O_RDWR,每次open只能用其中一个,调用fcntl()中的F_GETFL可以检索文件访问模式;
2)文件创建标志:不能检索,无法修改;
3)已打开文件的状态标志:用fcntl()中的F_GETFL和F_SETFL可以分别检索和修改此类标志,有时简称文件状态标志;
- 原子操作
open()时使用O_EXCL标志,保证调用者就是文件的创建者;使用O_APPEND标志调用open(),确保多个进程对同一文件尾部增加数据时不会覆盖彼此的输出;
- 文件控制操作、打开文件的状态标志
fcntl()函数:
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
man手册已经对其使用范围解释很清楚了:
F_GETFL (void)
Return (as the function result) the file access mode and the file status
flags; arg is ignored.
F_SETFL (int)
Set the file status flags to the value specified by arg. File access
mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags (i.e.,
O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored. On Linux, this
command can change only the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and
O_NONBLOCK flags. It is not possible to change the O_DSYNC and O_SYNC
flags; see BUGS, below.
以上,我们有时会需要F_SETFL去设置文件状态标志,例如标准输入输出(不是我们打开的)、socket()接口(不是通过open打开的)等等。
- 文件描述符和打开文件之间的关系
下图表示的是文件描述符、打开的文件句柄、打开文件表、i-node表之间的关系,中英对照看一下,这里的概念有些模糊,文件描述符应该是我们常用的fd,如调用open返回的正整数,打开文件句柄应该是内核里面系统级的索引概念:
- 复制文件描述符
关于shell中的I/O重定向语法,此文章有很详细的解释:https://www.bbsmax.com/A/GBJrLoQ3d0/
dup(),dup2(),dup3(),fcntl()+F_DUPFD,都是关于复制文件描述符以及关于close-on-exec处理的
#include <unistd.h> int dup(int oldfd); int dup2(int oldfd, int newfd);
dup()将参数中的文件描述符oldfd复制,返回新的描述符,返回的新的描述符指向oldfd对应的打开文件,且是当前最小的未使用文件描述符;
而dup2()位oldfd创建副本,且副本的编号由newfd决定,若newfd已打开则强行关闭
- 在文件特定偏移量处的I/O
pread()和pwrite()
#include <unistd.h> ssize_t pread(int fd, void *buf, size_t count, off_t offset); ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
这两个函数类似于read和write,但是可以指定操作起始偏移,且使用后不会改变当前文件偏移;
分散输入和集中输出
#include <sys/uio.h> ssize_t readv(int fd, const struct iovec *iov, int iovcnt); ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
这两个系统调用不操作单个缓冲区,可一次传入多个缓冲区数据,*iov只缓冲区,iovcnt只*中包含的缓冲区个数:
struct iovec { void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ };
readv()和writev()都是原子操作,使用时都要检查返回值判断数据操作是否完整,注意它们也会改变文件偏移;
类似于pread()和pwrite(),在指定的文件偏移量处执行分散输入/集中输出的函数是preadv()和pwritev();
#include <sys/uio.h> ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset); ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
- 截断文件:truncate()和ftruncate()
#include <unistd.h> #include <sys/types.h> int truncate(const char *path, off_t length); int ftruncate(int fd, off_t length);
将当前文件大小设置为length参数指定的值;
若当前文件长度大于length,丢弃超出部分,若小于length,则文件尾部添加一系列空字节或一个文件空洞;
truncate()以路径名字符串指定文件,要求该文件路径名个目录有可执行权限,且有写权限;
ftruncate()以文件描述符指定文件,需要先以可写方式打开文件,该系统调用不会修改文件偏移量;
- 非阻塞I/O
内核缓冲区保证了普通I/O文件不会陷入阻塞,所以打开普通文件时一般会忽略O_NONBLOCK标志;
- 大文件I/O
存放文件偏移量的数据类型off_t为一个有符号长整型,32位机中文件大小限制在2^31 - 1个字节(2GB)以内,有两种方式可以使32位机获取控制大文件的要求:
1、过渡性LFS API:
编译时程序时要定义 _LARGEFILE64_SOURCE,并且使用如下API:fopen64() open64() lseek64() truncate64() stat64() mmap64() setrlimit64()
增加了一些数据类型,如:struct stat64,off64_t
2、_FILE_OFFSET_BITS宏:
在编译程序时,将宏_FILE_OFFSET_BITS的值设定为64,可以在编译时:cc -D_FILE_OFFSET_BITS 64,或者C文件中添加:#define _FILE_OFFSET_BITS 64。
之后,相关的32位数据类型和函数会自动转换为64位版本,此种方式可以执行更强,要注意的是应用程序代码更规范(例如文件偏移变量数据类型必须是off_t,而不能用long long);
注意此时使用printf,以如下形式:printf("offset=%lld ",(long long )offset );
- /dev/fd目录
内核对每个进程体哦概念股一个特殊的虚拟目录/dev/fd,其中内容便是和进程中打开的文件描述符对应的编号,例如/dev/fd/0就对应进程的标准输入;
打开/dev/fd目录中一个文件等同于复制相应的文件描述符,以下两行功能相同:
fd = open("/dev/fd/1", O_WRONY);
fd = dup(1);
- 创建临时文件
#include <stdlib.h> int mkstemp(char *template);
用于创建临时文件,生成一个唯一文件名并打开该文件,打开时使用了O_EXCL标志,*template是路径形式,必须是字符数组,且最后六个字符必须是XXXXXX,打开后不久需要用unlink将其文件名删除,close()后该文件将被删除 :
#include <stdio.h> FILE *tmpfile(void);
tmpfile()会创建一个名称唯一的临时文件,并以读写方式将其打开, 打开时使用了O_EXCL标志,执行成功后返回一个文件流共stdio库函数使用,该函数在打开文件后内部会调用unlink删除文件名,文件流关闭后自动删除该文件;
以上是关于深入探究文件I/O的主要内容,如果未能解决你的问题,请参考以下文章
TLPI(liunx/unix系统编程手册)笔记 深入探究文件I/O