Linux之基础IO

文章目录

  • 前言
  • 一、再谈文件
  • 二、再谈文件操作
  • 二、如何理解文件
    • 1.文件操作的本质
    • 2.管理被打开的文件
  • 三、进程和被打开的文件如何关联
  • 四、文件描述符fd
    • 1.引入
    • 2.理解
    • 3.分配规则
  • 五、重定向
    • 1.引入重定向
    • 2.接口
    • 3.追加重定向
    • 4.输入重定向
  • 总结


前言

本文介绍了系统IO、fd、重定向等内容。


一、再谈文件

  1. 空文件也要占磁盘空间;
  2. 文件 = 内容 + 属性;
  3. 文件操作 = 对内容 or 对属性 or 内容和属性;
  4. 表示一个文件,必须使用:文件路径 + 文件名(有且·仅有一个);
  5. 如果我们没有指定对应文件的路径,默认是在当前路径下对该文件进行访问;
  6. 当我们把fopen,fclose,fread,fwrite等接口写完之后,代码编译形成二进制可执行程序后,在还没有运行的情况下,文件对应的操作有没有起作用?
    答:没有,文件的访问是运行时进行访问的。因此,对文件的操作本质是,进程对文件的操作。
  7. 一个文件如果没有被打开,它可以直接被访问吗?
    答:不能!!!一个文件要被访问,必须先被打开fopen —— 文件访问 是 用户进程 + OS。 ——> 文件操作的本质是 : 进程和被打开文件的关系

二、再谈文件操作

  1. C语言有文件操作接口,C++有文件操作接口,Java有……这些语言都有文件操作接口,但是他们的操作接口都不一样。
    文件在哪里? ——在磁盘 ——是硬件 ——要通过OS访问
    所以,要访问磁盘不能绕过OS——要使用OS提供的接口——OS必定要提供文件级别的系统调用接口。注意:操作系统只有一个,在操作系统之上可以运行多种语言,而无论上层语言如何变化:
    (1)库函数底层必须调用系统调用接口;
    (2)库函数可以千变万化,但是底层不变。
    那么,如何降低学习成本呢?
    学习不变的东西(上层语言的库函数都是基于系统调用做的不同封装,系统调用就是不变的东西)。
  2. 操作C语言
    C语言中传标记位——int的一个标记位。
    但是int整数有32位比特位,因此我们可以通过比特位传递选项。那么如何使用比特位传递选项呢?——一个比特位一个选项,比特位不能重复。
    系统调用write是用来往文件中写内容的,其中
    在这里插入图片描述

那么是谁提供的文件读取的分类(文本类和二进制类)? 是语言本身。
库函数的接口是对系统调用进行了封装的结果。
在这里插入图片描述

二、如何理解文件

1.文件操作的本质

文件的本质是进程和被打开文件的关系

2.管理被打开的文件

进程可以打开多个文件,意味着系统中一定会存在大量的被打开的文件。这些被打开文件也是需要被操作系统管理起来的,我们知道管理的本质是先描述再组织,所以操作系统为了管理对应的被打开文件,必定是要为这些文件创建对应的内核数据结构来标识文件。这个内核数据结构就是struct file{}结构体(注意:这与C语言的FILE没有关系),包含文件的大部分属性。

三、进程和被打开的文件如何关联

通过文件打开(open)的返回值和文件描述符进行联系。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 #include<assert.h>
  7 #include<string.h>
  8 #define FILE_NAME(number) "log.txt"#number
  9 int main()
 10 {
 11         umask(0);
 12         int fd0 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 13         int fd1 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 14         int fd2 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 15         int fd3 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 16         int fd4 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 17         printf("fd: %d\n", fd0);
 18         printf("fd: %d\n", fd1);
 19         printf("fd: %d\n", fd2);
 20         printf("fd: %d\n", fd3);
 21         printf("fd: %d\n", fd4);
 22         close(fd0);
 23         close(fd1);
 24         close(fd2);
 25         close(fd3);
 26         close(fd4);
 27         return 0;
 28 }

在这里插入图片描述
为什么文件描述符fd是从3开始的?

四、文件描述符fd

1.引入

