Linux系统程序设计——文件IO

标准C的IO缓存类型

  • 全缓存
    • 要求填满整个缓存区后才能进行IO系统调用。对磁盘文件通常使用全缓存访问。
  • 行缓存
    • 设计一个终端时(例如标准输入和标准输出),使用行缓存
    • 行缓存满自动输出
    • 碰到换行符自动输出
  • 无缓存
    • 标准错误流stderr通常是不带缓存区的,这使得错误信息能够尽快显示出来
/**************************************************** 
 * Description: example1 行缓存案例:
 * 1、行缓存满自动输出,"--hello-- "行缓存未满,不会显示;
 * 2、碰到换行符自动输出,"--hello--\n"有换行服,可以显示;
 * 3、如果没加\n,执行到return 也会强制输出;
 *****************************************************/
void example1()
{
    printf("--hello--\n");
    while (1) {
        sleep(1);
    }
}

文件IO系统调用

常用函数

以下都是内核提供的不带缓存的函数,他们不是ANSI C的组成部分,但是POSIX的组成部分

  • open() 打开文件
    在这里插入图片描述
    open的flags参数:
    在这里插入图片描述

  • create() 创建文件

    在这里插入图片描述
    在这里插入图片描述

  • close() 关闭文件

    在这里插入图片描述
    在这里插入图片描述

  • read() 读取文件

    在这里插入图片描述
    在这里插入图片描述

  • write() 写入文件
    在这里插入图片描述在这里插入图片描述

    /***************************************************
     * Description:  example2 文件读写案例:
     * 1、从file1中读取数据;
     * 2、将读取的数据写入file2
    ****************************************************/
    #define BUFFER_LEN 1024
    void example2()
    {
        char buffer[BUFFER_LEN];
        int fd1, fd2;
        const char* filepath1 = "./../IoTest/file1.txt";
        const char* filepath2 = "./../IoTest/file2.txt";
        fd1 = open(filepath1, O_RDONLY);
        if (fd1 == -1) {
            perror("Open file1 fail! err");
            _exit(-1);
        }
        fd2 = open(filepath2, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND);
        if (fd2 == -1) {
            perror("Open file2 fail! err");
            _exit(-1);
        }
        ssize_t nreads;
        while (nreads = read(fd1, buffer, BUFFER_LEN)) {
            if (nreads < 0) {
                perror("Read file1 fail!  err");
            } else {
                if (write(fd2, buffer, nreads) == -1) {
                    perror("Write file2 error! err");
                }
            }
        }
    }
    
    
  • Iseek() 文件定位

在这里插入图片描述
在这里插入图片描述

文件描述符

  • 对于内核而言,所有打开的文件都由文件描述符引用。文件描述符是一个非负整数。

  • 在POSIX应用程序中,整数0、1、2被替换为STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,这些常数被定义在<unistd.h>

  • 文件描述符的范围是0~OPEN_MAX,LINUX最大为1024

  • 文件描述符与文件指针转换

    • FILE *fdopen(int fd,const char *mode);//文件描述符转指针
    • int fileno(FILE *stream);//文件指针转文件描述符

文件操作

文件IO的内核数据结构

  • 文件描述表
    • 文件描述符标志
    • 文件表项指针
  • 文件表项
    • 文件状态标志(读、写、追加、同步和非阻塞等状态标志)
    • 当前文件偏移量
    • i节点表项指针
    • 引用计数器
  • i节点
    • 文件类型和对该文件的操作函数指针
    • 当前文件长度
    • 文件所有者
    • 文件所在的设备、文件访问权限
    • 文件所在的设备、文件访问权限
    • 指向文件数据在磁盘上所在位置的指针等
      在这里插入图片描述

dup和dup2函数

在这里插入图片描述

//文件内容的复制
void copy(int fdin, int fdout)
{
    char buffer[BUFFER_LEN];
    ssize_t size;
    lseek(fdin, 0L, SEEK_SET);
    while ((size = read(fdin, buffer, BUFFER_LEN)) > 0) {
        if (write(fdout, buffer, size) != size) {
            perror("Write  error! err");
            _exit(-1);
        }
    }
}
/********************************************************
 * Description:实现cat 命令的功能
 * 并将重定向符号<和> 利用dup和dup2替换为+和-
 * 例:IO_test + temp.txt(+ 为输入重定项)
 *    IO_test - temp.txt(- 为输出重定项)
 * ******************************************************/
