【Linux】文件IO的系统接口 | 文件标识符

🪐🪐🪐欢迎来到程序员餐厅💫💫💫

          主厨:邪王真眼

主厨的主页:Chef‘s blog  

所属专栏:青果大战linux

总有光环在陨落,总有新星在闪烁

最近真的任务拉满了,真想一直安安静静的敲代码啊,补药来打扰我了。 

我们前面这几篇博客算是把进程这个东西翻了个底朝天,接下来要做的就和文件相关了。为了方便后面对接口的调用,我会把我们要用的接口都在这里讲清楚。

文件接口可以分为两大类,一类是系统接口,一类是语言接口,当然了语言接口都是对系统接口的封装以方便用户使用。


C语言文件接口 

fopen

fopen是 C 语言中的一个标准库函数,用于打开一个文件。它的函数原型在<stdio.h>头文件中定义,

FILE * fopen ( const char * filename, const char * mode );
  • filename是一个字符串,表示要打开的文件的路径(绝对路径或相对路径)

  • mode也是一个字符串,用于指定文件的打开模式

mode参数

  • 只读模式(r当使用"r"模式打开一个文件时,程序只能从文件中读取数据。如果文件不存在,fopen会返回NULL

  • 只写模式(w"w"模式打开文件时,会创建一个新文件(如果文件不存在),或者清空原有文件内容(如果文件已存在),然后用于写入数据。例如:

  • 追加模式(a​​​​​​​使用"a"模式打开文件时,会在文件末尾追加数据。如果文件不存在,会创建一个新文件用于追加。

返回值是一个FILE*的指针,可以通过该值对文件进行进一步操作,FILE是一个结构体类型,里面存放了关于文件的各种信息,当然了,学C语言时的我们是不会去深入探究的,但学Linux的我们就需要好好研究它了。

fclose

fclose是 C 语言中用于关闭文件的标准库函数。,定义在<stdio.h>头文件中。

int fclose(FILE *stream);

stream是我们要关闭的文件的指针

成功关闭文件返回0,失败返回EOF(-1)


Linux的IO接口

在一个进程启动时,会自动打开三个输入输出流,即stdin,stdout,stderr,标准输入流,标准输出流,标准错误流,他们对应的硬件分别的是键盘,显示器,显示器,但是这些硬件被包装成了文件的形式,使得我们可以使用操作文件的方式通过FILE*指针操作他们

open

他是一个系统接口,用于打开文件

 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 int open(const char *pathname, int flags);
 int open(const char *pathname, int flags, mode_t mode);
  • pathname是一个字符串,用于指定要打开或创建的文件的路径和名称。

  • flags是一个整数,用于指定文件的打开方式,

flags是以位图的形式被使用的,而下面这些选项其实就是整型所定义的宏,要用哪些选项直接通过按位或传参就行

  • 只读方式(O_RDONLY进程只能从指定的文件中读取数据。
  • 只写方式(O_WRONLY允许进程向文件中写入数据,但不能读取。
  • 读写方式(O_RDWR允许进程对文件进行读取和写入操作。
  • O_CREAT(创建文件):如果要打开的文件不存在,就创建一个新的文件。
  • O_APPEND(追加模式):用于在文件末尾追加数据。当文件以O_APPEND标志打开后,每次写入操作都会将数据添加到文件的末尾,而不会覆盖已有的内容。
  • O_TRUNC。用于截断现有文件的内容,在文件以可写方式打开时,将文件长度设置为 0。

如果你想在flags里传入多个宏,那就要以按位或的方式传参

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
    open("./test.txt",O_WRONLY);
    return 0;
}

这段代码在执行后,发现没有test.txt文件所以就什么也不做,而下面这段代码则是创建了该文件 

int main(){
    open("./test.txt",O_WRONLY |O_CREAT);
    return 0;
}

但是这个文件是被标红了的,注意,这个不是我干的是OS干的,原因是他的权限是随机生成的,OS认为有问题所以给他标红,要解决这个问题就要引入第三个参数了

mode参数主要在创建新文件(O_CREAT被指定)时使用,用于指定新文件的权限。

int main(){
    open("./test.txt",O_WRONLY |O_CREAT,0666);
    return 0;
}

 此时权限就被设置好了,当然了结果并不是我们传入的0666,而是0664,这是因为我们传入的是初始权限,而最后文件的权限还要在经过掩码修改,对掩码不清楚的可以移步这里Linux的权限讲解


文件描述符fd

好了,关于剩下的文件系统接口都需要一个叫做fd的参数,所以我们插叙一下,先来学fd

开宗明义:所谓的整型fd,其实就是数组下标

文件是等于文件内容+属性(创建时间、所属人等)的,在打开文件之前这些信息都被保存在了磁盘上。

假设我们创建了一份代码A,它里面包含一段打开文件的代码。那么当他被编译链接成可执行文件并且被执行时,就会成为进程A在CPU上执行代码,当CPU执行到了打开文件的代码段时,open函数被执行,根据冯诺依曼体系结构一个文件与CPU交互首先要加载到内存,于是文件就被从磁盘加载到了内存。在此基础上结合进程的知识,我们可以猜想电脑种可能有几十、几百个文件都被打开了,那这么多文件怎么被有效的管理呢?

因此,OS其实为文件设计了一个结构体struct file,去描述文件,之后再通过各种数据结构把这些结构体组织起来。关于这个结构体有什么我们暂且不讨论。

首先我们要明确,一个进程是可以打开多个文件的的,只需要写很多open函数就可以了,那么怎么把打开的文件和进程联系起来呢?很简单,我们在task_struct中加入一个struct file*的数组就可以了

具体是这样的,task_struct中有一个struct files_struct*类型的变量叫做files,它就是负责管理该进程所打开的文件信息的,该结构体中有一个数组struct file* fd_array,而文件描述符fd就是该数组中的下标,所以我们可以通过fd找到对应的文件。

在系统层面文件标识符fd是访问文件的唯一方式

现在我们就理解了为什么open函数的返回值是int类型,因为这个返回值就是被打开的文件fd

我们通过下面的代码展示一下

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
   int a1= open("./test1.txt",O_WRONLY |O_CREAT,0666);
   int a2= open("./test1.txt",O_WRONLY |O_CREAT,0666);
   int a3= open("./test1.txt",O_WRONLY |O_CREAT,0666);
   int a4= open("./test1.txt",O_WRONLY |O_CREAT,0666);
   int a5= open("./test1.txt",O_WRONLY |O_CREAT,0666);
   int a6= open("./test1.txt",O_WRONLY |O_CREAT,0666);
printf("%d\n%d\n%d\n%d\n%d\n%d\n",a1,a2,a3,a4,a5,a6);
   return 0;
}

可以看到系统在给文件分配fd时,就是递增的,毕竟数组下标本身也是递增的嘛,但是为什么我们打开的第一个文件fd时3呢 ?不应该是0吗,

原因很简单,每个进程在启动时都会默认打开三个输入输出流:stdin(标准输入流),stdout(标准输出流),stderr(标准错误流),他们的fd分别是0,1,2。

在验证这点之前先来点小分析:

现在我们知道c语言的文件接口都是对系统文件接口的封装,而系统文件接口想要被调用去访问文件做IO就需要传入fd(刚说了阿,在系统层面文件标识符fd是访问文件的唯一方式),可是我们随便看一个c语言的文件接口(如下图的fread)发现他的参数只有FILE*这个指针,于是我们就可以猜测fd是包含在FILE这个结构体中的。

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
printf("%d\n",stdin->_fileno);
printf("%d\n",stdout->_fileno);
printf("%d\n",stderr->_fileno);
    return 0;
}

 

我们也确实在FILE这个结构体中找到了一个成员fileno,它就是fd!

现在我们理解了fd就可以愉快的学习别的系统文件接口了


close

传入文件fd,即可关闭它

成功返回0,失败返回-1

 #include <unistd.h>

 int close(int fd);

 write

向文件写入

 #include <unistd.h>

 ssize_t write(int fd, const void *buf, size_t count);
  • fd:要被写入的文件

  • buf:要写入的内容

  • count:要写入内容的大小,单位字节

读取成功返回写入内容的字节数,失败返回-1

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
int fd1=open("./test1.txt",O_WRONLY);
write(fd1,"HELLO",5);
close(fd1);
return 0;
}

结果是我们确实向test1.txt写入的HELLO

重点:

虽然我们传入的参数是字符串,但是write的buf类型是void,也就是什么参数都可以。

我们看下面的代码

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
int a=12345;
write(1,&a,4);
return 0;
}

我们在试着把变量a的值输出到显示器

然而最后的输出结果不是12345,而是90,这是因为write采用的是文本输入!

当我们把a的地址传入进去后 他会以字符的方式分析。我的linux上是小端存储,12345的十六进制是0x00 00 30 39 ,所以他会依次解析一个字节大小的地址,分别是39,30,00,00,换成十进制就是57,48,0,0,在转换成字符就是‘9’、‘0、、’\0'.'\0',所以最后的输出结果是90.

于是我们就知道,当我们要输出的不是char类型时,还要先把要输出的值转化为字符串形式,如下

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
int a=12345;
char buff[1024];
snprintf(buff,1024,"%d",a);
write(1,buff,4);
return 0;
}

