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

【Linux】 基础IO——文件(中)

文章目录

    • 1. 文件描述符为什么从3开始使用?
    • 2. 文件描述符本质理解
    • 3. 如何理解Linux下的一切皆文件?
    • 4. FILE是什么,谁提供?和内核的struct有关系么?
      • 证明struct FILE结构体中存在文件描述符fd
    • 5. 重定向的本质
      • 输出重定向
      • 输入重定向
      • 追加重定向
      • 重定向函数 ——dup2
    • 6. 如何理解缓冲区?

1. 文件描述符为什么从3开始使用?

修改test.c文件内容

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"        
int main()    
{    umask (0);//将权限掩码设置成0        int fd=open(LOG, O_RDONLY  );//打开一个文件,若文件不存在则重新创建一个        if(fd==-1)//说明打开失败        {    printf("fd:%d,errno:%d,errstring:%s\n",fd,errno,strerror(errno));//打印出错误信息        }    else    printf("fd :%d\n",  fd);                                                                                                                                                                  char buffer[1024];    ssize_t n= read(fd,buffer,sizeof(buffer)-1);//使用系统接口来进行IO的时候,一定要注意\0的问题    if(n>0)//成功了,实际读到了多少字节    {    buffer[n]='\0';    printf("%s\n",buffer);    }    close(fd); //关闭文件        return 0;    
} 

运行可执行程序,发现文件描述符返回的是3

但为啥是3,不是0 ,1,2
任何一个进程,在启动的时候,默认会打开当前进程的三个文件:
标准输入、标准输出、标准错误 ——本质都是文件
C语言:标准输入(stdin) 标准输出(stdout) 、标准错误(stderr) ——文件在系统层的表现
C++: 标准输入(cin) 标准输出(cout) 、标准错误(cerr) ——文件在系统层的表现,它是一个类


因为Linux下一切皆文件,所以向显示器打印,本质就是向文件中写入
标准输入—设备文件—>键盘文件
标准输出—设备文件—> 显示器文件
标准错误—设备文件—> 显示器文件


创建test.cc文件(cc后缀即cpp代码)

#include<iostream>      
#include<cstdio>//写C++时,使用C++风格的C语言代码      
int main()      
{      //C语言      printf("hello printf->stdout\n");//向stdout进行输出      fprintf(stdout,"hello printf->stdout\n ");//将数据向stdout进行输出      fprintf(stderr,"helllo printf->stderr\n");//将数据向标准错误打印      //C++      std::cout<<"hello cout->cout"<<std::endl;//表示标准输出      std::cerr<<"hello cerr->cerr"<<std::endl;//向标准错误中打印数据      return 0;      
} 

输出重定向是将标准输出重定向,此时log.txt文件中只会存在标准输出的内容
所以标准输出和标准错误都会向显示器打印,但是其实是不一样的

0默认对应标准输入,1默认对应标准输出、2默认对应标准错误


修改myfile.c文件内容

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"        
int main()    
{    
int fd1=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    
int fd2=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    
int fd3=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    
int fd4=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    
int fd5=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    
int fd6=open(LOG,O_WRONLY |O_CREAT | O_TRUNC,0666);    
printf("%d\n",fd1);    
printf("%d\n",fd2);    
printf("%d\n",fd3);    
printf("%d\n",fd4);    
printf("%d\n",fd5);    
printf("%d\n",fd6);                                                                                                                                                                         return 0;    
}

运行可执行程序,发现 打印结果为 3 4 5 6 7 8
因为 标准输入、标准输出、标准错误分别占用了0 、1、2,所以只能从3开始

文件描述符(open对应的返回值)本质就是数组的下标

2. 文件描述符本质理解

在这里插入图片描述
启动代码时就会变成一个进程,该进程在内核中就必须有自己的数据结构 struct task_struct,
称之为当前进程所对应的进程描述符
打开文件时,操作系统会把文件加载到内存里,以供CPU通过进程的方式来访问对应的文件

任何一个进程,在启动的时候,默认会打开进程的三个文件,系统中一定会存在大量被打开的文件,这些文件一定会被操作系统管理起来,通过先描述,在组织,创建 struct file 结构体,该结构体一定包含文件属性等,每一次创建并打开文件时,都是在内核中创建一个struct file的结构体

目前认为只要找到file,就可以找到所有文件内容
为了维护一个进程和多个文件的映射关系,在内核中定义了数据结构struct files_struct,该结构体内部有一个数组struct file* fd [ ] ,是一个内容为struct file*的数组
当进程初始化时,会创建struct files_struct 结构体,通过结构体找到数组,只要有数组一定有下标

3. 如何理解Linux下的一切皆文件?

