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

【Linux】基础 IO(一)

📝前言:

这篇文章我们来讲讲Linux——基础IO主要包括:

  1. 文件基本概念
  2. 回顾 C文件的操作
  3. 介绍系统关于文件的基本操作

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


目录

  • 一,基本概念
    • 1. 概念
    • 2. 系统角度
  • 二,C 文件的 IO
    • 1. 打开操作
    • 2. 写操作
    • 3. 读操作
    • 4. 默认打开的流
  • 三,系统文件的 IO
    • 1. 位标记传递
    • 2 系统调用 open
    • 3 读写的系统调用
      • 使用示例
      • 库函数和系统调用
    • 4 文件描述符(重点)
      • 文件描述符的分配原则

一,基本概念

1. 概念

  • 狭义:
    • 文件在磁盘上
    • 磁盘是永久性存储物质
    • 磁盘是外设,即是输入设备,也是输出设备
    • 对文件的操作,其实是对外设的输入和输出,称为 IO
  • 广义:
    • Linux 下⼀切皆文件
    • 文件 == 内容 + 属性

2. 系统角度

  • 对⽂件的操作本质是进程对⽂件的操作
  • 硬件(如磁盘)的管理者是操作系统
  • ⽂件的读写本质不是通过 C 语⾔ / C++ 的库函数来操作的(这些库函数只是为用户提供⽅便),底层是通过文件相关的系统调用接口来实现的

二,C 文件的 IO

1. 打开操作

fopen

  • FILE *fopen(const char *pathname, const char *mode);
    • pathname:文件路径
    • mode:打开方式
  • 返回:
    • 成功:返回文件指针
    • 失败:返回NULL

这个FILE的类型是一个文件结构体struct file,通过typedef来的

2. 写操作

写操作,在文件不存在的时候都会创建文件。

打开文件时的写入模式:

  • 清空写w:清空文件内容,重新写
  • 追加写a:在原来文件内容后面追加写