这样显然是不方便的,于是c语言就封装出了printf等接口。 


read

读取成功返回读取内容的字节数,开始读取时就已经是文件末尾则返回0,失败返回-1

ssize_t就是有符号整型

  #include <unistd.h>

  ssize_t read(int fd, void *buf, size_t count);
  • fd:要用于读取数据的文件

  • buf:要把数据读到哪个指针所指向的空间

  • count:要读取的字节数

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
char arr[10];
int fd1=open("./test1.txt",O_RDONLY);
int first=read(fd1,arr,5);
printf("%d\n%s\n",first,arr);
int second=read(fd1,arr,5);
printf("%d\n",second);
close(fd1);
return 0;
}

第一次从test.txt读取了五个字节的字符串,所以返回值是5,第二次开始读取时已经到了文件末尾所以返回值是0

与write一样,read在从文件读取信息时也是文本读入,即读取的是字符或者字符串,假如我们要给一个int变量输入值,就要先把读取进来的字符串格式化

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
int a;
char buff[1024];
read(0,buff,1024);
sscanf(buff,"%d",&a);
printf("a的值:%d\n",a);
return 0;
}

于是乎,scanf诞生了,他封装了read以方便我们的使用。

----------------------------------创作不易,觉得有帮助的话就点赞支持一下吧-----------------------------------​​​​​​​

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

