【Linux】基础IO-----文件详解

news2025/1/17 3:43:24

目录

一、文件理解:

二、C语言的文件操作:

1、fopen:

什么是当前路径:

2、fclose:

3、fwrite:

4、默认打开的三个流:

三、系统文件:

1、open:

2、close:

3、write:

O_TRUNC:

O_APPEND:

四、文件描述符与FILE:

文件描述符:

FILE:


一、文件理解:

通过几个问题来理解文件:

文件是由什么构成的,是在哪里存放的

文件 = 文件内容+文件属性
文件分为已被使用的文件未使用的文件
已使用的文件存放在磁盘,未使用的文件存放在内存(CPU只和内存打交道)

对文件进行操作本质是什么

对文件的操作可以是在语言方面的,也可以是在系统方面
对于语言方面的文件操作就是通过库函数的调用而对文件进行读写
对于系统层面的文件操作就是通过先描述再组织的方式对文件进行管理操作

操作系统是怎么对各个正在使用的文件进行区分,管理的

操作系统对文件进行区分管理就 类似于管理进程 通过对文件进行描述(使用struct file 结构体对文件属性进行管理)再组织(每一个struct file结构体中有着指向下一个结构体的指针)

二、C语言的文件操作:

接下来回顾一下C语言中的对文件的操作接口:

可以看看以前写的文章,但是对于接口还有更多的补充

【C语言】文件操作-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/2303_80828380/article/details/139933028?spm=1001.2014.3001.5501

1、fopen:

这个C语言中的标准库函数的作用是打开一个文件,如果没有找到文件就创建一个文件,再打开

在C语言中更多的是:

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

解析:

第一个参数:
可以写成要操作的文件名,如果在文件名前面不加路径,那就是在当前路径下打开,创建文件,如果加上了绝对路径那么就在指定的路径进行打开,创建文件

第二个参数:
是一个字符串,有几个选项可以选择的:

r :  只读模式,文件必须存在
r+: 读写模式,文件必须存在
w : 写模式,如果文件存在则文件长度清为0,即文件内容会消失,如果文件不存在则创建该文件
w+读写模式,如果文件存在则文件长度清为0,即文件内容会消失,如果文件不存在创建立该文件
a:  追加模式,如果文件存在,写入的数据会被加到文件尾后,如果文件不存在,则创建文件
a+: 追加模式,如果文件存在,写入的数据会被加到文件尾后,如果文件不存在,则创建文件
其中:a,附加写方式打开,不可读;a+,附加读写方式打开

如上,这就是在当前路径以写的模式打开 log.txt 文件

返回值:
返回类型是一个指向FILE对象的指针,FILE又是C库中自己封装的结构体,里面封装了文件描述符若文件打开失败,会返回NULL,

文件描述符:(这是一个非负整数)
这个可以理解为:每个文件,操作系统进行设计的时候都需要有一个下标对应一个文件,可以理解为每个数组下标就对应了一个文件,通过这个数组下标就能访问操作文件 这个数组下标就被称为文件描述符,每个进程都有一个文件描述符表,用于跟踪进程打开的文件和I/O资源

什么是当前路径:

通过上述知识可以知道,当要在当前路径下以写模式打开文件时,如果没有找到该文件,就在当前路径下创造一个文件,那么系统是怎么找到当前路径的呢?当前路径又是什么呢?

#include<stdio.h>
#include<unistd.h>
#include<string.h>

int main()
{
	printf("pid:%d\n",getpid());
	FILE* fp = fopen("log.txt","w");
	if(fp == NULL)
	{
		perror("fopen fail");
		return 1;
	}
	fclose(fp);
	sleep(1000);
	return 0;
}

如上代码,这就是在当前路径下以读的形式打开一个文件,如果不存在就在当前路径下创建一个log.txt文件再打开

可以通过下述的指令查看到进程的当前路径cwd(current working directory)

如上,当进程执行的时候,可以在/proc目录下查看该进程的数据,里面有一个cwd,操作系统就是通过查看这个来作为进程的当前路径

2、fclose:

打开、关闭文件类似于动态开辟空间(开辟好一个空间后要将其释放),当我们打开一个文件后,在使用后也要记得关闭文件,这个时候就使用fclose即可,