看到上面的结果,open的返回值为什么从3开始的,那么0,1,2到哪里去了呢?
我们可以发现文件描述符是连续的整数,说到连续的整数我们可以想到数组的下标就是数组的下标就是连续的整数。
C语言阶段,我们知道C程序会默认打开三个标准输入输出流:stdin(标准输入设备键盘)、stout(输出设备显示器)、stderr(显示器)
对于C语言的FILE,我们其实不是很熟悉。C语言的FILE究竟是什么?它本质上是一个结构体。访问文件时,是用底层的open系统调用接口,它访问文件需要用到文件描述符,在C语言中,我们访问文件用的是FILE而不是文件描述符,因此可以推测出,FILE中必定有一个文件描述符的字段。所以C语言不仅在接口上存在封装,它的数据结构FILE也有封装。
当然上面都是我们的推测,我们可以实际看一下标准输入标准输出和标准错误它们的文件描述符具体是多少:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 #include<assert.h>
  7 #include<string.h>
  8 #define FILE_NAME(number) "log.txt"#number
  9 int main()
 10 {
 11         printf("stdin -> fd :%d\n", stdin -> _fileno);
 12         printf("stdout -> fd :%d\n", stdout -> _fileno);
 13         printf("stderr -> fd :%d\n", stderr -> _fileno);
 14         umask(0);
 15         int fd0 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 16         int fd1 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 17         int fd2 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 18         int fd3 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 19         int fd4 = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_APPEND, 0666);
 20         printf("fd: %d\n", fd0);
 21         printf("fd: %d\n", fd1);
 22         printf("fd: %d\n", fd2);
 23         printf("fd: %d\n", fd3);
 24         printf("fd: %d\n", fd4);
 25         close(fd0);
 26         close(fd1);
 27         close(fd2);
 28         close(fd3);
 29         close(fd4);
 30         return 0;
 31 }    

在这里插入图片描述
可以看到,0,1,2是默认被stdin,stdout,stderr占用,可以反推我们上面理论是正确的。

2.理解

为什么文件描述符是0,1,2,3……这些整数?它的本质是什么?
文件描述符的本质是数组的下标
在这里插入图片描述
文件是存储在磁盘上,要操作某个文件就要打开该文件,即,将该文件的相关属性信息从磁盘加载到内存中。操作系统中存在着大量的进程,一个进程可以打开多个文件,因此操作系统要将被打开的文件管理起来,还要将每个进程与它打开的文件的关系管理起来。
如何管理?先描述,再组织。
OS为了管理被打开的文件,构造了struct file结构体用来描述被打开的文件,为了管理进程与文件之间的联系,进程创建了struct file_struct结构,里面包含了struct file* fd_array[]指针数组,它存储了描述被打开文件的结构体对象的地址,将进程对应的struct file_struct对象存放在进程的PCB中。
这就是为什么文件操作符读到的是连续的整数,因为文件操作中用来标记进程与文件间的关系的就是文件描述符表,用数组标定文件内容。

3.分配规则

文件描述符本质上就是数组的下标。文件描述符是如何进行分配的呢?

要关闭fd对应的文件,需要调用close,即close(1)就是关闭fd = 1对应的文件。
在这里插入图片描述

先看下面这个例子:
我们知道了,系统启动后,默认会打开0,1,2这三个文件符对应的文件,如果我们将这三个中的其中一个关闭,然后去打开新的文件,这个新文件对应的fd会是多少呢?
文件test.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 int main()
  7 {
  8         close(1);
  9         umask(0);
 10         int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
 11         if(fd < 0)
 12         {
 13                 perror("open");
 14                 return 1;
 15         }
 16         printf("open fd : %d\n", fd);
 17         return 0;
 18 }

运行:
在这里插入图片描述
可以看到,当我们将fd = 1对应的文件关闭,此时fd = 1就没有被使用,因此新打开的文件就占用了1。

为什么这里.test没有直接打印出结果呢?
答:fd = 1对应的是标准输出,即我们将要打印的内容写到标准输出。此时,我们关闭了1,将fd = 1重新分配给文件log.txt,则要打印出来的内容就会写入文件log.txt。(close系统调用改变的是fd与文件的对应关系,而上层调用的fd = 1并不改变,即上层不知道fd = 1被改变了,所以还是将fd = 1认为成标准输出)就导致了./test不能打印出结果,结果被写入到文件log.txt中。

分配规则是:从小到大,按照顺序寻找最小的且没有被占用的fd
如果我们没有关闭1,则新打开文件对应的fd就是3:
文件test.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 int main()
  7 {
  8         umask(0);
  9         int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
 10         if(fd < 0)
 11         {
 12                 perror("open");
 13                 return 1;
 14         }
 15         printf("open fd : %d\n", fd);
 16         return 0;
 17 }

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

五、重定向

1.引入重定向

上面的将fd=1对应的文件关闭,然后将fd = 1重新分配给新文件的例子,就是一个重定向的过程。
关于重定向,我们最先接触的是>输出;>>追加;<输入
重定向最经典的特征就是在上层调用不变的情况下,改变底层的数组内容的指向。比如:调用fwrite(stdout,…);无论底层的指向如何改变,上层都会用到stdin(标准输入),stdout(标准输出),stderr(标准错误)对应的文件描述符0,1,2.当我们将3号描述符对应的文件指针赋值给1号文件描述符时,1号文件描述符就指向了3号对应的文件。
重定向的本质就是,上层使用的fd不变,在内核中修改了fd对应的struct_file*的地址。

