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

目录

一、文件理解:

二、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)//8void 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.mzph.cn/news/888923.shtml

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

相关文章

第7章:响应式设计 --[CSS零基础入门]

什么是响应式设计 响应式设计&#xff08;Responsive Web Design, RWD&#xff09;是一种网页设计和开发的方法&#xff0c;它使网站能够根据用户的设备特性&#xff08;如屏幕尺寸、分辨率、方向等&#xff09;自动调整其布局和内容。响应式设计的目标是确保网站在不同类型的…

探索 ONLYOFFICE 8.2 版本:更高效、更安全的云端办公新体验

引言 在当今这个快节奏的时代&#xff0c;信息技术的发展已经深刻改变了我们的工作方式。从传统的纸质文件到电子文档&#xff0c;再到如今的云端协作&#xff0c;每一步技术进步都代表着效率的飞跃。尤其在后疫情时代&#xff0c;远程办公成为常态&#xff0c;如何保持团队之间…

Vue-打印自定义HTML表格

自定义打印方法 1. 准备HTML结构 首先&#xff0c;构造了一个基本的HTML页面框架&#xff0c;并设置了页面的字符编码为UTF-8&#xff0c;以确保中文和其他特殊字符能正确显示。页面的标题设置为传入的 title 参数值。 let printStr "<html><head><met…

http1.0、1.1、2.0、 3.0

http1.0、1.1、2.0、 3.0 http1.1 引入长连接&#xff0c;在1.0&#xff0c;每次请求都需要建立新的TCP连接&#xff0c;处理请求完毕后立即断开。就导致处理大量图片&#xff0c;链接等资源&#xff0c;需要大量的连接与断开&#xff0c;造成资源浪费和时间延迟。而长连接允许…

跟着问题学15——GRU网络结构详解及代码实战

1 RNN的缺陷——长期依赖的问题 &#xff08;The Problem of Long-Term Dependencies&#xff09; 前面一节我们学习了RNN神经网络&#xff0c;它可以用来处理序列型的数据&#xff0c;比如一段文字&#xff0c;视频等等。RNN网络的基本单元如下图所示&#xff0c;可以将前面的…

pytest中使用conftest做测试前置和参数化

pytest中比较高阶的应用是&#xff0c;使用conftest去做测试前置工作、测试收尾工作和参数化。conftest是pytest的一个组件&#xff0c;用于配置测试环境和参数。通过conftest, 可以创建一个可复用的测试配置文件&#xff0c;以便在多个测试模块之间共享配置信息。即&#xff0…

04 创建一个属于爬虫的主虚拟环境

文章目录 回顾conda常用指令创建一个爬虫虚拟主环境Win R 调出终端查看当前conda的虚拟环境创建 spider_base 的虚拟环境安装完成查看环境是否存在 为 pycharm 配置创建的爬虫主虚拟环境选一个盘符来存储之后学习所写的爬虫文件用 pycharm 打开创建的文件夹pycharm 配置解释器…

mvn test 失败,单独运行单元测试成功

标题mvn test 失败&#xff0c;单独运行单元测试成功 使用junit4进行单元测试时是通过的&#xff0c;但是在执行maven的test与package时测试不通过 报错信息&#xff1a; parse data from Nacos error,dataId:guoyu-new-asset-dev.yml,data: ....... 配置文件内容 ....... o…

android 富文本及展示更多组件

模拟微博 #热贴 和 用户 的这种 富文本形式组件&#xff0c;不说了&#xff0c; 直接上代码 package com.tongtong.feat_watch.viewimport android.content.Context import android.graphics.Color import android.util.AttributeSet import android.view.LayoutInflater impo…

gitlab 生成并设置 ssh key

一、介绍 &#x1f3af; 本文主要介绍 SSH Key 的生成方法&#xff0c;以及如何在GitLab上添加SSH Key。GitLab 使用SSH协议与Git 进行安全通信。当您使用 SSH密钥 对 GitLab远程服务器进行身份验证时&#xff0c;您不需要每次都提供您的用户名和密码。SSH使用两个密钥&#x…

保姆级教程Docker部署Nacos镜像

目录 1、创建挂载目录 2、拉取 Nacos 镜像 3、临时启动并复制文件 4、创建Nacos表结构 5、修改Nacos配置 6、正式启动 Nacos 7、登录Nacos 1、创建挂载目录 在宿主机上创建一个目录用于配置文件映射&#xff0c;这个目录将作为数据卷挂载到容器内部&#xff0c;使得我…

【北京迅为】iTOP-4412全能版使用手册-第六十七章 USB鼠标驱动详解

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

【银河麒麟操作系统真实案例分享】内存黑洞导致服务器卡死分析全过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 现象描述 机房显示器连接服务器后黑屏&#xff…

Java项目实战II基于微信小程序的旅游社交平台(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着移动互联网的迅猛发展&#xff0c;旅游已经成为人…

【数据库】关系代数和SQL语句

一 对于教学数据库的三个基本表 学生S(S#,SNAME,AGE,SEX) 学习SC(S#,C#,GRADE) 课程(C#,CNAME,TEACHER) &#xff08;1&#xff09;试用关系代数表达式和SQL语句表示&#xff1a;检索WANG同学不学的课程号 select C# from C where C# not in(select C# from SCwhere S# in…

IS-IS二

目录 ISIS建立邻接关系的基本条件&#xff1a; 1、接口链路类型一致 2、广播型链路上&#xff0c;接口类型一致 3、Hello包级别和类型一致 4、L1区域的ID要一致&#xff0c;L2的邻居区域ID不做要求 5、L1-2在区域ID相同下&#xff0c;即建立L1也建立L2区域ID不同只能建立…

️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南20241206

&#x1f6e0;️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南 &#x1f4dd; 引言 随着大语言模型&#xff08;LLM&#xff09;和人工智能的飞速发展&#xff0c;越来越多的开发者尝试在本地环境中部署大模型进行实验。然而&#xff0c;由于资源需求高、网络限制多…

设计模式の单例工厂原型模式

文章目录 前言一、单例模式1.1、饿汉式静态常量单例1.2、饿汉式静态代码块单例1.3、懒汉式单例&#xff08;线程不安全&#xff09;1.4、懒汉式单例&#xff08;线程安全&#xff0c;同步代码块&#xff09;1.5、懒汉式单例&#xff08;线程不安全&#xff0c;同步代码块&#…

net.sf.jsqlparser.statement.select.SelectItem

今天一启动项目&#xff0c;出现了这个错误&#xff0c;仔细想了想&#xff0c;应该是昨天合并代码&#xff0c;导致的mybatis-plus版本冲突&#xff0c;以及分页PageHelper版本不兼容 可以看见这个我是最下边的Caused by 报错信息&#xff0c;这个地方提示我 net .sf.jsqlpar…

第427场周赛: 转换数组、用点构造面积最大的矩形 Ⅰ、长度可被 K 整除的子数组的最大元素和、用点构造面积最大的矩形 Ⅱ

Q1、转换数组 1、题目描述 给你一个整数数组 nums&#xff0c;它表示一个循环数组。请你遵循以下规则创建一个大小 相同 的新数组 result &#xff1a; 对于每个下标 i&#xff08;其中 0 < i < nums.length&#xff09;&#xff0c;独立执行以下操作&#xff1a; 如…