参数就是将该文件的文件指针传入fclose函数即可,fclose函数如果关闭文件成功会返回0
最后当关闭后及时将文件指针置空防止野指针

fclose(pf);//关闭文件
pf = NULL;//及时置空

3、fwrite:

这是一个文件写入方式,有四个参数
解析:

第一个参数:
代表着要写入数据的起始地址
第二个参数:
代表着要拷贝数据的大小
第三个参数:
代表着要拷贝数据的个数
第四个参数:
代表着要数据要到写到哪儿去

#include<stdio.h>
#include<unistd.h>
#include<string.h>

int main()
{
	FILE* fp = fopen("log.txt","w");
	if(fp == NULL)
	{
		perror("fopen fail");
		return 1;
	}

	const char* sum = "abcdefghijklmn\n";
	fwrite(sum,sizeof(char),6,fp);
	fclose(fp);
	return 0;
}

如上代码,就是将sun字符串,6个大小为char的数据写入到fp指针指向的 log.txt 中,
这样打开log.txt文件就可以看到已经写了6个char大小的字符

如果想一次全部写入可以修改为

fwrite(sun,strlen(sum),1,fp);

这样,再打开log.txt文件就可以看到把sum所有字符串都写入

4、默认打开的三个流:

在Linux下可以看做一切皆文件,所以我们电脑的显示器,键盘也可以看作是文件,
比如当在显示器上能够看到数据,其实就是往显示器文件中写入了数据
键盘能够输入数据,其实就是CPU从键盘文件中读入数据

那么当进程启动的时候我们为什么不用在代码中打开键盘文件,显示器文件呢?

其实在进程启动的时候,就默认打开了三个流:标准输入流,标准输出流,标准错误流
中三个在C语言中对应的分别就是stdin,stdout,stderr,我们在man手册中可以看到这三者的类型都是FILE*,这也就相当于打开了三个文件

比如我们也可以直接向stdout流里面写数据,这样的话就可以在显示器中看到了

三、系统文件:

首先要知道,对文件进行操作上述讲的是在语言方面的接口,操作系统还有一套系统接口来对文件进行访问的,实际上,语言方面的接口就是对系统接口进行封装的,

如下:语言方面的接口就在用户操作接口地方,系统接口就在system call处,系统接口更接近底层

文件是在磁盘上存储的,磁盘又属于硬件,平时我们在IO的时候访问文件本质上就是和硬件打交道,通过前面的知识我们了解到,用户如果想访问硬件是不能够直接访问的,必须要经过操作系统的,又因为操作系统不相信任何人,所以操作系统提供了系统调用接口供用户使用而访问底层硬件

1、open:

这个就是一个系统调用接口,man手册中的初步介绍如下:

解析:

第一个参数:
这是一个待操作文件名,其实和fopen中的一样,
如果以文件名的方式给出,就是在当前路径下进行文件操作
如果以路径+文件名的方式给出,就是在所给路径下进行文件操作
第二个参数:
这是一个打开文件的方式,
第三个参数:
这是代表着创建一个文件时,这个文件的默认权限是什么,起始权限为(0666)

扩展:

 对于一个整形来说,有32个比特位,就可以看做有32个标志位。而这种标志位就是flags,flags利用了这种比特位级别的标志方式

接下来看看下面代码,这就是类似于想看哪里的标志位,就直接show(ONE)之类的

#define ONE (1<<0)//1
#define TWO (1<<1)//2
#define THREE (1<<2)//4
#define FOUR (1<<3)//8

void show(int flag)
{
	if(flag & ONE) printf("hello one\n");
	if(flag & TWO) printf("hello two\n");
	if(flag & THREE) printf("hello three\n");
	if(flag & FOUR) printf("hello four\n");
}

int main()
{
	printf("-----------------------------\n");
	show(ONE);
	printf("-----------------------------\n");
	show(TWO);
	printf("-----------------------------\n");
	show(FOUR|THREE);
	printf("-----------------------------\n");
	show(ONE|TWO|THREE);
	return 0;
}

运行结果:

所以,在open函数的内部就类似于上述的方法,定义宏,然后在open函数内部进行传参,然后通过“按位与”运算来进行判断

返回值:

open函数的返回值是返回的文件描述符,