时序论文23|ICML24谷歌开源零样本时序大模型TimesFM

论文标题&#xff1a;A DECODER - ONLY FOUNDATION MODEL FOR TIME - SERIES FORECASTING 论文链接&#xff1a;https://arxiv.org/abs/2310.10688 论文链接&#xff1a;https://github.com/google-research/timesfm 前言 谷歌这篇时间序列大模型很早之前就在关注&#xff…

OpenAI 助力数据分析中的模式识别与趋势预测

数据分析师的日常工作中&#xff0c;发现数据中的隐藏模式和预测未来趋势是非常重要的一环。借助 OpenAI 的强大语言模型&#xff08;如 GPT-4&#xff09;&#xff0c;我们可以轻松完成这些任务&#xff0c;无需深厚的编程基础&#xff0c;也能快速上手。 在本文中&#xff0…

基于深度学习的点云分割网络及点云分割数据集

点云分割是根据空间、几何和纹理等特征对点云进行划分&#xff0c;使得同一划分内的点云拥有相似的特征。点云的有效分割是许多应用的前提&#xff0c;例如在三维重建领域&#xff0c;需要对场景内的物体首先进行分类处理&#xff0c;然后才能进行后期的识别和重建。 传统的点…

快速图像识别:落叶植物叶片分类

1.背景意义 研究背景与意义 随着全球生态环境的变化&#xff0c;植物的多样性及其在生态系统中的重要性日益受到关注。植物叶片的分类不仅是植物学研究的基础&#xff0c;也是生态监测、农业管理和生物多样性保护的重要环节。传统的植物分类方法依赖于人工观察和专家知识&…

MySQL 没有数据闪回?看 zCloud 如何补齐MySQL数据恢复能力

ENMOTECH 上一篇文章为大家介绍了某金融科技企业通过 zCloud 多元数据库智能管理平台的告警中心“警警”有条地管理告警并进行敏捷处置的实践案例。本篇跟大家继续分享该案例客户如何利用 zCloud 备份恢复模块下的Binlog解析功能补齐 MySQL 数据恢复能力&#xff0c;让运维人员…

transformer.js(四): 模型接口介绍

前面的文章底层架构及性能优化指南介绍了transformer.js的架构和优化策略&#xff0c;在本文中&#xff0c;将详细介绍 transformer.js 的模型接口&#xff0c;帮助你了解如何在 JavaScript 环境中使用这些强大的工具。 推荐阅读 ansformer.js&#xff08;二&#xff09;&…

使用 Elasticsearch 构建食谱搜索(二)

这篇文章是之前的文章 “使用 Elasticsearch 构建食谱搜索&#xff08;一&#xff09;” 的续篇。在这篇文章中&#xff0c;我将详述如何使用本地 Elasticsearch 部署来完成对示例代码的运行。该项目演示了如何使用 Elastic 的 ELSER 实现语义搜索并将其结果与传统的词汇搜索进…