void example4(int argc, char* argv[])
{
    int fd_in, fd_out;
    int flag = 0;

    for (int i = 1; i < argc; i++) {
        if (!strcmp("+", argv[i])) {

            fd_in = open(argv[++i], O_RDONLY);
            if (fd_in < 0) {
                perror("Open file error!");
                _exit(-1);
            }
            //将标准输入重定向到文件,让STDIN_FILENO指向fd_in所代表的文件
            if (dup2(fd_in, STDIN_FILENO) != STDIN_FILENO) {
                perror("dup2 error!");
                _exit(-1);
            }
            close(fd_in);
        } else if (!strcmp("-", argv[i])) {

            fd_out = open(argv[++i], O_WRONLY | O_CREAT | O_TRUNC, 0777);
            if (fd_out < 0) {
                perror("Open file error!");
                _exit(-1);
            }
            //将标准输出重定向到文件
            if (dup2(fd_out, STDOUT_FILENO) != STDOUT_FILENO) {
                perror("dup2 error!");
                _exit(-1);
            }
            close(fd_out);
        } else {

            flag = 1;
            fd_in = open(argv[i], O_RDONLY);
            if (fd_in < 0) {
                perror("open error!");
                _exit(-1);
            }
            //让STDIN_FILENO指向fd_in所代表的文件
            if (dup2(fd_in, STDIN_FILENO) != STDIN_FILENO) {
                perror("dup2 error!");
                _exit(-1);
            }
            copy(STDIN_FILENO, STDOUT_FILENO);
        }
        if (!flag) {
            copy(STDIN_FILENO, STDOUT_FILENO);
        }
    }
}

fcntl函数

在这里插入图片描述

//设置文件标志位
void set_fl(int fd, int flag)
{
    //获得原来的文件状态标志
    int val = fcntl(fd, F_GETFL);
    //增加新的文件状态标志
    val |= flag;
    //重新设置文件状态标志(val为新的文件状态标志)
    if (fcntl(fd, F_SETFL, val) < 0) {
        perror("fcntl error");
    }
}

//清除文件标志位
void clr_fl(int fd, int flag)
{
    int val = fcntl(fd, F_GETFL);
    //清除文件标志位
    val &= ~flag;
    if (fcntl(fd, F_SETFL, val) < 0) {
        perror("fcntl error");
    }
}
/***************************************************************
 * Description:实现文件状态标志未的追加和清除
 * 1、对file1在open时不设置O_APPEND,使用set_fl设置O_APPEND,
 *    写入文件时应该在原有内容后面写入
 * 2、对file2在open时设置O_APPEND,使用clr_fl清除O_APPEND,
 *    写入文件时应该在原有内容前写入
 ***************************************************************/
void example5()
{
    int fd1, fd2;
    off_t current_location;
    const char* filepath1 = "./../IoTest/file1.txt";
    const char* filepath2 = "./../IoTest/file2.txt";
    fd1 = open(filepath1, O_WRONLY);
    if (fd1 == -1) {
        perror("Open file1 fail! err");
        _exit(-1);
    }
    set_fl(fd1, O_APPEND);
    char buffer[] = "\nset_fl\n";
    if (write(fd1, buffer, strlen(buffer) * sizeof(char)) == -1) {
        perror("Write file1 error! err");
    }
    fd2 = open(filepath2, O_WRONLY | O_APPEND);
    if (fd2 == -1) {
        perror("Open file1 fail! err");
        _exit(-1);
    }
    clr_fl(fd2, O_APPEND);
    char buffer2[] = "\nclr_fl\n";
    if (write(fd2, buffer2, strlen(buffer2) * sizeof(char)) == -1) {
        perror("Write file2 error! err");
    }
}

IO处理方式

  • IO处理的五种模型

    • 阻塞IO模型

      若所调用的I/O函数没有完成相关的功能就会使进程挂起,直到相关数据到达才会返回。如:终端、网络设备访问。

    • 非阻塞模型

      当请求的IO操作不能完成时,则不让进程休眠,而且返回一个错误。
      在这里插入图片描述

      /*******************************************************
       * Description:演示非阻塞IO
       * 1、不设置O_NONBLOCK时,会阻塞IO,
       *    等待从键盘输入后,才忘下执行
       * 2、设置 O_NONBLOCK后,不阻塞IO,sleep执行后,
       *    直接报错,读取失败,因为没有阻塞IO,没有从键盘上输入内容
       *******************************************************/
      void example6()
      {
          char buffer[4096] = { '\0' };
          ssize_t size = 0;
          //设置非阻塞IO
          set_fl(STDIN_FILENO, O_NONBLOCK);
          sleep(5);
      
          size = read(STDIN_FILENO, buffer, sizeof(buffer));
          if (size < 0) {
              perror("read error");
              _exit(-1);
          } else if (size == 0) { //读到文件尾部
              printf("read finished!\n");
          } else {
              if (write(STDIN_FILENO, buffer, size) != size) {
                  perror("write error");
                  _exit(-1);
              }
          }
      }
      
      
    • IO多路转接模型

      如果请求的IO操作阻塞,且他不是真正阻塞IO,而且让其中的一个函数等待,在这期间,IO还能进行其他操作,如select函数

    • 信号驱动IO模型

      在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动IO

    • 异步IO模型

      在这种模型下,当一个描述符已准备好,可以启动IO时,进程会通知内核。由内核进行后续处理,这种用法现在较少。

文中所涉及的例程,完整工程皆上传至下面链接中,需要自取
https://download.csdn.net/download/qq_45601625/85112008

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值