【Linux】IO 篇:文件调用原理,文件描述符,FILE的内涵,解析重定向,理解缓冲区

news2024/11/22 22:30:16

文章目录

  • 一、系统调用接口
  • 二、文件调用
      • 1. 文件描述符 fd
      • 2. 文件调用原理
      • 3. FILE
  • 三、重定向
      • dup2
  • 四、缓冲区
  • 简易 FILE 的代码实现

文件被加载之前,被存在磁盘上,操作文件,文件的部分内容则会被调度到 内存中。

要分析文件,我们也把文件分成两种:

  • 磁盘上的文件(文件系统)
  • 内存中的文件

这里谈论的是,内存中的文件

文件被打开,OS 会为被打开的文件创建对应的内核数据结构 struct file,将所有这个类型的结构体用某种数据结构链接起来以供 OS 管理。

struct file
{
	// 各种文件属性(磁盘中读出来的)
	// 各种链接关系
	// 缓冲区相关
};

一、系统调用接口


主要介绍一个

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// man 手册查看
man 2 open
  • int open(const char *pathname, int flags)
  • int open(const char *pathname, int flags, mode_t mode);

参数 pathname:

  • 文件名

参数 flags:

  • 标志位,man 2 手册可查

参数 mode:

  • 设置(新建)文件权限

返回值:

  • 为 -1 则说明,open 失败
  • 非负为 文件描述符(见后文)

参数 flags

  • O_CREAT | O_WRONLY
    如果没有文件则创建生成,
    默认不会对原始文件内容做清空,会从从最开始覆盖

  • O_CREAT | O_WRONLY | O_TRUNC
    如果没有文件则创建生成
    清空 写

  • O_CREAT | O_WRONLY | O_APPEND
    如果没有文件则创建生成
    追加写

注意在往文件里写入的时候,strlen(str) 不要 +1,因为 ‘\0’ 是 C 语言的结束规定,不是文件的规定,加进去会乱码。

使用系统接口进行 IO 的时候,一定要注意,\0 问题

我们 C 语言使用的一系列函数

  • fopen、fclose、fwrite/fputs、fread/fgets

都是系统函数的封装

  • open、close、write、read

二、文件调用


1. 文件描述符 fd

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

  • 标准输入 - - 设备文件 -> 键盘文件   0

  • 标准输出 - - 设备文件 -> 显示器文件   1

  • 标准错误 - - 设备文件 -> 显示器文件   2

      其中 标准输出 和 标准错误 都会向 显示器 打印,但他们其实是不一样的。
      eg:测试中,输入受重定向符的影响,而错误不受重定向符的影响
    

文件描述符,也是 open 对应的返回值。我们创建的文件返回的值是从 3 开始的,而 0 1 2 正是被上面默认打开的三个文件占用了。这个数字本质就是 数组下标

一张简图:
在这里插入图片描述

进程中,文件描述符的分配规则:
在文件描述符表中,最小的,没有被使用的数组元素,分配给新文件
fclose(stdin);
// 等价于
close(0);

2. 文件调用原理

  • 1 个进程 可以调度 n 个文件,每个文件都有 一个缓冲区
  • 调用 read / write / close 这些系统接口时,都需要文件操作符。也就是说,在操作系统层面,我们必须要访问fd,才可以找到文件
  • 我们所谓的 IO 类 read / write 函数,本质上是 拷贝函数
  • 什么时候将缓冲区上的内容刷新到磁盘中指定的位置,由 OS 自主决定
  • 进程 和 文件 并没有深度耦合,便于操作系统的管理
    在这里插入图片描述

如何理解一切皆文件:

  • 每个硬件都有一个 struct file 对象,C语言里面没有成员函数,使用的就是函数指针完成的众多行为。
  • 进程通过 指针数组,访问的其实是这些 struct file 对象,包括里面的缓冲区、函数指针…
  • 而用户的操作,实际上都是进程的操作
  • 所以我们说,Linux 下,一切皆文件
    在这里插入图片描述

3. FILE

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

FILE *fopen(const char *path, const char *mode);

在这里插入图片描述

这里的 FILE * 是 结构体类型!由 C语言提供的,跟内核的 struct file 没有任何关系。