1、HCIP之RSTP协议与STP相关安全配置

目录 RSTP—快速生成树协议 STP STP的缺点&#xff1a; STP的选举&#xff08;Listening状态中&#xff09;&#xff1a; RSTP P/A&#xff08;提议/同意&#xff09;机制 同步机制&#xff1a; 边缘端口的配置&#xff1a; RSTP的端口角色划分&#xff1a; ensp模拟…

hhdb数据库介绍(9-21)

计算节点参数说明 checkClusterBeforeDnSwitch 参数说明&#xff1a; PropertyValue参数值checkClusterBeforeDnSwitch是否可见否参数说明集群模式下触发数据节点高可用切换时&#xff0c;是否先判断集群所有成员正常再进行数据节点切换默认值falseReload是否生效是 参数设…

java基础概念38:正则表达式3-捕获分组

一、定义 分组就是一个小括号。 分组的特点&#xff1a; 二、捕获分组 捕获分组就是把这一组的数据捕获出来&#xff0c;再用一次。 后续还要继续使用本组的数据。 正则内部使用&#xff1a;\\组号正则外部使用&#xff1a;$组号 2-1、正则内部使用&#xff1a;\\组号 示…

使用Mac下载MySQL修改密码

Mac下载MySQL MySQL官网链接MySQL​​​​​​ 当进入到官网后下滑到community社区&#xff0c;进行下载 然后选择community sever下载 这里就是要下载的界面&#xff0c;如果需要下载之前版本的话可以点击archives&#xff0c; 可能会因为这是外网原因&#xff0c;有时候下…

【初阶数据结构篇】队列的实现(赋源码)

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…

【云计算】腾讯云架构高级工程师认证TCP--考纲例题,知识点总结

【云计算】腾讯云架构高级工程师认证TCCP–知识点总结&#xff0c;排版整理 文章目录 1、云计算架构概论1.1 五大版块知识点&#xff08;架构设计&#xff0c;基础服务&#xff0c;高阶技术&#xff0c;安全&#xff0c;上云&#xff09;1.2 课程详细目录1.3 云基础架构设计1.4…

AR智能眼镜|AR眼镜定制开发|工业AR眼镜方案

AR眼镜的设计与制造成本主要受到芯片、显示屏和光学方案的影响&#xff0c;因此选择合适的芯片至关重要。一款优秀的芯片平台能够有效提升设备性能&#xff0c;并解决多种技术挑战。例如&#xff0c;采用联发科八核2.0GHz处理器&#xff0c;结合12nm制程工艺&#xff0c;这种低…

大数据新视界 -- 大数据大厂之 Impala 性能优化:集群资源动态分配的智慧(上)(23 / 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

代理池搭建优化-(书接上回,优化改进)

炮台有效炮弹实现 声明 学习视频来自 B 站UP主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 ✍&#x1f3fb;作者…

光伏业务管理系统能解决光伏企业什么问题?

随着技术进步和市场规模的扩大&#xff0c;光伏企业面临着日益复杂的管理挑战&#xff0c;包括但不限于项目监管、运维管理、供应链优化、客户管理以及数据分析决策等方面。为了解决这些挑战&#xff0c;光伏业务管理系统应运而生&#xff0c;成为提升光伏企业运营效率、降低成…

【UE5】在材质中计算模型在屏幕上的比例

ViewProperty节点有很多有意思的变量 例如用 ViewProperty 的 tan ⁡ ( FOV / 2 ) \tan(\text{FOV} / 2) tan(FOV/2) 输出&#xff0c;用它计算模型占屏幕的比例。 &#xff08;常用于for运算的次数优化&#xff0c;也可以用于各种美术效果&#xff09; ScaleOnScreen Obje…

2024年人工智能技术赋能网络安全应用测试:广东盈世在钓鱼邮件识别场景荣获第三名!

近期&#xff0c;2024年国家网络安全宣传周“网络安全技术高峰论坛主论坛暨粤港澳大湾区网络安全大会”在广州成功举办。会上&#xff0c;国家计算机网络应急技术处理协调中心公布了“2024年人工智能技术赋能网络安全应用测试结果”。结果显示&#xff0c;广东盈世计算机科技有…

spring @Async

讨论一下 spring boot 下 使用 spring 异步执行的注解 先看下这个类&#xff1a; 这个类是 spring boot auto configure 下完成 TaskExecutor的自动配置。 1. 需要在类路径存在 ThreadPoolTaskExecutor&#xff0c;这个类是 是spring context模块下的类&#xff0c;也就是 需…