接下来我们使用open函数,并且查看它的返回值,这里第二个参数采用的是以读的形式打开,如果在当前路径下没有找到文件,就创建文件,并且默认权限为0666

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main()
{
	int fp1 = open("log1.txt",O_WRONLY|O_CREAT,0666);
	int fp2 = open("log2.txt",O_WRONLY|O_CREAT,0666);
	int fp3 = open("log3.txt",O_WRONLY|O_CREAT,0666);
	int fp4 = open("log4.txt",O_WRONLY|O_CREAT,0666);
	int fp5 = open("log5.txt",O_WRONLY|O_CREAT,0666);
	int fp6 = open("log6.txt",O_WRONLY|O_CREAT,0666);
	printf("fp1 : %d\n",fp1);
	printf("fp2 : %d\n",fp2);
	printf("fp3 : %d\n",fp3);
	printf("fp4 : %d\n",fp4);
	printf("fp5 : %d\n",fp5);
	printf("fp6 : %d\n",fp6);
	return 0;
}

如上的运行结果如下,返回的这些整数就是文件描述符,那么为什么是从3开始而不是从0开始的呢?

这当然是因为进程启动的时候就已经默认打开了三个文件流,stdin,stdout,stderr,这三个文件占领了0,1,2,所以后面打开的文件就依次从3开始

接下来看看这些已经创建的文件的权限:

可以看到权限对应的是0664,但是我们传的明明是0666,这是为什么呢?

很简单,在前面的学习中我们了解到了文件的真正的权限等于默认权限 & (~umask),系统中默认的umask为0002,所以默认权限0666变成真正权限就是0664

当然,我们也可以在代码中进行umask的修改,将umask置为0

这样,文件的真正权限就变为0666

2、close:

在打开文件后要记得关闭文件,关闭文件成功返回0,关闭文件失败返回-1

3、write:

这个系统调用接口是向文件中写入数据,

解析:

第一个参数:
文件描述符,也就是打开文件时的返回值(open的返回值)
第二个参数:
写入的数据来源
第三个参数:
写入数据的字节数

如下就是一个先打开一个log.txt的文件,然后再向文件中写入字符串

int main()
{
	int fd = open("log.txt",O_WRONLY|O_CREAT,0666);
	if(fd < 0)
	{
		perror("open fail");
		return 1;
	}
	const char* message = "abcdefghijkl\n";
	write(fd,message,strlen(message));
	close(fd);
	return 0;
}

那么运行后再查看log.txt就可以看到我们已经把字符串写进去了

O_TRUNC:

这个宏作为第二个参数中,是打开文件后清空当前文件在写入

当如果是没有O_TRUNC宏的时候,对已经存在数据如下,

在进行写入数据的时候就会从开始写入,本来存在的数据并不会被清除

如果想清楚本来的数据,只需在open的第二个参数加上O_TRUNC这个宏即可

O_APPEND:

如果不想进行清空写入,也不想在最开始写入,我要在最后面追加写入,那么就在open的第二个参数加上宏O_APPEND即可

如下,就是在open上加上宏O_APPEND,然后在最后追加aaaaaaaaaaaaaaa

如下,一开始log.txt就是三行abcdefg...,然后在运行程序后,就可以看到在最后追加了一串a

四、文件描述符与FILE:

文件描述符:

当启动进程的时候内存会加载一个PCB(Linux中是task_struct)
这个task_struct结构体里面肯定有一个指针struct file_struct* file指向一个叫做struct file_struct的结构体
这个结构体里面有一个struct file* fd_array[ ]的指针数组,这个数组的下标就是文件描述符
这个指针数组里面存放的是struct file*的指针,每个指针指向struct file的结构体
这个struct file的结构体里边就是对文件属性进行管理

当我们打开一个文件的时候,会生成一个描述文件的结构体,然后进程会在struct file* fd_array[](文件指针数组)里面找一个空位置保存刚刚创建描述文件的结构体的地址,然后再将这个数组的下标返回给用户,这个返回值就是open的返回值,最终,进程就可以根据这一张文件描述符表,就可以把我们打开的文件找到了

FILE:

FILE是C库中封装的一个结构体,因为Linux访问文件只看文件描述符,所以FILE这个结构体里面肯定封装了文件描述符,如下,stdin,stdout,stderr是FILE*类型的,所以里这三个结构体里面肯定分别封装了fd = 0,fd = 1,fd = 2