我们指定在操作系统层面,我们必须要访问 fd,才可以找到文件。也就是说 struct FILE 里面必定封装了 fd。

我们来看 FILE 源码是这样写的:

typedef struct _IO_FILE FILE;/usr/include/stdio.h

struct _IO_FILE {
	//...
	int _fileno; //封装的文件描述符,就是我们说的 fd
	
	// C语言维护的缓冲区相关内容
	//...
};

测试如下:

print("%d\n", stdin->_fileno);
print("%d\n", stdout->_fileno);
print("%d\n", stderr->_fileno);
FILE *fp = fopen("test.txt", "w");
print("%d\n", fp->_fileno);


--------
输出结果:
0
1
2
3

三、重定向


🌰< 输出重定向举例

close(1);
int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);	// fd = 1

printf("hello\n"); // stdout -> 1
printf("hello\n");
printf("hello\n");

---------
hello
hello
hello
字样,被保存进了 test.txt 中

在这里插入图片描述

🌰> 输入重定向举例

close(0);
int fd = open("test.txt", O_RDONLY);	// fd = 0

int a,b;
scanf("%d %d", &a, &b);	// stdin -> 0
printf("a = %d, b = %d\n", a, b);

---------
在 test.txt 文件中写入 123 456
运行程序后输出
a = 123, b = 456

在这里插入图片描述

🌰>>追加重定向举例