fwrite

  • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    • ptr的数据写到stream
    • size:每个数据项的大小,比如写intsiezeof(int)(就是4
    • nmemb:数据项的个数
  • 返回:实际成功写入的数据项数量

注意:文件写入的时候不能写入'\0''\0'是C语言的东西,写入了以后是乱码

示例:

  5 // C 语言文件接口回顾6 int main()7 {8     FILE* fp1 = fopen("log1.txt", "w");9     FILE* fp2 = fopen("log2.txt", "a");10     if(fp1 == NULL) printf("log1.txt 打开失败\n");11     if(fp2 == NULL) printf("log2.txt 打开失败\n");12     char* str1 = (char *)"测试写入\n";13     fwrite(str1, strlen(str1), 1, fp1);14     fwrite(str1, strlen(str1), 1, fp2);15     fclose(fp1);16     fclose(fp2);17     return 0;18 } 

当使用相对路径的时候,进程通过自己记录的自己工作路径CWD信息,知道新建的文件要放在哪里

运行结果:
在这里插入图片描述

3. 读操作

读操作,如果文件不存在会报错
fread

  • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    • 不解释了,和fwrite的都一样,不一样的就是stream是读入的流,ptr是存储读入数据的地方。
    • 在上一次读取结束后文件指针的位置继续读取。
  • 返回:实际成功读取的数据项数量

示例:

 20 int main()21 {22     FILE* fp = fopen("log2.txt", "r");23     if(fp == NULL) printf("打开文件失败");24 25 26     char* str1 = (char *)"测试写入\n";27     char buff[1024];28     while(1)29     {30         // 这里每次读都是覆盖写buff的,但是我们及时打印出来                                                                                                                                                  31         int s = fread(buff, 1, sizeof(str1), fp); // 这里每次读一个char,方便后续设置 '\0'32         if(s > 0)33         {34             buff[s] = 0;35             printf("%s", buff);36         }37         if(feof(fp))38             break;39     }40     return 0;41 }

运行结果:
在这里插入图片描述

4. 默认打开的流

#include <stdio.h>
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

C默认会打开三个输⼊输出流,分别是标准输入:stdin, 标准输出:stdout, 标准错误:stderr

三,系统文件的 IO

头文件:<unistd.h>

1. 位标记传递

利用位图进行标记位传递,假设,现有标记位:ONE : 0001TWO:0010THREE:0100

  • 如果我们单独传入一个ONE就相当于传递了最低位的1
  • 如果我们希望传递第最低位的1和次低位的1,就可以传入ONE | TWO(通过获的方式)

2 系统调用 open

系统,通过系统调用open打开文件:
在这里插入图片描述

  • pathname:文件路径
  • falgs:选项(约等于C语言里面的打开模式),通过 或|来实现传递多个选项【这个选项本质是宏实现的标记位】
  • 常见选项:
    • O_RDONLY:只读打开
    • O_WRONLY: 只写打开
    • O_RDWR : 读,写打开
  • 上面这三个常量,必须指定⼀个且只能指定⼀个。
  • 其他选项
    • O_CREAT :若文件不存在,则创建它。需要使用mode选项(传入3个8进制数字),来指明新文件的访问权限。如果新建的时候,不传入,那对应的文件就是乱码【别忘了实际的权限要考虑umask的影响】
    • O_APPEND:追加写
  • 返回值
    • 成功:新打开的文件的文件描述符(系统通过这个来找到需要访问的文件)
    • 失败:-1

3 读写的系统调用

在这里插入图片描述
write

  • 参数
    • fd:文件描述符
    • buf:指向:要写入的数据,的缓冲区指针
    • count:要写入的字节数
  • 返回
    • 成功:实际写入的字节数
    • 失败:-1,并设置errno
  • 写入类型
    • 对于系统而言,没有什么文本写入和二进制写入(这是语言层的概念)
    • 你传什么指针,他就写什么,如果你原来数据是一个int a,指针传&a,则它就是按二进制写

在这里插入图片描述
readwrite一样,只是这里是读到buf

其余closelseek接口类似C语言

使用示例

以写入为例:

 45 int main()46 {47     int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); // 这个打开的效果就相当于 C 语言库函数的直接 mode 为 a48     const char* str = (char*)"hello world!\n";49     write(fd, str, strlen(str)); // 不写入 '\0'50     char fd_str[20];51     sprintf(fd_str, "%d\n", fd);  // 转换为文本52     write(fd, fd_str, strlen(fd_str));53     close(fd);54     return 0;55 } 

通常如果是read,则open就是用带两个参数的

运行结果:
在这里插入图片描述

库函数和系统调用

  • C语言的库函数fwritefread其实都是对底层系统调用writeread的封装,在上层为用户提供便利。
  • C语言的库函数具有可移植性,但是系统调用没有
    • 系统调用,如:Linux 的,用的是Linux这一套的。如果把代码移植到Windows或者其他操作系统就不行了
    • C语言,C的fwritefread是对系统调用的封装,但是它不只封装一份操作系统的,它会实现多个操作系统的。后续根据自己在哪个操作系统上使用,通过条件编译(裁剪),来调用对应的系统接口

4 文件描述符(重点)

什么是文件描述符?
如上图所展示的 3 就新打开的log.txt的文件描述符
0, 1, 2分别对应 stdin, stdout, stderr

那系统是如何通过文件描述符来找到要访问的文件的呢?
在这里插入图片描述

  • 在进程的task_struct里面有一个files指针,指向文件描述表files_struct
  • 在文件描述表中,有fd_array[]指针数组,数组的下标对应着文件描述符,指向对应的文件
  • 而每个文件被描述成一个结构体struct file(类似C语言的FILE),里面存储着文件的信息

有了文件描述符fd,进程就会在自己的文件描述表里面找对应下标的文件指针,然后就可以访问对应的文件了。

在这里插入图片描述

文件描述符的分配原则

files_struct数组当中,找到当前没有被使用的最小的⼀个下标,作为新的⽂件描述符。
比如,一开始有0, 1, 2

  • 直接创建一个新文件,则新文件fd == 3
  • close(1),再创建新文件,则fd == 1

注意:这里的关闭不是真关闭,只是“引用计数 - 1”。

  • 因为一个文件可以同时被多个进程访问
  • 同一个文件也可以被同一个进程的多个下标指针同时指向。比如stdoutstderr其实都是向显示器输出

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关文章:

  • 深入浅出之STL源码分析2_类模版
  • 实现三个采集板数据传送到一个显示屏的方案
  • 大模型(LLMs)强化学习——RLHF及其变种
  • Fabric系列 - SoftHSM 软件模拟HSM
  • Yocto项目实战经验总结:从入门到高级的全面概览
  • 从零开始跑通3DGS教程:(四)修改(缩放、空间变换)colmap生成的sfm结果
  • 数学相关使用笔记
  • Kubernetes 使用 containerd 实现 GPU 支持及 GPU Operator 部署指南
  • KNOWLEDGE-BASED SYSTEMS(KBS期刊)投稿经验分享
  • JavaScript基础-局部作用域
  • 深度学习篇---MediaPipe 及其人体姿态估计模型详解
  • 加速pip下载:永久解决网络慢问题
  • 动态规划之完全背包问题
  • Day21 奇异值分解(SVD)全面解析
  • C++:this指针
  • 编译后的js文件如何跟进调试
  • 研发效率破局之道阅读总结(5)管理文化
  • AtCoder AT_abc405_d ABC405D - Escape Route
  • 使用FastAPI和React以及MongoDB构建全栈Web应用03 全栈开发快速入门
  • 每日脚本学习5.10 - XOR脚本
  • 5.19中国旅游日,上海56家景区景点限时门票半价
  • 水豚出逃40天至今未归,江苏扬州一动物园发悬赏公告
  • 深圳市政协原副主席王幼鹏被“双开”
  • 习近平会见古巴国家主席迪亚斯-卡内尔
  • 习近平会见塞尔维亚总统武契奇
  • 47本笔记、2341场讲座,一位普通上海老人的阅读史