在这里插入图片描述
内存把数据写到显示器上,属于写入的过程,读取是从键盘中读取的,键盘输入后,操作系统把输入的数据回显到显示器上了,所以显示器只能负责打印

不同的硬件所对应的方法是完全不一样的,打开键盘时,操作系统内部会创建struct file对象

将键盘的read方法和 write方法 保存到函数指针中

每一个设备也只需要把方法的地址放入函数指针中
在当前进程看来,所有的东西都是文件对象,要有数据放到缓冲区里,底层读写时只需要调用对应的方法,来完成对应的读写,不关心底层的差异化
操作系统也有自己的wirte和read,本质上是拷贝,将应用层的数据拷贝到缓冲区里,在调用底层不同设备的方法,所以看起来就是Linux下一切皆文件

4. FILE是什么,谁提供?和内核的struct有关系么?

操作系统层面,必须要访问fd,才能找到文件
任何语言层访问外设或者文件必须经历操作系统
FILE *fopen(const char *path, const char *mode);

FILE是一个结构体,FILE由C语言提供的


C语言动态库


C语言头文件
在这里插入图片描述

证明struct FILE结构体中存在文件描述符fd

#include<sys/types.h>      
#include<sys/stat.h>      
#include<fcntl.h>      
#include<stdio.h>      
#include<unistd.h>      
#include<errno.h>      
#include<string.h>      
#define LOG "log.txt"          
int main()      
{    
printf("%d\n",stdin->_fileno);//fileno代表文件描述符    
printf("%d\n",stdout->_fileno);    
printf("%d\n",stderr->_fileno);    
FILE*fp=fopen(LOG,"w");    
printf("%d\n",fp->_fileno);                                                                                                                                                                 return 0;                                                                                                                                           
}   

说明结构体struct FILE内部存在文件描述符
同时因为0 1 2 被占用了,所以我们自己写的文件描述符返回3

5. 重定向的本质

关闭文件描述符0后,发现从0开始可以被输出了


关闭文件描述符0和2后,发现0和2都可以被使用了


进程中,文件描述符的分配规则:在文件描述符表中,最小的,没有被使用的数组元素分配给新文件

输出重定向

若不关闭文件描述符1,当前printf打印的结果显示到显示器上面


关闭文件描述符1,再打开新的文件log.txt


此时运行可执行程序没有显示出you can see me,打开新文件发现本来应该打印到显示器的内容,打印到log.txt中了

本来应该打印到显示器上的内容,打印到文件里 ,这种现象叫做重定向


在这里插入图片描述

在文件描述符表中,最小的,没有被使用的数组元素分配给新文件,所以把文件描述符1分配给了log.txt

1号下标里面的地址填成了log.txt文件的地址,上层printf打印它知道吗?
不知道,它也不关心,它只认文件描述符1

重定向的原理:在上层无法感知的情况下,在OS内部 ,更改进程内部对应的文件描述符表中,特定下标的指向

输入重定向

先在log.txt文件中输入内容 123 456
修改myfile.txt文件内容

关闭文件描述符0,所以scanf读取时会读取log.txt文件中的内容

读取的内容与log.txt文件内容相同


本来要从键盘中读取,结果现在要在文件中读取,这叫做输入重定向

在这里插入图片描述

追加重定向

关闭文件描述符1后,导致printf不会打印在显示器上,而是追加到log.txt文件中

运行可执行程序,无显示,都追加到log.txt文件中


重定向函数 ——dup2

输入 man dup2 查看
在这里插入图片描述

刚刚重定向时,需要先关闭文件描述符1,再打开文件
现在可以直接将文件打开,使用dup2重定向
输出重定向对应的文件描述符是1
打开myfile文件,假设其文件描述符是fd
newfd为oldfd的一份拷贝,最后只剩下oldfd
dup2(fd,1)


将3号描述符里面的内容拷贝到1里面,用3号内容覆盖1号内容,此时1号描述符就不再指向标准输出了,转而指向myfile文件,写入1的内容,就会写入文件中


把本来应该显示到标准输出的内容,显示到log.txt文件中


此时printf打印内容显示到log.txt文件中


6. 如何理解缓冲区?

修改myfile.c文件的内容

#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<stdio.h>    
#include<unistd.h>    
#include<errno.h>    
#include<string.h>    
#define LOG "log.txt"            
int main()    
{    //C库    
fprintf(stdout,"hello world\n");    
//系统调用    const char*msg="hello write\n";    write(1,msg,strlen(msg));    fork();return 0;    
}

运行可执行程序只有两行信息,但是重定向到log.txt文件后,打印出三行信息,说明重复打印了


若将fork函数注释掉后,发现 两者显示结果相同\


