当前位置: 首页 > article >正文

Linux:基础IO

目录

文件的理解

open函数

文件描述符

0 & 1 & 2

重定向

dup2系统调用

缓冲区

缓冲区的概念

为什么要引入缓冲区机制?

缓冲类型


文件的理解

文件 = 文件内容 + 文件属性

文件是存储在磁盘中的,磁盘是外设,磁盘上文件所有的操作本质都是对外设的输入和输出,简称IO

Linux下一切皆文件

所有的文件操作本质是文件内容操作和文件属性操作

open函数

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

pathname表示要打开或创建的目标文件

flags表示打开文件时传入需要的参数,具体如下:

  • O_RDONLY:只读打开
  • O_WRONLY:只写打开
  • O_RDWR:读,写打开
  • (以上只能且必须三选一)
  • O_CREAT:若文件不存在,则创建。创建的文件权限需要用到mode选项
  • O_APPEND:追加写

mode表示若创建文件则权限的大小

返回值:

  • 成功:新打开的文件描述符
  • 失败:-1

mode_t类型是一个整数类型

文件描述符

文件描述符fd就是一个小整数,从0开始

0 & 1 & 2

Linux进程默认情况下会有3个缺省打开的文件描述符fd,分别是标准输入0,标准输出1,标准错误2

0,1,2分别对应的物理设备为:键盘,显示器,显示器

当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了file结构体

每个进程都有一个*files指针,它指向了一张表files_struct,该表包含了一个指针数组,这个指针数组每一个元素都是指向打开文件的指针,本质上文件描述符就是该指针数组的下标,这样有文件描述符就能找到相应的文件了

文件描述符的分配规则:

在flies_struct数组中,找到当前没有被使用的最小的一个下标,作为新的文件描述符

所以0,1,2关闭后是可以被替代的,这也是重定向的原理

重定向

常见的重定向有:>,>>,<

 

我们正常的echo后面的内容是直接打印到显示器上

但是当我们重定向后显示器上就没有打印该文字了,而是将文字直接写到了重定向的文件中

本质上是把文件描述符1关闭,变成了我们新创建的test.txt文件,这样只要是即将在显示器1号文件描述符上输出的就都会写到test.txt文件中

dup2系统调用

#include <unistd.h>

int dup2(int oldfd, int newfd);

 我们在代码中也想重定向就可以使用dup2系统调用

echo "hello world" > test.txt就相当于dup2(fd, 1); fd表示test.txt的文件描述符,1是我们要替换的文件描述符

缓冲区

缓冲区的概念

缓冲区是内存空间的一部分,在内存空间中预留了一定的空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫缓冲区

缓冲区分为输入缓冲区和输出缓冲区

为什么要引入缓冲区机制?

如果没有缓冲区,直接通过系统调用对磁盘进行操作,那么每一次对文件进行一次读或写的时候都需要调用读写系统调用来处理,执行一次系统调用将涉及到CPU状态的切换,即从用户空间切换到内核空间,实现进程上下文的切换,这样频繁的磁盘访问对程序的执行效率会造成很大的影响

为了解决以上问题,我们就可以引入缓冲区机制,我们可以一次从文件中读出大量的数据到缓冲区中,这部分的访问都不需要系统调用,等缓冲区的数据读取完成后再去磁盘中读取,这样可以极大的减少调用系统调用的次数,大大提高了计算机的运行速度

缓冲类型

标准I/O提供了3种类型的缓冲区

  • 全缓冲区:这种缓冲方式要求填满整个缓冲区后才进行I/O系统调用操作。对于磁盘文件的操作通常使用全缓冲的方式访问。这也是效率最高的一种类型
  • 行缓冲区:当输入和输出种遇到换行符时,标准I/O库函数将会执行系统调用操作。当所操作的流涉及一个终端时,使用行缓冲方式
  • 无缓冲区:无缓冲区是指标准I/O库不对字符进行缓存,直接调用系统调用

如果我们想强制刷新缓冲区,可以使用fflush强制刷新缓冲区

fflush(stdout);

有如下代码: 

#include <stdio.h>
#include <string.h>

int main()
{
    const char *msg0="hello printf\n";
    const char *msg1="hello fwrite\n";
    const char *msg2="hello write\n";

    printf("%s", msg0);
    fwrite(msg1, strlen(msg0), 1, stdout);
    write(1, msg2, strlen(msg2));

    fork();

    return 0;
}

运行结果:

hello printf
hello fwrite
hello write

但当我们重定向到file文件后,结果变成了:

hello write
hello printf
hello fwrite
hello printf
hello fwrite

这是因为:

printf和fwrite这些库函数会自带缓冲区,当重定向到普通文件时,缓冲方式由行缓冲变成了全缓冲,所以我们放在缓冲区的数据并不会被立即刷新

当我们fork的时候,父子数据会发生写时拷贝,父进程准备刷新的时候,子进程也具有相同的数据(缓冲区),所以就刷新了两份数据到内核级缓冲区中,write没有变化,write是没有缓冲区的

库函数在系统调用的上层,是对系统调用的封装


完 


http://www.kler.cn/a/463499.html

相关文章:

  • 设计心得——流程图和数据流图绘制
  • UE5 Debug的一些心得
  • Ethernet 系列(12)-- 基础学习::SOME/IP
  • 被催更了,2025元旦源码继续免费送
  • python中常用的内置函数介绍
  • 深入解析Android JNI:以Visualizer为例
  • HTML入门教程|| HTML 基本标签(2)
  • 第 28 章 - ES 源码篇 - Elasticsearch 启动与插件加载机制解析
  • 【每日学点鸿蒙知识】输入框光标显示问题、web组件回调async问题、图片加载流程监控、背景图片大小不生效问题、alert无效
  • 像素的访问和算术运算
  • 【R语言】校准曲线,绘制原理
  • 游戏关卡设计方法的杂感
  • 【Unity3d】C#浮点数丢失精度问题
  • 如何查询快手IP归属地?如何关闭
  • HTML——46.制作课程表
  • 鸿蒙应用开发 - 如何去掉字符串中空格
  • 使用 `Celery` 与 `RabbitMQ` 实现异步任务队列:构建高效、可靠的任务调度系统
  • 深度学习在光学成像中是如何发挥作用的?
  • [创业之路-222]:波士顿矩阵与GE矩阵在业务组合选中作用、优缺点比较
  • 如何通过深度学习提升大分辨率图像预测准确率?
  • Ajax阶段总结(二维表+思维导图+四种请求方式)
  • 数据库概念(MySQL第一期)
  • MongoDB 固定集合
  • AWTK 在 ESP 上的移植笔记
  • quasar v2 setup语法中报错: undefined is not an object (evaluating ‘this.$q.notify‘)
  • 使用 Actix-Web、SQLx 和 Redis 构建高性能 Rust Web 服务