如果将stdout这个文件关闭了,那么就看不到输出在显示器上的数据了

但是可以继续往stderr里面输入,这个时候也可以看到了

这是因为stdout和stderr都是指向显示器文件,显示器文件有引用计数(事实上struct file结构体里面都有引用计数)
所以关闭stdout文件不会彻底关闭显示器文件
关闭文件的本质就是让struct file里面的引用计数-1,并且把文件描述符表里面指向这个struct file的下标置为空

当一个文件的引用计数减为0时,操作系统就会关闭该文件的文件描述符,但文件本身在文件系统中仍然存在,直到被删除

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

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

相关文章

AI开发:卷积神经网络CNN原理初识,简易例程 - 机器学习

一 、卷积神经网络是什么 &#xff08;1&#xff09;印象 今天说的CNN&#xff0c;并不是我们熟知的美国有线电视新闻网。 那什么是CNN呢&#xff1f; Convolutional Neural Networks, CNN&#xff09;简单来说&#xff0c;就是用一个筛子来筛面粉的。 筛子就是卷积核&…

Nginx基础学习——介绍、安装与常用命令(windows/linux安装详细攻略)

目录 前言&#xff1a; 一、 Nginx 基本概念 &#xff08;1&#xff09; Nginx 是什么&#xff0c; 做什么事情 &#xff08;2&#xff09; 反向代理 &#xff08;3&#xff09; 负载均衡 &#xff08;4&#xff09; 动静分离​编辑 二、 Nginx安装、常用命令和配置文件…

人脸识别Adaface之libpytorch部署

目录 1. libpytorch下载2. Adaface模型下载3. 模型转换4. c推理4.1 前处理4.2 推理4.3 编译运行4.3.1 写CMakeLists.txt4.3.2 编译4.3.3 运行 1. libpytorch下载 参考&#xff1a; https://blog.csdn.net/liang_baikai/article/details/127849577 下载完成后&#xff0c;将其解…

Windows电脑伪关机(快速启动模式),怎么真关机

Windows电脑在关机的时候&#xff0c;进入到一个伪关机的状态&#xff0c;也就是并没有真正的关机&#xff0c;但是在一些系统更新、变更了一些设置&#xff0c;进行重启等操作也会进入到真关机状态 这种一般是开启快速启动模式&#xff0c;开启了快速启动模式功能会在关机的时…

Bellman-Ford 算法详解及应用

Bellman-Ford 算法详解及应用 图24-4 的结构(假设)Bellman-Ford 算法步骤伪代码C 语言实现 Bellman-Ford 算法运行结果分析输出示例(部分)Bellman-Ford 算法是一种用于计算单源最短路径的算法,即从给定的源节点到其他所有节点的最短路径。它可以处理带有负权重的边,但不适…

92.插入排序

一.什么是插入排序 把数据分为有序和无序俩个部分&#xff0c;将无序部分中的一个数据插入到已排好的有序部分中&#xff0c;有序部分从而变成一个新的、排好数据数量增1的有序数据&#xff0c;直到数据全部排序完成。 插入排序一般适用于小型数据。大型数据性能较差。 二.实…

69 mysql 中 is null 的实现

前言 Mysql 中我们偶尔会用到 字段为 NULL 的情况 这时候 我们只能使用查询 “select * from tz_test_02 where field1 is null;” 来进行 field1 字段为 null 的行的查询 然后如果是使用 “select * from tz_test_02 where field1 null;” 你会发现查询 不出数据 但是如…

A1017 基于Java+JSP+SQL Server+servlet的二手购物平台的设计与实现

二手购物平台 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 摘 要 计算机以及网络技术的飞速发展&#xff0c;网络的应用在全国乃至全球日益普及&#xff0c;随着人们的思想水平和生活水平的提高&#xff0c;网络已经是人们必不可少的一部分。人们的…

力扣-图论-6【算法学习day.56】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

使用PHPUnit使用本地调试代替远程调试,快速提高开发效率

Laravel 是一个在 Linux 环境下表现非常出色的 PHP 框架&#xff0c;但它在 Windows 环境下可能会遇到一些兼容性和配置问题。为了调试或没试的方便可以在 Windows 环境下进行 Laravel PHPUnit进行本地调试和测试。 本地主要针对断点调试效果非常高效。 在 Laravel 中&#x…

