【Linux】:文件fd

朋友们、伙计们,我们又见面了,本期来给大家带来关于文件fd的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

​ 

目录

1. 文件基础介绍

1.1 文件 = 内容 + 属性 

1.2 文件操作

1.3 文件数据

1.4 访问文件

1.5 描述文件 

2. 语言级文件接口

3. 系统级文件接口

3.1 open返回值

3.2 如何理解一切皆文件 

3.3 文件数据读写本质 

3.4 文件fd的分配规则


1. 文件基础介绍

当我们建立一个文件时,此时的文件是一个空文件,那么它要不要占据空间呢?

1.1 文件 = 内容 + 属性 

在我们的计算机内部存在许许多多的文件,这些文件默认都是在磁盘中保存的,那么在磁盘中保存的不仅仅是文件的内容,还有一些描述文件的属性,所以文件通常由两部分构成:

① 文件内容 ② 文件属性

因此我们建立的空文件也是需要占据内存空间的,占据的空间用于存储文件的属性

1.2 文件操作

文件 = 内容 + 属性,因此对文件进行操作的方式有两种:

  • ① 对文件内容操作
  • ② 对文件属性操作

1.3 文件数据

文件是由两部分构成:内容 + 属性,所以一个文件来说,文件的内容和属性都属于文件的数据,所以要能完整的存储一个文件必须既存储该文件的内容又存储文件的属性,它默认是存储在磁盘中的。

1.4 访问文件

我们要访问一个文件,通常是使用代码进行访问,所以要能访问到文件的数据,首先得打开这个文件,所以我们的代码必须运行起来,换句话说我们访问文件其实就是进程访问文件。由于文件在磁盘中存储,所以打开文件之后就会将文件从磁盘加载到内存中,加载磁盘上的文件,就一定会涉及到访问磁盘设备,这个过程由OS来做。

1.5 描述文件 

一个进程很有可能打开不止一个文件,有可能打开多个文件,又或者说多个进程打开多个文件,将这些文件打开就是加载到内存,在操作系统运行时,可能会打开很多的文件,因此为了防止打开的文件混乱,操作系统也是要将这些打开的文件管理起来的,如何管理呢?先描述、再组织!

一个文件要被打开,一定要先在内核中形成被打开的文件对象:

所以文件按照是否被打开可以分为两类:被打开文件、未被打开文件;打开的文件在内存中,未被打开的文件在磁盘中,所以我们本次研究文件操作的本质就是:进程与被打开文件之间的关系。

2. 语言级文件接口

在这里我们主要来回顾一下C语言中的文件接口:

打开文件接口:fopen

打开方式:

  • r:只读方式打开文件,文件不存在则打开失败;
  • w:写入的方式打开文件,文件不存在则创建;
  • a:追加式写入的方式打开文件,文件不存在则创建。

小细节:

以"w"方式打开文件,首先会清空文件内容,再写入;

以"a"方式打开文件,会在文件结尾开始写入,不清空。

关闭文件接口:fclose

 

写入接口:fputs、fwrite

#include <stdio.h>int main()
{// 打开FILE* fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}const char * msg = "hello\n";int cnt = 10;while(cnt--){fputs(msg, fp);  // 写入}// 关闭fclose(fp);return 0;
}

3. 系统级文件接口

我们上面写的C语言打开文件的相关接口,在底层一定封装了系统调用接口!

一个进程通过操作系统打开文件,所以操作系统一定要给我们提供系统调用接口。

接下来就来看一看文件操作相关的系统调用接口:

打开文件接口:open

这里来说一下这个标志位,标志位的常用选项有:

  • O_WRNOLY:写入
  • O_CREAT:创建
  • O_TRUNC:清空
  • O_APPEND:追加

这些标志位可以通过 | 来组合在一起使用。

文件权限我们设置为默认权限0666即可。

写入文件接口:write

关闭文件接口:close

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{// 打开文件                    写入 + 创建 + 清空int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0){perror("open");return 1;}const char *msg = "Hello Linux";// 写入write(fd, msg, strlen(msg));// 关闭close(fd);return 0;
}

 在往文件写入内容时不需要在最后面加上'\0'。

3.1 open返回值