2.接口

dup2:在这里插入图片描述

dup2的作用是在两个文件描述符之间进行拷贝(拷贝的不是文件描述符本身,而是它们在文件描述符表中所对应的文件指针)
dup2的参数中oldfd和newfd,dup2一旦重定向后,留下的只有oldfd(将oldfd所对应的文件指针,拷贝给newfd对应的数组内容)
输出重定向
例子:
将原来打印在显示器上的内容,写入文件log.txt
文件test.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 int main()
  7 {
  8         umask(0);
  9         int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
 10         if(fd < 0)
 11         {
 12                 perror("open");
 13                 return 1;
 14         }
 15         dup2(fd,1);
 16         printf("open fd : %d\n", fd);
 17         fprintf(stdout, "open fd : %d\n", fd);
 18         return 0;
 19 }

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

3.追加重定向

在打开文件的时候不要情况原来的内容,即将O_TRUNC改为O_APPEND即可。
文件test.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 int main()
  7 {
  8         umask(0);
  9         //int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
 10         int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
 11         if(fd < 0)
 12         {
 13                 perror("open");
 14                 return 1;
 15         }
 16         dup2(fd,1);
 17         printf("open fd : %d\n", fd);
 18         fprintf(stdout, "open fd : %d\n", fd);
 19         return 0;
 20 }

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

4.输入重定向

前提条件是文件必须存在。stdin -> 0,dup(fd,0);//输入重定向文件
文件test.c

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<unistd.h>
  5 #include<fcntl.h>
  6 int main()
  7 {
  8         umask(0);
  9         //int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
 10         //int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
 11         int fd = open("log.txt", O_RDONLY);
 12         if(fd < 0)
 13         {
 14                 perror("open");
 15                 return 1;
 16         }
 17         dup2(fd,0);
 18         char line[64];
 19         while(1)
 20         {
 21                 printf(">");
 22                 if(fgets(line, sizeof(line), stdin) == NULL) break;
 23                 printf("%s", line);
 24         }
 25         close(fd);
 26         return 0;
 27 }

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


总结

以上就是今天要讲的内容,本文介绍了Linux中基础IO的相关概念。本文作者目前也是正在学习Linux的相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。
最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/25890.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】在Ubuntu中卸载、下载mysql以及如何检查mysql是否卸载成功

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

micropython固件编译——把自己的py库添加进固件

目录 0. 前言1. 编写自己库的代码2. 移植库3. 验证 0. 前言 本节编译自己写的py库&#xff0c;增强移植性&#xff0c;往后烧录自己的固件即可轻易移植代码 没装好环境或者没有基础可以先看看这个&#xff1a; Ubuntu下ESP-IDF的环境搭建 Ubuntu下编译esp32micropython固件编…

windows下上架iOS应用到appstore

windows下上架iOS应用到appstore 背景步骤申请苹果开发者账号创建唯一标示符App IDs申请发布证书申请发布描述文件创建App并填写信息选择证书编译打包上传IPA到App Store提交审核 尾巴 背景 现在由于跨平台技术的兴起&#xff0c;不使用原生技术就能开发出Android和iOS应用。A…

一些关于c++的琐碎知识点

目录 bool强转 const构成重载:const修饰*p 移动构造 new int (10)所做的四件事 this指针---为什么函数里面需要this指针&#xff1f; .和->的区别 new创建对象 仿函数 new和malloc的区别 c系统自动给出的函数有 delete和delete[ ]区别何在 检查有没有析构函数 e…

BTC API:如何在比特币网络上创建应用程序?

比特币是一种去中心化的数字货币&#xff0c;可以通过比特币API与比特币网络进行交互。比特币API是一组允许开发人员与比特币网络进行交互的编程接口&#xff0c;可以帮助开发者构建各种比特币应用程序。 比特币API可以用于创建区块浏览器、钱包和比特币支付。其中利用比特币A…

Android-Activity生命周期

文章参考&#xff1a;添加链接描述 文章参考&#xff1a;添加链接描述 五大状态 StartingRunningStoppedPausedDestroyed 借用一张已经包浆的图 PS&#xff1a;Running和Paused是可视阶段&#xff0c;其余都是不可视 几大函数 onCreate&#xff1a;通过setContentLayout初始…

Markdown笔记应用程序Note Mark

什么是 Note Mark Note Mark 是一种轻量、快速、简约&#xff0c;基于网络的 Markdown 笔记应用程序。具有时尚且响应迅速的网络用户界面。 安装 在群晖上以 Docker 方式安装。 ghcr.io 镜像下载 官方的镜像没有发布在 docker hub&#xff0c;而是在 ghcr.io&#xff0c;所以…