动态规划 - 4( 背包问题 10000 字详解 )

一&#xff1a; 背包问题 1.1 01 背包 题目链接&#xff1a;01 背包 import java.util.Scanner;public class Main {public static void main(String[] args) {// 不做空间优化&#xff0c;用 dp 表存下所有状态Scanner sc new Scanner(System.in);// 读入 n 和 Vint n sc.…

关于利用 EtherNet/IP 转 Profinet 网关模块实现罗克韦尔变频器接入西门子 PLC 的配置范例

在现代工业自动化领域&#xff0c;不同品牌设备之间的通信兼容性问题一直是企业面临的挑战之一。某智能工厂为了优化生产流程&#xff0c;提高设备的协同工作效率&#xff0c;决定对其生产线上的控制系统进行升级改造。该生产线中&#xff0c;AB罗克韦尔PowerFlex变频器作为关键…

论文研读|信息科技风险管理模型的主要内容、定位、目标企业、风险管理机制, 以及相应的风险评估流程和风险应对策略

文献来源&#xff1a;[1]李政.商业银行数据中心风险管理研究[D].合肥工业大学[2024-12-08]. 内容总结&#xff1a;风险管理是一项不断发展完善的体系化工作。本章研究了国际上普遍使用的风险管理基本模型、并针对主流的信息科技风险管理的框架进行研究、分析&#xff0c;重点研…

MySQL-DML之数据表操作

文章目录 一. 插入表记录1. 向表中插入部分字段2. 向表中插入所有字段,字段的顺序为创建表时的顺序3. 一次添加多条数据信息 二. 更新表记录1. 更新所有记录的指定字段 更新符号条件记录的指定字段2. 更新符号条件记录的指定字段 三. 删除表记录1. 按条件删除记录2. 清空记录 四…

mac mini 4 上手指南

文章目录 和 mac 初见购机历程外设配置win 系统 适应 mac 系统一些使用体验mac 快捷键 和 mac 初见 最开始用 mac 是小时候用着刷成 windows 的 MacBook 和好朋友一起打游戏&#xff0c;再后来就是上大学&#xff0c;用朋友的 mac 做一些简单的办公 购机历程 我之前 11月2日…

Apache AGE:基于PostgreSQL的图数据库

Apache AGE&#xff08;A Graph Extension&#xff09;是一个基于 PostgreSQL 的图数据库。它以扩展插件的形式提供&#xff0c;可以在利用 PostgreSQL 先进的 SQL 查询功能和事务支持的同时&#xff0c;享受图数据库的灵活性和可扩展性。 Apache AGE 最初由 Bitnine Global In…

关于springBoot+vue项目中配置SSL证书问题

前端可以通过https进行访问 1.前端在访问后端接口时&#xff0c;使用https进行访问&#xff0c;在request.js配置文件中&#xff0c;这个文件是配置axios的基本请求的&#xff0c;在基础请求地址中改为https方式 2.需要在Linux中的nginx中配置ssl证书&#xff0c;具体请参考&…

华为、华三交换机纯Web下如何创关键VLANIF、操作STP参数

华为交换机WEB操作 使用的是真机S5735&#xff0c;目前主流的版本都适用&#xff08;V1R5~V2R1的就不在列了&#xff0c;版本太老了&#xff0c;界面完全不一样&#xff0c;这里调试线接的console口&#xff0c;电脑的网络接在ETH口&#xff09; 「模拟器、工具合集」复制整段内…

二分查找(带图详解)

优选算法系列 文章目录 优选算法系列前言一、二分查找的思想二、算法使用小总结 三、代码实现四、二分查找拓展4.1、查找第一次出现的target小总结 4.2、target最后出现的位置小总结 五、代码总结 前言 在这篇博客中&#xff0c;我会给大家分享二分查找及其扩展。 这是链接-&…

【C#】NET 9中LINQ的新特性-CountBy

前言 在 .NET 中,使用 LINQ 对元素进行分组并计算它们的出现次数时,需要通过两个步步骤。首先,使用 GroupBy方法根据特定键对元素进行分类。然后,再计算每个组元素包含个数。而随着 .NET 9 版本发布,引入了一些新特性。其中 LINQ 引入了一种新的方法 CountBy,本文一起来了…