我们一次打开多个文件,看看它们的返回值有何特点:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{// 打开文件                    写入 + 创建 + 清空int fd1 = open("log1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd2 = open("log2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd3 = open("log3.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);int fd4 = open("log4.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);printf("fd1: %d\n", fd1);printf("fd2: %d\n", fd2);printf("fd3: %d\n", fd3);printf("fd4: %d\n", fd4);// 关闭close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}

可以看到这些返回值都是一个个连续的小整数,很类似于数组的下标

接下来就从系统的层面来理解一下文件fd:

在磁盘中存在众多的文件,每打开一个文件就需要在内核中形成对应的文件结构体对象,OS既然要管理这些被打开的文件,久而久之当被打开的文件越来越多,那么OS怎么知道哪几个文件是哪个进程打开的呢?

所以在OS中就会有一个struct files_struct的结构体对象,而这个结构体中存在一个struct file *fd_array[]的结构体指针数组,该数组被我们亲切的称为进程文件描述符表。每一个进程PCB都有一个指向该数组的指针,在这个数组中,记录了进程与文件结构体对象的对应关系,所以每一个进程都可以找到自己打开的文件。

整个过程就是open先给文件创建对应的结构体对象,再将对应的文件与文件描述符表联系起来,再通过返回之将下标返回给外部。

所以文件描述符fd本质就是数组的下标,OS访问文件只认文件描述符。

那么C语言的FILE是什么呢?

它是C语言提供的一个结构体类型,它的底层必定封装了文件描述符,接下来就通过代码的方式验证一下:

#include <stdio.h>
#include <unistd.h>int main()
{// 打开文件       FILE *fp1 = fopen("log.txt1", "w");FILE *fp2 = fopen("log.txt2", "w");FILE *fp3 = fopen("log.txt3", "w");printf("fp1->fd: %d\n", fp1->_fileno);printf("fp2->fd: %d\n", fp2->_fileno);printf("fp3->fd: %d\n", fp3->_fileno);fclose(fp1);fclose(fp2);fclose(fp3);return 0;
}

那么文件描述符表中的0、1、2是什么呢?

在我们进程运行的时候会默认打开三个文件流:

  • 标准输入流:stdin (键盘)     0
  • 标准输出流:stdout(显示器)1
  • 标准错误流:stderr(显示器) 2

OS/C语言为什么要默认把这三个流打开呢?

最主要的原因是为了方便程序员进行默认的输入输出的代码编写。

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

3.2 如何理解一切皆文件 

磁盘、键盘、显示器、网卡等,这些外设统一按照文件的方式去看待,如何理解呢?

在底层的这些外设,站在方法的角度去考虑,每一种设备都有对应的读写方法,例如键盘,它具有读方法,写方法认为是空,显示器具有写方法,读方法认为是空,所以,这些设备的读写方法均不同,为了提升使用效率,在他们各自对应的文件结构体对象中会设置读写方法的函数指针,用于指向各自设备的具体读写方法,在访问这些设备时,直接去各自的文件结构体对象中调用读写方法,不需要关注底层,所以访问外设这一工作只需要访问各自对应的文件结构体,统一把它们当成文件对待,所以Linux下一切皆文件。

3.3 文件数据读写本质 

文件一般是存储在磁盘外设上的,struct file是在内核中创建,专门用来管理被打开文件的。

我们对文件进行读写操作的时候,并不能直接操作文件数据,所以在struct file存在一个字段,指向一块空间,叫做文件缓冲区,无论是我们读写文件数据,都需要先把数据加载到文件缓冲区中,这个加载的过程是由OS来完成的。所以我们在应用层进行数据读写的本质就是将内核缓冲区中的数据来回拷贝。

3.4 文件fd的分配规则

1. 进程默认已经打开了文件描述符0、1、2,可以直接使用0、1、2进行数据访问:

#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{char buffer[1024];ssize_t s = read(0, buffer, 1024);  // 从0号fd(键盘)中读取数据至bufferif(s > 0){buffer[s - 1] = 0; // 将读取的'\n'置为'\0'} write(1, buffer, strlen(buffer)); // 将buffer内容写入到1号fd(显示器)return 0;
}