struct FILE除了会封装fd之外,还会预留一部分输出缓冲区
当把字符串想写入stdout中时 ,struct FILE除了fd,还有一部分缓冲区
当我们想写的时候,并不是把数据拷贝到操作系统内部,而是把数据放到缓冲区当中
此时这个fprintf函数会直接返回
C库会结合一定的刷新策略,将缓冲区中的数据写入操作系统(write(FILE->fd,xxxx))


刷新策略:
1.无缓冲 (不提供缓冲)
2.行缓冲
如果碰到\n,就会把\n在内之前的内容刷新出来
3. 全缓冲
只有把缓冲区写满的时候,才会刷新缓冲区
显示器采用的刷新策略:行缓冲
普通文件采用的刷新策略:全缓冲


为什么要有缓冲区?
节省调用者的时间
系统调用也会花费时间
可能写了10次,如果每次调用fprintf传给操作系统 都要花费时间
但若都写入缓冲区中,统一传给操作系统 效率就变高了


write接口不论有没有重定向,都会正常打印,因为调用write是系统调用 没有缓冲区,直接调用就写给操作系统了
而使用fprintf ,数据会先写入缓冲区
当要打印到显示器中时 刷新策略:行缓冲
因为打印的内容都存在\n,在调用fork时,打印的内容已经在缓冲区中被刷新走了,刷新之后在fork就没有任何意义了
所以fork就什么也没干

当打印到普通文件时 刷新策略:全缓冲
使用 hello world 没办法把缓冲区写满,就无法刷新,父子两个进程都要刷新
刷新就要对缓冲区做清空,即对数据做修改,此时谁先刷新就先发生写时拷贝,所以最终就会打印两次相同数据

相关文章:

【Linux】 基础IO——文件(中)

文章目录1. 文件描述符为什么从3开始使用&#xff1f;2. 文件描述符本质理解3. 如何理解Linux下的一切皆文件&#xff1f;4. FILE是什么&#xff0c;谁提供&#xff1f;和内核的struct有关系么&#xff1f;证明struct FILE结构体中存在文件描述符fd5. 重定向的本质输出重定向输…...

蓝桥杯刷题冲刺 | 倒计时13天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;马上就要蓝桥杯了&#xff0c;最后的这几天尤为重要&#xff0c;不可懈怠哦&#x1f43e; 文章目录1.母牛的故事2.魔板1.母牛的故事 题目 链接&#xff1a; [递归]母牛的故事 - C语言网 (dotcpp.c…...

MIPI D-PHYv2.5笔记(5) -- 不同的PHY配置方式

声明&#xff1a;作者是做嵌入式软件开发的&#xff0c;并非专业的硬件设计人员&#xff0c;笔记内容根据自己的经验和对协议的理解输出&#xff0c;肯定存在有些理解和翻译不到位的地方&#xff0c;有疑问请参考原始规范看 规范5.7章节列举了一些常见的PHY配置&#xff0c;但实…...

【周末闲谈】文心一言,模仿还是超越?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️周末闲谈】 周末闲谈 ✨第一周 二进制VS三进制 文章目录周末闲谈前言一、背景环境二、文心一言&#xff1f;(_)?三、文心一言的优势&#xff1f;&#x1f617;&#x1f617;&#x1f617;四、文心一…...

《一“企”谈》 | 「佛山市政」:携手企企通,让采购业务数智化

近日&#xff0c;国家施工总承包壹级企业「佛山市市政建设工程有限公司」&#xff08;以下简称“佛山市政”&#xff09;正积极布局数字化建设工作&#xff0c;基于采购业务数智化&#xff0c;携手企企通打造了SaaS采购云平台。 01、岭南建筑强企 匠心铸造精品 …...

Spark运行架构

目录 1 运行架构 2 核心组件 2.1 Driver 2.2 Executor 2.3 Master & Worker 2.4 ApplicationMaster 3 核心概念 3.1 Executor 与 Core 3.2 并行度&#xff08; Parallelism&#xff09; 3.3 ​​​​​​​有向无环图&#xff08; DAG&#xff09; 4 提交流程 …...

基于卷积神经网络CNN的水果分类预测,卷积神经网络水果等级识别

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 卷积神经网络CNN水果分类预测 基本结构 主要参数 MATALB代码 结果图 展望 背影 现在生活&#xff0c;为节能减排&#xff0c;减少电能…...

Spring Boot 框架总结

Spring Boot 框架总结 1. springboot的引言 Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的 初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不 再需要定义样板化的配置。通过这种方式&#xff0…...

【数据结构】第五站:带头双向循环链表

目录 一、链表的八种结构 二、带头双向循环链表的实现 1.链表的定义 2.链表的接口定义 3.接口的具体实现 三、带头双向循环链表的完整代码 四、顺序表和链表的区别 一、链表的八种结构 我们已经知道链表可以有以下三种分法 而这三种结构又可以排列组合&#xff0c;形成八…...