close(1);
int fd = open("test.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);	// fd = 1

printf("hello\n"); // stdout -> 1
printf("hello\n");
printf("hello\n");

---------
运行两次则有
hello
hello
hello
hello
hello
hello
字样,被保存进了 test.txt 中

回头看之前的问题

其中 标准输出 和 标准错误 都会向 显示器 打印,但他们其实是不一样的。
eg:测试中,输入受重定向符的影响,而错误不受重定向符的影响

原因如下:

stdout、cout -> 他们都是向 1 号文件描述符对应的文件打印;
stderr、cerr -> 他们都是向 2 号文件描述符对应的文件打印。
输出重定向时,更改的只是 1 号对应的指向,2 号未被影响。

当我们需要手动分离一个程序正确和错误信息的时候:

./a.out 1>log.txt 2>err.txt

当然也有直接的函数可以使用

dup2

头文件:

#include <unistd.h>

int dup2(int oldfd, int newfd);

参数 oldfd:

  • 最后需要的 fd

参数 newfd:

  • 需要被覆盖的 fd

相当于,把本应该到 newfd 上的,重定向到 oldfd,最后剩下的只有 oldfd

四、缓冲区

C 语言维护的 FILE 结构体 和 OS 维护的 struct file 结构体,都有自己的缓冲区(每个对象都有自己的缓冲区),这两个缓冲区是不相同的。

在这里插入图片描述

C库提供的刷新策略,一般有三种:

  1. 无缓冲
  2. 行缓冲(遇到 \n 刷新)
  3. 全缓存(缓冲区满了刷新)
  • 显示器采用的刷新策略:行缓冲
    普通文件采用的刷新策略:全缓冲

缓冲区的作用:节省调用者的时间

这会产生一些奇怪的现象:

// c 库
fprintf(stdout, "hello fprintf\n");
// os 系统调用
const char *msg = "hello write\n";
write(1, msg, strlen(msg));

fork();

这个程序我们在 linux 下,重定向到文件,会出现如下情况

[xxx@hostname file]$ ./a.out
hello write
hello fprintf
[xxx@hostname file]$ ./a.out > test.txt
[xxx@hostname file]$ cat test.txt
hello write
hello fprintf
hello fprintf
[xxx@hostname file]$

第一个运行容易理解,分析第二次 cat 文件内容出现的结果
原因如下:

  • 首先,write 正常调用输出到显示器
  • fprintf 的缓冲区,对于重定向到普通文件,使用全缓冲,这里的内容显然不能将缓冲区填满,所以进程结束时刷新
  • 一直到 fork 被调用,程序还没结束,此时父子进程的缓冲区里都有一份 hello fprintf。谁先结束谁就先写诗拷贝,刷新到屏幕上,于是被打了两次

简易 FILE 的代码实现


👉🔗链接如下



🥰如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

问道管理:沪指震荡跌0.84%,银行、医药等板块走弱,地产板块逆市拉升

2日早盘&#xff0c;沪指盘中震动下探&#xff0c;深成指、创业板指亦走低&#xff1b;两市半日成交约5400亿元&#xff0c;北向资金净卖出约35亿元。 到午间收盘&#xff0c;沪指跌0.84%报3263.2点&#xff0c;深成指跌0.42%&#xff0c;创业板指跌0.27%&#xff0c;上证50指数…

UPnP是什么?有什么更好的连接方案?快解析内网穿透

一、UPnP是什么 有些小伙伴对于UPnP并不了解&#xff0c;其实UPnP只是一种网络协议&#xff0c;主要作用就是简化家庭和企业网络中设备之间的连接和通信过程&#xff0c;它的主要目标是实现网络的无缝连接&#xff0c;并简化相关网络操作。 二、UPnP有什么主要作用&#xff1…

激光切割机好不好?激光切割与线切割应该怎么选择对比

在选择激光切割机与线切割机进行比较时&#xff0c;我们首先需要理解两者的特性与特点。 激光切割机&#xff1a;现阶段主流的激光切割设备主要包括光纤激光切割机和CO2激光切割机。其中CO2激光切割机主要用于切割厚板&#xff0c;但也可完成非金属材料的切割。光纤激光切割机则…

面试题:JS中的String常见方法有哪些?

面试题&#xff1a;说不出五个就尴尬了&#xff01;我目前只写了几个方法&#xff0c;待更新中。。。 1、length2、slice()3、substr()4、substring()5、split()6、indexOf() 1、length 作用&#xff1a;检测字符串的长度。 let str abcde console.log(str.length) // 52、sl…

深入解读Gartner 2023中国数据、分析与AI技术成熟度曲线报告

近日&#xff0c;国际权威研究机构Gartner发布了《Hype Cycle for Data, Analytics and AI in China, 2023》&#xff08;2023中国数据、分析与AI技术成熟度曲线报告&#xff09;。作为业内的权威报告&#xff0c; Gartner 每年针对技术、应用和行业创建的技术成熟度曲线&#…

【WebRTC---源码篇】(二:一)PeerConnection详解

Track的添加 上图是整体流程图 RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> PeerConnection::AddTrack(rtc::scoped_refptr<MediaStreamTrackInterface> track,const std::vector<std::string>& stream_ids) {RTC_DCHECK_RUN_ON(signal…

Linux(三)---------网络路由命令(route路由命令)

一.route路由命令 1.什么是route路由&#xff1f; 计算机之间的数据传输必须经过网络&#xff0c;网络可以直接两台计算机&#xff0c;也可以通过一个一个的节点去连接。路由可以理解为互联网的中转站&#xff0c;网络中的数据包就是通过一个一个的路由器转发到目的地的。 路…

【枚举边+树的直径】CF14D

Problem - 14D - Codeforces 题意&#xff1a; 思路&#xff1a; 两条链不相交&#xff0c;说明是在不同连通分量中&#xff0c;我们可以枚举边来把树分为两个连通分量&#xff0c;然后分别计算直径即可 Code&#xff1a; #include <bits/stdc.h>#define int long lo…

目标检测与跟踪 (1)- 机器人视觉与YOLO V8

目录 1、研究背景 2. 算法原理及对比 2.1 点对特征&#xff08;Point Pairs&#xff09; 2.2 模板匹配 2.3 霍夫森林 2.4 深度学习 3、YOLO家族模型演变 4、YOLO V8 1、研究背景 机器人视觉识别技术是移动机器人平台十分关键的技术&#xff0c;代表着机器人智能化、自动化…

XGBoost的参数空间与超参数优化

目录 1. 确定XGBoost的参数空间 2. 基于TEP对XGBoost进行优化 1. 确定XGBoost的参数空间 对任意集成算法进行超参数优化之前&#xff0c;我们需要明确两个基本事实&#xff1a;①不同参数对算法结果的影响力大小&#xff1b;②确定用于搜索的参数空间。对XGBoost来说&#x…

什么?仅使用配合功能就能让内燃机运动起来

配合作为SOLIDWORKS中装配体的重要命令&#xff0c;拥有着非常强大的功能。根据应用场合的不同软件将其分类为标准配合、高级配合和机械配合&#xff0c;按照这个顺序功能也越来越强大。本次使用内燃机的模型&#xff0c;将三个配合依次进行展示。 标准配合包含了常见的自由度约…

redis安装,开启自启动(Windows)

1、下载redis包 Releases tporadowski/redis GitHub 2、配置环境变量 path中添加redis解压后的路径&#xff1b; 3、配置文件修改 注释掉 bind 127.0.0.1 使其他ip可以远程访问&#xff1b; 修改redis密码 4、启动Redis服务 redis-server.exe --service-install redis.w…

如何让win10开机默认开启小键盘?

一、背景 省流版&#xff1a;直接看最后。 如题。 随便搜一搜&#xff0c;就会发现&#xff0c;有大量雷同&#xff0c;甚至内容完全相同的文章&#xff0c;然而并无卵用。 如&#xff1a; 如何让win10开机默认开启小键盘&#xff1f;【知乎】 如何让win10开机默认开启小键盘…

【网络】网络层(IP协议)

目录 一、基本概念 二、协议头格式 三、网段划分 四、特殊的IP地址 五、IP地址的数量限制 六、私有IP地址和公网IP地址 七、路由 一、基本概念 IP协议&#xff1a;提供一种能力&#xff0c; 将数据从A主机送到B主机&#xff0c;&#xff08;TCP协议&#xff1a;确保IP协议…

python去除重复图片(数据清洗)

其中1文件夹中有重复出现的图片,只是图片名不同。 2文件夹为空文件夹,用于保存去除的重复图。运行py文件。 import shutil import numpy as np from PIL import Image import osdef 比较图片大小(dir_image1, dir_image2):with open(dir_image1, "rb") as f1:size1…

postgresql 查询:查询是否在该列:一个字符串以“|”分割的列

上面是section 表中的link_laneid 的字段描述。 需求是&#xff1a;查询一个值是否在link_laneId 中存在&#xff08;注意这个值是个复合类型&#xff0c;以“|”分割的字符串&#xff09;。 sql语句&#xff1a; select * from section where 162243 any((string_to_arr…

怎样选择适合的爬虫ip服务商?

在当今数字化的时代&#xff0c;越来越多的企业和个人需要采集和分析大量的数据来进行市场调研、竞品分析、舆情监测等工作。而为了保护其数据和资源&#xff0c;很多网站采取了反爬虫措施&#xff0c;限制了普通用户和爬虫程序的访问。为了应对这种限制&#xff0c;许多人开始…

2023年8月5日(星期六):骑行小河边村

2023年8月5日(星期六)&#xff1a;骑行小河边村&#xff0c;早8:30到9:00&#xff0c; 大观公园门囗集合&#xff0c;9:30点准时出发 【因迟到者&#xff0c;骑行速度快者&#xff0c;可自行追赶偶遇。】 偶遇地点: 大观公园门囗集合&#xff0c;家住南&#xff0c;东&#x…

nginx 配置多域名多站点 Ubuntu

nginx 配置多域名多站点 Ubuntu 一、安装 nginx apt install nginx二、配置文件说明 nginx 的配置文件在 /etc/nginx 目录下&#xff0c;它的默认内容是这样的 root2bd0:/etc/nginx# ll total 72 drwxr-xr-x 8 root root 4096 Jul 31 15:21 ./ drwxr-xr-x 104 root root …

一作分享| eDNA快速检测鱼类多样性!长江上游江津至涪陵河段鱼类多样性调研

长江上游作为鱼类多样性热点研究区域&#xff0c;已有诸多相关调查研究。传统捕捞法不仅耗时费力&#xff0c;并具有偶然性&#xff0c;对鱼体和生态环境均有害&#xff0c;而且难以发现一些稀少或体型较小的鱼类。环境DNA宏条形码&#xff08;eDNA metabarcoding&#xff09;技…