2. 文件描述符分配规则:寻找最小的,没有被使用的位置,分配给指定的打开文件!

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{// 先关闭2号fdclose(2);// 重新分配int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);printf("fd: %d\n", fd); // 2close(fd);return 0;
}

  

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!        

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

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

相关文章

FastAPI 学习之路(四十一)定制返回Response

接口中返回xml格式内容 from fastapi import FastAPI, Responseapp FastAPI()# ① xml app.get("/legacy") def get_legacy_data():data """<?xml version"1.0"?><shampoo><Header>Apply shampoo here.</Header&…

[笔记] SEW的振动分析工具DUV40A

1.便携式振动分析仪 DUV40A 文档编号&#xff1a;26871998/EN SEW是一家国际化的大型的机械设备供应商。产品线涵盖电机&#xff0c;减速机&#xff0c;变频器等全系列动力设备。DUV40A是他自己设计的一款振动分析工具。 我们先看一下它的软硬件参数&#xff1a; 内置两路传…

国内外大模型 SuperCLUE 基准测试

本心、输入输出、结果 文章目录 国内外大模型 SuperCLUE 基准测试前言国内外大模型 SuperCLUE 基准测试榜单什么是中文大模型基准 SuperCLUE国内外大模型 SuperCLUE 基准测试 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_15071263 如果觉得本文对你有帮助,…

大数据开发中的数据驱动决策:关键问题与实践指南

目录 决策前的准备工作1. 我已经掌握了哪些信息&#xff1f;2. 我们已经做出决定了吗&#xff1f;3. 我们需要哪些额外信息以及何时需要&#xff1f; 决策过程中的关键问题1. 我们需要做这个决定吗&#xff1f;2. 错误地做出这个决定的代价是什么&#xff1f; 决策后的反思1. 我…

Excel第28享:如何新建一个Excel表格

一、背景需求 小姑电话说&#xff1a;要新建一个表格&#xff0c;并实现将几个单元格进行合并的需求。 二、解决方案 1、在电脑桌面上空白地方&#xff0c;点击鼠标右键&#xff0c;在下拉的功能框中选择“XLS工作表”或“XLSX工作表”都可以&#xff0c;如下图所示。 之后&…

数据库-三范式

第一范式 1 数据库所有字段都只有单一属性。 2 单一属性由基本数据类型构成。 3 数据库的表都是二维的行与列。 例如上面的例子就不满足第一范式&#xff0c;因为是可以继续拆分的&#xff0c;拆分为更多的属性。 第二范式 1 符合第一范式 2 表必须有个主建 3 其它字段可以…

印尼Facebook直播网络需要达到什么要求?

在全球化浪潮的推动下&#xff0c;海外直播正受到企业、个人和机构的广泛关注和青睐。无论是用于营销、推广还是互动&#xff0c;海外直播为各种组织提供了更多机会和可能性。本文将探讨在进行印尼Facebook直播前&#xff0c;需要满足哪些网络条件以确保直播的质量和用户体验。…

解决安卓tv 蓝牙遥控器配对后输入法弹不出来的问题

t972在蓝牙配对后&#xff0c;自带的LatinIME 输入法会出现弹不出来的现象。 经过分析&#xff0c;主要为蓝牙的kl 文件适配存在问题。解决如下&#xff1a; 1.新建 kl文件 这个需要结合选用的遥控器来设定名称&#xff0c;我这边的遥控器是按照如下配置的 Vendor_2b54_Pr…

初步探究Rust生态与图形界面编程

引言 Rust作为一种现代的、安全的系统编程语言&#xff0c;自2010年问世以来&#xff0c;逐渐在开发社区中崭露头角。它的内存安全保证、并发处理能力、以及无需垃圾回收机制的高性能特性&#xff0c;使得它成为了开发系统工具、网络服务、以及嵌入式系统的热门选择。然而&…

8-1 搭建solidity开发环境,自己定制一个truffle

8-1 搭建solidity开发环境&#xff0c;自己定制一个truffle&#xff08;react区块链实战&#xff09; 从零开始搭建一个项目 自己实现一套类似truffle的自动编译系统&#xff0c;加深理解 此处可以跳过无需自己实现编译合约的模块&#xff0c;使用已有的truffle模块即可 项目…

