【Linux】C文件系统详解(二)——什么是fd文件描述符以及理解“一切皆文件“

文章目录

  • fd-文件描述符
  • 如何深度理解"一切皆文件"
    • **我们使用OS的本质:**
    • FILE
      • `FILE`是什么?谁提供的?和我们刚刚讲的内核的struct有关系吗
        • `FILE`是一个结构体.该结构体内部一定要有以下字段:
        • `FILE`是C语言标准库提供的.
        • `FILE`和我们刚刚讲的内核的struct没有关系,最多就是上下层的关系
    • 做实验->重定向的本质
      • 第一个实验->文件描述符的分配规则
      • 第二个实验->输出重定向
        • 重定向的原理
      • 第三个实验->输入重定向
      • 第四个实验->追加重定向
      • 结论
        • 需求:把常规消息放一个文件,错误消息放在另一个文件
        • 更好的写法
    • 让我们自己的程序支持重定向:

fd-文件描述符

任何一个进程,在启动的时候,默认会打开当前进程的三个文件

标准输入标准输出标准错误本质都是文件
stdinstdoutstderr文件在语言层的表现
cincoutcerr同上,但是他是一个类
012<-fd ,数组下标

文件描述符,即open对应的返回值,本质就是:数组下标
标准输出和标准错误都会向显示器打印,但是其实是不一样的

类型设备文件
标准输入键盘文件
标准输出显示器文件
标准错误显示器文件
#include<iostream>
#include<cstdio>int main()
{//因为linux一切皆文件,所以,向显示器打印,本质就是向文件中写入printf("hello printf->stdout\n");	fprintf(stdout,"hello fprintf->stdout\n");fprintf(stderr,"hello fprintf->stderr\n");std::cout << "hello cout -> cout" << std::endl;std::cerr << "hello cerr -> cerr" << std::endl;
}
int fd1 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//3
int fd2 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//4
int fd3 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//5
int fd4 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//6
int fd5 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//7
int fd6 = open(LOG,O_WRONLY | O_CREAT | O_TRUNC,0666);//8

![[Drawing 2023-11-08 11.12.05.excalidraw|900]]

如何深度理解"一切皆文件"

我们使用OS的本质:

都是通过进程的方式进行操作系统的访问,在进程的角度,只能看到文件对象,而看不到底层的设备的区别,所以我们才说"Linux下一切皆文件".
![[文件系统 2023-03-18 15.19.03.excalidraw|800]]

FILE

操作系统层面,我们必须使用fd才能找到文件!
任何语言层面访问外设或者文件,都必须经历OS

FILE是什么?谁提供的?和我们刚刚讲的内核的struct有关系吗

#include<stdio.h>
FILE* fopen(const char *path,const char* mode);

答案:

FILE是一个结构体.该结构体内部一定要有以下字段:
fd

证明:

int main()
{printf("%d\n",stdin->_fileno);printf("%d\n",stdout->_fileno);printf("%d\n",stderr->_fileno);FILE* fp = fopen(LOG,"w");printf("%d\n",fp->_fileno);
}
FILE是C语言标准库提供的.

我们平时安装VS2019,是在安装IDE环境以及对应语言的库和头文件

FILE和我们刚刚讲的内核的struct没有关系,最多就是上下层的关系

做实验->重定向的本质

第一个实验->文件描述符的分配规则

把fd为3的文件关闭以后,新的文件fd应该是什么

int main()
{close(0);//fclose(stdin)close(2);//fclose(stderr)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);0printf("%d\n",fd2);2printf("%d\n",fd3);3printf("%d\n",fd4);4printf("%d\n",fd5);5return 0;
}

进程中,文件描述符的分配规则:

最小的,没有被使用的数组元素,分配给新文件

第二个实验->输出重定向

int main()
{fclose(1);int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUC, 0666);//此时log.txt的fd是'1'//但是上层结构不知道这个变化,他只知道要写进fd=1的文件中printf("you can see me!\n");//本来是指向stdout -> 1的,但是stdout变成了log.txtprintf("you can see me!\n");printf("you can see me!\n");printf("you can see me!\n");printf("you can see me!\n");return 0;
}