iptables和防火墙

文章目录 1.防火墙2.Iptables基本介绍2.1 什么是iptables2.2 什么是包过滤防火墙2.3 包过滤防火墙如何实现 1.防火墙 Linux防火墙主要工作在网络层&#xff0c;针对 TCP/IP 数据包实施过滤和限制&#xff0c;典型的包过滤防火墙&#xff0c;基于内核编码实现&#xff0c;具有非…

什么是低代码开发?低代码开发可以解决哪些问题?

一、什么是低代码开发&#xff1f; 低代码可以理解为是一种全新的应用开发理念。主要以可视化、参数化的系统配置方式来进行程序应用的开发&#xff0c;因此可以大幅度减少代码编写的工作&#xff0c;从而提高开发效率。 低代码平台则是通过对于业务场景进行高度抽象、提炼&…

unittest单元测试框架

背景 unittest也称为PyUnit&#xff0c;是借鉴Java中JUnit框架产生。 unittest使我们具备创建测试用例、测试套件、测试夹具的能力。 组成 Test Fixture&#xff08;测试夹具&#xff09;&#xff1a;可以定义在单个或多个测试执行之前的准备工作和测试执行之后的清理工作。…

【P42】JMeter 运行时间控制器(Runtime Controller)

文章目录 一、运行时间控制器&#xff08;Runtime Controller&#xff09;参数说明二、测试计划设计 一、运行时间控制器&#xff08;Runtime Controller&#xff09;参数说明 可以通过时间来确定其后代元素运行多长时间&#xff0c;在时间范围内&#xff0c;后代元素会一直运…

八、(重点)视图集ModelViewSet自定义action路由routers

上一章&#xff1a; 七、Django DRF框架GenericAPIView--搜索&排序&分页&返回值_做测试的喵酱的博客-CSDN博客 下一章&#xff1a; 九、DRF生成API文档_做测试的喵酱的博客-CSDN博客 1、View 最基础的视图类View 2、views.APIView class APIView(View): …

kafka 的内部结构和 kafka 的工作原理

基本设置 让我们开始安装kafka。下载最新的 Kafka 版本并解压缩。打开终端并启动 kafka 和 zookeeper。 $ cd $HOME $ tar -xzf kafka_<version>.tgz $ cd kafka_<version> $ bin/zookeeper-server-start.sh config/zookeeper.properties # open another termina…

Canvas画布基本功能及实现网页签名功能

canvas 简介 <canvas> 是 HTML5 新增的&#xff0c;一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画&#xff0c;甚至可以进行实时视频处理和渲染。和所有 DOM 元素一样&#xff0c;拥有自己…

Elasticsearch:数据是如何被写入的?

在我之前的文章 “Elasticsearch&#xff1a;索引数据是如何完成的”&#xff0c;我详述了如何索引 Elasticsearch 的数据的。在今天的文章中&#xff0c;我将从另外一个视角来诠释如何写入数据到 Elasticsearch。更多关于 Elasticsearch 数据操作&#xff0c;请阅读文章 “Ela…

实体店引流获客系统模式开发详解

随着互联网的日益发展&#xff0c;实体店的处境变得越来越艰难&#xff0c;获客难和销量差成为了实体店最头疼的两大问题。面对这种情况&#xff0c;一味固步自封是不行的&#xff0c;最好还是顺应潮流&#xff0c;结合一款合适的商业模式&#xff0c;来帮助自己快速引流获客和…

深入理解设计原则之接口隔离原则(ISP)【软件架构设计】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 LSP&#xff1a;接口隔离原则 系列文章目录1、接口隔离原则的定义和解读2、案例解读3、如何判断一个接口是否符合接口隔离原则&#xff1f;小结 1、接口隔离原则的定义和…

canal server 标准化集群搭建(完结)

4.2. 创建 server 所属集群&#xff1a;选择刚才添加的 “集群名称” server 名称&#xff1a; server_1、server_2、server_3 依次类推 server ip&#xff1a;server 的 ip 地址 admin 端口&#xff1a;canal server 与 canal admin 的通信端口&#xff0c;非生产环境从 2…

云南LED、LCD显示屏系统建设,户外、室内广告大屏建设方案

LED大屏幕显示系统是LED高清晰数字显示技术、显示单元无缝拼接技术、多屏图像处理技术、信号切换技术、网络技术等科技手段的应用综合为一体&#xff0c;形成一个拥有高亮度、高清晰度、技术先进、功能强大、使用方便的大屏幕投影显示系统。通过大屏幕显示系统&#xff0c;可以…

3.1 矩阵连乘问题

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 学习目标&#xff1a; 如果我要学习动态规划中的矩阵连乘问题&#xff0c;我会采取以下学习方法&#xff1a; 1. **理解问题的背景和目标&#xff1a;首…