Linux文件IO详解

文件的维护方式

默认情况下,文件是在磁盘中以inode形式维护的。
对于打开文件,内核维护了3个数据结构:

  • 进程级文件描述符表,用来映射文件描述符和文件句柄(文件对象);
  • 系统级打开文件表,用来映射文件句柄和inode;
  • 文件系统的inode表,用来标记文件数据位置。

image.png

上述三者的关系:

  • 在进程 A 中,文件描述符 1 和 20 都指向同一个打开的文件句柄(文件对象),这可能是通过调用 dup()、dup2()或 fcntl()而形成的;
  • 进程 A 的文件描述符 2 和进程 B 的文件描述符 2 都指向同一个打开的文件句柄。这可能是 fork() 后出现的,或socket传递了一个打开的文件描述符给另一个进程;
  • 进程 A 的描述符 0 和进程 B 的描述符 3 分别指向不同的打开文件句柄,但这些句柄均指向相同的 i-node ,换言之,指向同一文件。是因为每次 open() 时返回的文件描述符通常是不一样的。

打开文件 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); 

//成功返回文件描述符,失败返回-1

open的操作过程: (1)判断文件路径是否合法,是否创建文件;(2)从磁盘加载文件数据到内存,创建file对象;(3)返回文件描述符。
若打开文件时发生错误,open()将返回−1,错误号 errno 标识错误原因,也可以使用perror显示错误原因。

open中的flag参数和open返回的错误码

标志用途错误码含义
O_RDONLY以只读方式打开EACCES权限拒绝
O_WRONLY以只写方式打开EISDIR是目录而非文件
O_RDWR以读写方式打开EMFILE进程级文件描述符到达上限
O_CLOEXECfork时,子进程不继承该文件描述符ENFILE系统级文件描述符达到上限
O_CREAT若文件不存在,则创建文件ENOENT没有这样的文件或目录
O_DIRECT直接 I/O 模式,即数据不缓存到系统内存中,直接读写磁盘EROFS只读文件
O_EXCL若文件存在,则open返回失败ETXTBSY是可执行文件
O_LARGEFILE在 32 位系统中打开大于2G的文件
O_NOCTTY不要让 pathname(所指向的终端设备)成为控制终端
O_NOFOLLOW对符号链接不予解引用
O_TRUNC覆写文件
O_APPEND追加文件
O_ASYNC异步IO
O_NONBLOCK以非阻塞方式打开
O_SYNC以同步方式写入文件

读取文件 read

函数原型:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
//成功返回读取字节数,失败返回-1

:::tip{title=“read的作用”}

  1. 按字符流(每次读取一个字符,先入先出顺序)读取count个字符;
  2. 读到\n时结束读取;
  3. 读到EOF时结束读取;
  4. 如果想把缓冲区内容交给printf输出,那么在read读取后,手动向缓冲区添加\0来结束字符串;
  5. 因为缓冲区可能比要读取的数据大,因此返回值可以比缓冲区小。
    :::

写入文件 write

函数原型:

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
//成功返回写入字节数,失败返回-1

关闭文件 close

函数原型:

int close(int fd);
//成功返回0,失败返回-1

:::tip{title=“close”}

  1. close并不会直接关闭文件,而是先减少引用计数;
  2. 当引用计数为0时,则关闭文件;
    :::

改变文件偏移量 lseek

函数原型:

#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
//成功返回新的偏移量,失败返回-1

:::tip{title=“where选项”}
SEEK_SET,相对文件头
SEEK_CUR,相对当前偏移量
SEEK_END,相对文件尾
:::

通用IO操作 ioctl

函数原型:

int ioctl(int fd, unsigned long request, ...);
//成功返回值取决于定义,失败返回-1

ioctl就是自定义操作,可以自己制定请求编号代表的含义,实现非标IO。

改变文件属性 fcntl

函数原型:

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );
//成功返回值取决于cmd,失败返回-1

例如,改变函数的标志位:

int flags;
flags = fcntl(fd , F_GETFL);

//判断文件标志位
if(flags & O_SYNC){
    printf("设置了同步标志位");
}

//判断访问权限
int accessMode;
accessMode=flags & O_ACCMODE;
if(accessMode == O_RDONLY){
    printf("设置了只读权限");
}

//更改文件标志位
flags |= O_APPEND;
fcntl(fd,F_SETFL,flags);

:::tip{title=“逻辑操作的作用”}
&,判断标志位,如if (info_nd->wxflag & WX_OPEN)
|,设置标志位,info_nd->wxflag |= WX_OPEN
& ~,清除标志位,int wxflag = info_od->wxflag & ~WX_DONTINHERIT;
:::

复制文件描述符 dup

函数原型:

#include <unistd.h>

int dup(int oldfd);
//成功返回最小文件描述符,失败返回-1

int dup2(int oldfd, int newfd);
//成功返回新描述符,失败返回-1

dup2是dup和close组合的化简方式:

close(2);
newfd = dup(1);

:::tip{title=“提示”}
新旧文件描述符共享同一打开文件句柄所含的文件偏移量和状态标志。然而,新文件描述符有其自己的一套文件描述符标志,且其 close-on-exec 标志(FD_CLOEXEC)总是处于关闭状态。

dup2除了FD_CLOEXEC标志位不一样,还有什么不一样?
除了 FD_CLOEXEC 标志位不同之外,dup2 复制的新文件描述符和旧文件描述符在其他方面是完全相同的,比如指向同一个打开的文件,共享相同的文件位置偏移量,以及相同的文件状态标志等。
:::

参考链接

《Linux-Unix系统编程手册–第5章深入深究IO文件–5.5复制文件描述符》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多弗朗强哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值