结果:打印不到屏幕,但是打印到了log.txt
printf("",);不是认stdout,而是认fd==1的文件描述符

重定向的原理

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

第三个实验->输入重定向

现在log.txt中写入:

123 456
int main()
{fclose(0);int fd = open(LOG, O_RDONLY | O_CREAT | O_TRUC, 0666);//fd=0int a,b;scanf("%d %d",&a,&b);printf("a=%d , b=%d\n",a,b);return 0;
}

结果: cat log.txt:
a=123 , b=456

第四个实验->追加重定向

int main()
{close(1);//标准输出int fd = open(LOG, O_RDONLY | O_CREAT | O_APPEND, 0666);printf("you can see me!\n");//从屏幕(stdout)上追加到fd中printf("you can see me!\n");printf("you can see me!\n");printf("you can see me!\n");
}

结果: cat log.txt:

a=123 , b=456
you can see me!
you can see me!
you can see me!
you can see me!

结论

所以stdout cout->1,他们都是向1号文件描述符对应的文件打印
stderr cerr ->2 ,他们都是向2号文件描述符对应的文件打印
输出重定向,只改的是1号对应的指向,对2号不影响

需求:把常规消息放一个文件,错误消息放在另一个文件
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#define LOG "log.txt"
#define LOG_NORMAL "logNormal.txt"
#define LOG_ERROR "logError.txt"int main()
{close(1);int fd = open(LOG_NORMAL, O_WRONLY | O_CREAT | O_APPEND, 0666);close(2);int fd = open(LOG_ERROR, O_WRONLY | O_CREAT | O_APPEND, 0666);printf("hello printf->stdout\n");	fprintf(stdout,"hello fprintf->stdout\n");fprintf(stderr,"hello fprintf->stderr\n");}

所以为什么要默认把1和2打开:
就是为了把常规消息和错误消息分类开来,便于后面的调试!

bash中重定向操作

a.out > log.txt 2 > &1
或者
a.out 1>log.txt 2>err.txt

2 > &1
把1里面的内容,写到2下标的内容里

更好的写法

int dup2(int oldfd, int newfd)
是对数组对应下标的内容进行拷贝

new要成为old的拷贝
所以最终只有oldfd的内容了
而我们最后正确重定向肯定是剩下3啊
所以oldfd 是3
newfd 是1

所以代码
dup2(fd,1)

重定向写法:

int main ()
{int fd = open(LOG_NORMAL, O_WRONLY | O_CREAT | O_APPEND, 0666);if(fd < 0){perrer("open");return 1;}dup2(fd,1);printf("hello world,hello lx\n");close(fd);
}

就是打开文件,之后使用dup2就行

让我们自己的程序支持重定向:

enum redir{REDIR_INPUT = 0,REDIR_OUTPUT,REDIR_APPEND,REDIR_NONE
};char* checkdir(char commandstr[],redir &redir_type);
{//1.监测是否有 > < >> //2.如果有,要根据> < >> 设置redir_type = ?//3.将符号改成\0,分成两部分//保存文件名,并返回//如果不满足,直接返回
}int main()
{while(1){redir redir_type = REDIR_NONE//...char* filename = NULL;char* filename = checkdir(commandstr,&redir_type);if(*filename){//1.存在文件//2.获取redir_type}//遍历,看是否有> < >>,这三个字符//前半部分执行后续//把这三个字符变成\0,将后面的字符串打开文件//dup2(fd,1);//...if(id == 0){if(redir_typr != REDIR_NONE){dup2();}}}
}

未完待续…

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis(哈希Hash和发布订阅模式)

哈希是一个字符类型字段和值的映射表。 在Redis中&#xff0c;哈希是一种数据结构&#xff0c;用于存储键值对的集合。哈希可以理解为一个键值对的集合&#xff0c;其中每个键都对应一个值。哈希在Redis中的作用主要有以下几点&#xff1a; 1. 存储对象&#xff1a;哈希可以用…

PDU是什么?

PDU&#xff0c;即功率分配单元(Power Distribution Unit)&#xff0c;它是一种能够对电源进行管理、监控来保障电力质量与供电可靠性的电源扩展设备。它广泛应用于数据中心、IT机房、机房领域等&#xff0c;可以实现对电源进行远程监控、电源管理和电源分配&#xff0c;为IT设…

springboot+vue+element简单实现教学课程申报管理系统

目录 一、项目预览 二、项目效果图及说明 1.项目说明 1.登录 2.欢迎页 3.教师管理 4.课程申报 ​5.管理员管理 三、代码实现 1.后端项目结构图 2.数据库表脚本 3.路由配置 四、总结 一、项目预览 在线预览&#xff1a;点击访问其他项目访问&#xff1a;点击访问后端实…

庖丁解牛:NIO核心概念与机制详解

文章目录 Pre输入/输出Why NIO流与块的比较通道和缓冲区概述什么是缓冲区&#xff1f;缓冲区类型什么是通道&#xff1f;通道类型 NIO 中的读和写概述Demo : 从文件中读取1. 从FileInputStream中获取Channel2. 创建ByteBuffer缓冲区3. 将数据从Channle读取到Buffer中 Demo : 写…

计算机科学速成课

建议看看计算机科学速成课&#xff0c;一门很全面的计算机原理入门课程&#xff0c;短短10分钟可以把大学老师十几节课讲的东西讲清楚&#xff01;整个系列一共41个视频&#xff0c;B站上有中文字幕版。 每个视频都是一个特定的主题&#xff0c;例如软件工程、人工智能、操作系…

潇洒郎: 小白一次性成功——小米红米手机解BL锁+ ROOT-刷面具

一、账号与设备绑定 手机登录账号,绑定账号,使用手机卡流量,等待7天后解BL锁。 二、解BL锁 下载工具 申请解锁小米手机 (miui.com) https://www.miui.com/unlock/index.html 1、登录账号-与绑定的账号一样 2、驱动检测安装 驱动安装进入Fastboot模式后,会自动识别已连接…

[黑马程序员SpringBoot2]——开发实用篇1

目录&#xff1a; 手工启动热部署自动启动热部署热部署范围配置关闭热部署功能第三方bean属性绑定松散绑定常用计量单位应用bean属性校验进制数据转换规则加载测试专用属性加载测试专用配置测试类中启动web环境发送虚拟请求匹配响应执行状态匹配响应体匹配响应体(json)匹配响应…

解决k8s node节点报错: Failed to watch *v1.Secret: unknown

现象&#xff1a; 这个现象是发生在k8s集群证书过期&#xff0c;重新续签证书以后。 记得master节点的/etc/kubernetes/kubelet.conf文件已经复制到node节点了。 但是为什么还是报这个错&#xff0c;然后运行证书检查命令看一下&#xff1a; 看样子是差/etc/kubernetes/pki/…

软件安全学习课程实践3:软件漏洞利用实验

1 逆向分析 1.1.1 和 1.1.2 直接 F5 看 flag 就可以了,故略。 1.1.3 对输入用了算法变换,能看到flag,比如输入x,经过f处理成f(x)然后判断f(x)=y,现在要破解f的算法然后写个逆预算g(y)=x 这个代码看起来很抽象,因为 IDA 没有正确的恢复这里的变量结构。选中变量按“N”重…

数据科学家应该知道的 10 个高级深度学习架构!

一、介绍 跟上深度学习的最新进展变得非常困难。几乎每天都会出现深度学习的新创新或新应用。然而&#xff0c;这些进步大部分都隐藏在 ArXiv / Springer 等媒体上发表的大量研究论文中。 本文包含深度学习的一些最新进展以及 keras 库中的实现代码。我还提供了…

【漏洞复现】致远OA wpsAssistServlet接口存在任意文件上传漏洞

漏洞描述 致远OA互联新一代智慧型协同运营平台以中台的架构和技术、协同、业务、连接、数据的专 业能力,夯实协同运营中台的落地效果;以移动化、AI智能推进前台的应用创新,实现企业轻量化、智能化业务场景,促进企业全过程管理能效,赋予企业协同工作和运营管理的新体验;在…

⑩④【MySQL】什么是视图?怎么用?视图的检查选项? 视图的作用?[VIEW]

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 视图VIEW ⑩④详解MySQL视图1. 视图的基本使用…