【JavaScript】解决 JavaScript 语言报错:Uncaught SyntaxError: Unexpected token

文章目录 一、背景介绍常见场景 二、报错信息解析三、常见原因分析1. 缺少必要的语法元素2. 使用了不正确的字符或符号3. JSON 格式错误4. 字符串未正确闭合 四、解决方案与预防措施1. 检查语法元素2. 正确使用符号和字符3. 修正 JSON 格式4. 字符串闭合 五、示例代码和实践建议…

go 切片进行链式操作并支持泛型

背景&#xff1a; 由于团队不是专业级别的go开发人员&#xff0c;主开发还是java&#xff0c;用惯了java的lambda表达式特别是流式操作&#xff0c; 所以在用go语言时&#xff0c;发现切片处理起来比较麻烦&#xff0c;看看能不能支持类似流式操作&#xff0c;我这边就研究了下…

PDF公式转Latex

文章目录 摘要数据集 UniMER介绍下载链接 LaTeX-OCRUniMERNet安装UniMER 用的数据集介绍下载链接 PDF-Extract-Kit整体介绍效果展示评测指标布局检测公式检测公式识别 使用教程环境安装参考[模型下载](models/README.md)下载所需模型权重 在Windows上运行在macOS上运行运行提取…

ESP-IDF升级到V5.2.x碰到的问题,lvgl移动v9.x。

近日在学习IDF,将原有IDF代码,升级到IDF 5以上时。发现一些API的变化,具体如下(今后碰到再继续补充): 一、官网上找到部分。截图如下 二、网上找到部分 The fixes I attempted which got the file to compile: replace gpio_pad_select_gpio() with gpio_pin_reset()re…

Ubuntu 安装搜狗输入法

搜狗输入法已支持Ubuntu1604、1804、1910、2004、2010 各系统安装步骤可能略有不同 1、添加中文语言支持 打开 系统设置——区域和语言——管理已安装的语言——在“语言”tab下——点击“添加或删除语言” 弹出“已安装语言”窗口&#xff0c;勾选中文&#xff08;简体&…

Linux vim文本编辑器

Vim&#xff08;Vi IMproved&#xff09;是一个高度可配置的文本编辑器&#xff0c;它是Vi编辑器的增强版本&#xff0c;广泛用于程序开发和系统管理。Vim不仅保留了Vi的所有功能&#xff0c;还增加了许多新特性&#xff0c;使其更加强大和灵活。 Vim操作模式 普通模式&#xf…

机器学习——L1 L2 范数 —>L1 L2正则化

1、L1范数和L2范数是机器学习和数据分析中经常使用的两种范数&#xff0c;它们之间存在多个方面的区别。 以下是关于L1范数和L2范数区别的详细解释&#xff1a; 一、定义差异 L1范数&#xff1a;也被称为曼哈顿范数&#xff0c;是向量元素的绝对值之和。对于一个n维向量x&am…

【密码学】实现消息认证或数字签名的几种方式

消息认证的目的是验证消息的完整性和确认消息的来源。数字签名的目的是不仅验证消息的完整性和来源&#xff0c;还提供了不可否认性。此外&#xff0c;数字签名还可以验证消息的创建时间&#xff0c;防止重放攻击。那么具体有哪些实现的方式呢&#xff1f; 一、仅提供消息认证…

YOLOv10改进 | 损失函数篇 | SlideLoss、FocalLoss、VFLoss分类损失函数助力细节涨点(全网最全)

一、本文介绍 本文给大家带来的是分类损失 SlideLoss、VFLoss、FocalLoss损失函数&#xff0c;我们之前看那的那些IoU都是边界框回归损失&#xff0c;和本文的修改内容并不冲突&#xff0c;所以大家可以知道损失函数分为两种一种是分类损失另一种是边界框回归损失&#xff0c;…

如何使用HTML和JavaScript读取文件夹中的所有图片并显示RGB范围

如何使用HTML和JavaScript读取文件夹中的所有图片并显示RGB范围 在这篇博客中&#xff0c;我将介绍如何使用HTML和JavaScript读取文件夹中的所有图片&#xff0c;并显示这些图片以及它们的RGB范围。这个项目使用现代浏览器提供的<input type"file" webkitdirecto…