三、文件I/O相关接口

本文详细介绍了Unix系统中的文件I/O操作,包括open、read、write、lseek等函数的使用,以及文件描述符、原子操作、文件共享等内容。通过示例代码解释了如何进行文件操作,并探讨了不带缓冲与带缓冲I/O的区别。此外,还讲解了dup、dup2、sync、fsync和fdatasync等函数的作用。
摘要由CSDN通过智能技术生成

文件 I/O

注意:文章中使用到的源码都在我的 github 中找到,传送门:SourceCode

前言

  • 本章主要讲解了文件 I/O 操作的一些函数–打开文件、读文件、写文件等等。讲解了大多数 Unix 文件 I/O 操作主要需要五个函数:open、read、write、lseek 以及 close。而这些函数都是不带缓冲 I/O (Unbuffer I/O),不带缓冲指的是每个 read 或者 write 都会调用一个内核中的系统调用。其次讲解了多进程间的数据共享和原子操作。
    • 不带缓冲 I/O 操作与带有缓冲 I/O 操作的区别:
      • 内核存储缓冲区:以 write 与 fwrite 为例,前者为不带缓冲 I/O ,而后者则带有缓冲。首先要明确一个概念就是当我们向文件写入数据时无论调用这两个哪个接口最终都会将数据写到 Unix 系统在内核中设定的一个缓冲存储器,只有到这个缓冲存储器满了或者内核需要对其刷新时,才会真正的把数据排入输出队列写到文件中去。
      • 假设内核缓存时 100 个字节,调用 write 每次写 10 个字节,当写第 10 次时,才会导致内核缓存区满从而将数据写到硬盘中,而在这个过程中相当于足足调用了 9 次系统调用却没有达到实质的效果,反而增加了用户态与内核态切换的开销。而带有缓冲 I/O 操作的 fwrite 会维护一个处于用户态的缓冲区,它会优化调用系统接口的频率,减少多余开销。

文件描述符

  • 文件表述符主要通过 open() 或者 creat() 函数返回。注意这个 creat 没有 e,主要还是 Unix 历史原因,感兴趣的可以去百度下。

在这里插入图片描述

对于内核来说,想要使用 read、write 等对文件的操作必须通过文件描述符,它是一个非负整数,每个程序都有一个自己的文件描述符表,从 0 开始每次 +1,一般每个进程对会有 shell 默认的 0,1,2 三个文件描述符,分别是标准输入、标准输出、标准错误输出。当进程再通过 open 函数打开文件返回的文件描述符就是 3。

一般魔数 0、1、2 都应该使用宏定义 STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO。这些宏定义在头文件 <unistd.h> 中。

/* Standard file descriptors.  */
#define STDIN_FILENO    0   /* Standard input.  */
#define STDOUT_FILENO   1   /* Standard output.  */
#define STDERR_FILENO   2   /* Standard error output.  */
  • 有一个问题,当进程同时打开同一个文件两次会发生什么?
    • 答案就是你会得到两个文件描述符,因此在使用完成时要注意分别对两个文件描述符 close(),更有意思的是,这两个文件描述符的 seek 都是不同的,详情可以参考这篇BLOG,有一点需要注意的是,当你在父进程打开一个文件后,fork 出来的子进程由于是将父进程所有资源都复制的所以也包含这个文件描述符,注意关闭。

open 函数

#include <fcntl.h>
int open(const char *pathname,int flags,.../*, mode_tmode*/) ;
int openat(int dirfd, const char *pathname, int flags,.../*, mode_tmode*/);
//返回:若成功为文件描述符,若出错为- 1

openat 函数的目的是加强路径的选择,可以使用相对路径来进行操作。

example_openat

首先通过 open 上一级目录文件,然后将目录文件描述符传递给 openat 并创建一个 stderr.out 文件写入数据。同时再用 openat 再次打开文件观察文件描述符增长规律。

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>


int main()
{
   

    char* dir_path = "./..";
    char* relative_path = "stderr.out";
    char buf[20] = {
   "0"};
    int dir_fd;
    int fd;
    int flags;
    int fd1;
    mode_t mode;

    dir_fd = open(dir_path, O_RDONLY);
    if (dir_fd < 0)
    {
   
        perror("open");
        exit(EXIT_FAILURE);
    }

    flags = O_CREAT | O_RDWR;
    mode = 0777;
    fd = openat(dir_fd, relative_path, flags, mode);
    if (fd < 0)
    {
   
        perror("openat");
        exit(EXIT_FAILURE);
    }

    if (write(fd, "HELLO", 5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值