Springboot生成二维码

Springboot生成二维码整合 我们使用两种方式&#xff0c;去生成二维码&#xff0c;但是其实&#xff0c;二维码的生成基础&#xff0c;都是zxing包&#xff0c;这是Google开源的一个包&#xff0c;第一种是使用原始的zxing方式去实现&#xff0c;第二种是使用hutool来实现&…...

“独裁者”何小鹏,再造小鹏汽车

文丨智能相对论 作者丨沈浪 如果没有何小鹏&#xff0c;小鹏汽车将失去灵魂。 2014年&#xff0c;夏珩、何涛等人在广州组建小鹏汽车&#xff08;当时还叫“橙子汽车”&#xff09;&#xff0c;何小鹏还只是股权投资人。 夏珩、何涛原任职于广汽&#xff0c;负责新能源汽车…...

数据结构 | 泛型 | 擦除机制| 泛型的上界

目录 ​编辑 1.泛型 1.1Object类引出泛型概念 2.泛型语法 2.1泛型编写代码 3.泛型的机制 3.1擦除机制 4.泛型的上界 4.1泛型上界的语法 4.2泛型上界的使用 5.泛型方法 5.1泛型方法语法 5.2泛型方法的使用 1.泛型 一般的类和方法中&#xff0c;只能使用具体的代码…...

C++拷贝构造函数(复制构造函数)详解

拷贝和复制是一个意思&#xff0c;对应的英文单词都是copy。对于计算机来说&#xff0c;拷贝是指用一份原有的、已经存在的数据创建出一份新的数据&#xff0c;最终的结果是多了一份相同的数据。例如&#xff0c;将 Word 文档拷贝到U盘去复印店打印&#xff0c;将 D 盘的图片拷…...

python学习——多线程

python学习——多线程概念python中线程的开发线程的启动线程的退出和传参threading的属性和方法threading实例的属性和方法多线程daemon线程和non-demone线程daemon线程的应用场景线程的jointhreading.local类线程的延迟执行&#xff1a;Timer线程同步Event 事件Lock ——锁加锁…...

SAP 系统中过账码or记账码

SAP中过账码和记账码是指同一个事物。 在实际业务中&#xff0c;记账码就是只有“借”和“贷”&#xff0c; 而SAP中Posting Code肩负着更多的任务&#xff1a; 1&#xff09;界定科目类型&#xff0c; 2&#xff09;借贷方向&#xff0c; 3&#xff09;凭证输入时画面上的字…...

【FreeRTOS(一)】FreeRTOS新手入门——初识FreeRTOS

初识FreeRTOS一、实时操作系统概述1、概念2、RTOS的必要性3、RTOS与裸机的区别4、FreeRTOS的特点二、FreeRTOS的架构三、FreeRTOS的代码架构一、实时操作系统概述 1、概念 RTOS&#xff1a;根据各个任务的要求&#xff0c;进行资源&#xff08;包括存储器、外设等&#xff09…...

Python中 __init__的通俗解释是什么?

__init__是Python中的一个特殊方法&#xff0c;用于在创建对象时初始化对象的属性。通俗来讲&#xff0c;它就像是一个构造函数&#xff0c;当我们创建一个类的实例时&#xff0c;__init__方法会被自动调用&#xff0c;用于初始化对象的属性。 举个例子&#xff0c;如果我们定义…...

网友真实面试总结出的自动化测试面试题库

目录 常规问题 手工测试部 自动化测试 自动化测试面试题2&#xff1a;selenium篇 常规问题 1、如何快速深入的了解移动互联网领域的应用 &#xff08;答案&#xff1a;看http协议 restful api知识 json加1分&#xff09; 2、对xx应用自己会花多久可以在业务上从入门到精通&…...

2023 年最佳 C++ IDE

文章目录前言1. Visual Studio2. Code::Blocks3. CLion4. Eclipse CDT&#xff08;C/C 开发工具&#xff09;5. CodeLite6. Apache NetBeans7. Qt Creator8. Dev C9. C Builder10. Xcode11. GNAT Programming Studio12. Kite总结前言 要跟踪极佳 IDE&#xff08;集成开发环境&…...

在Ubuntu上使用VSCode编译MySQL Connector/C连接库

首先下载并解压MySQL Connector/C源码&#xff0c;然后执行以下步骤&#xff1a; 1、安装MySQL Connector/C依赖&#xff1a;在终端中输入以下命令来安装MySQL Connector/C的依赖项&#xff1a; sudo apt-get install build-essential cmake 2、下载并解压MySQL Connector/C源…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...