当前位置: 首页 > 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源…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南

在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...