当前位置: 首页 > news >正文

【Linux】文件描述符

初识文件

之前我们认识到当我们进行创建出一个空文件在磁盘上也是占用一部分空间的,因为文件的组成是由文件内容和文件属性共同构成。

文件=内容+属性,那我们对文件进行操作无外乎就是对内容和属性两个方面进行操作。

文件在磁盘上进行存储,那我们进行访问文件是如何进行访问的呢?

其实我们用户层进行访问文件的一般步骤代码→exe可执行程序→运行→访问文件,在这个过程中其实真正进行与访问文件进行直接关联的是进程。我们用户层要想进行文件的访问必须通过操作系统进行,操作系统为我们提供访问文件的函数接口,我们通过函数结构进行对文件的访问。

语言层面的访问文件的操作和操作系统中访问文件的操作的联系?

语言层面不管是C/C++/python/java进行访问文件的函数底层都是通过封装操作系统的函数接口进行实现的。

语言为什么要对操作系统中的函数进行封装?

操作系统中与文件有关系的函数都比较复杂,语言层面通过对操作系统中与文件有关系的函数进行封装,减少了我们进行访问文件的成本,并可以实现代码的跨平台性,库平台性的实现主要就是通过条件编译进行实现的,通过写三套(一般通过继承进行实现)代码,在每个操作系统下选择适配的代码方式进行运行。

既然语言层面已经将操作系统的文件接口进行封装好了,我们为什么还需要在进行学习操作系统的文件接口?

贴近底层的接口可以直接和文件进行交互,性能比较高

为什么说Linux下一切皆文件?

感性的认识:

我们在感性层次方面通常认为可读可写的称为文件,这里的可读可写是站在文件本身的角度,文件可以读取我们通过键盘输入的数据,文件也能将从键盘中读取的数据进行写到显示器上。那么对于硬件键盘而言,键盘只有读取的功能写入功能为空,同理对于显示器只有写入的能力读取的能力为空,所以说键盘和显示器这种硬件也是文件。

对文件进行下定义

站在系统层面的角度,能够进行input读取和output进行写入的设备叫做文件。也就是说绝大多数的硬件设备也是文件,例如键盘、显示器、网卡、声卡、显卡、磁盘在广义层面上都是称为文件。

C语言封装系统的接口

fopen-----打开文件函数

函数返回值

返回一个 FILE * 类型的指针,用于指向打开的文件。如果文件成功打开,返回一个指向文件的指针;如果打开文件失败,则返回 NULL。

函数参数

path:fopen函数进行打开文件的路径,可以是绝对路径也可以是相对路径

mode:fopen函数进行打开文件的方式,具体有以下几种

  • r:以只读的方式进行打开文件,流信息位于文件的开头,进行只读方式文件必须存在。
  • r+:以读写的方式进行打开文件,其他于r方式相同。
  • w:以写入方式进行打开文件,如果文件存在删除文件内容,文件不存在进行创建文件。
  • w+:以读写方式进行打开文件,其他和w方式相同。
  • a:以追加的方式进行打开文件,如果文件不存在则创建文件,如果文件存在则在文件末尾进行追加新内容。
  • a+:以读写方式进行打开文件,其他和a方式相同。

 fwrite、fread

r方式进行打开文件

w方式进行打开文件

a方式进行打开文件

语言和系统层面的文件接口的来联系

语言层面kan的文件接口其实就是对于系统接口的封装,语言层面的简单实现实际上都是对系统接口的封装,这个封装是通常是比较复杂的。

系统的文件接口

查看系统的文件接口

以打开文件的接口为例

man 2 open

打开文件(open)

参数解析:

pathname:指定打开文件的路径

flag:定义打开文件的方式和选项

常用选项

  • O_RDONLY:只读方式打开文件。
  • O_WRONLY:只写方式打开文件。
  • O_RDWR:读写方式打开文件。
  • O_CREAT:如果文件不存在,则创建该文件。
  • O_TRUNC:如果文件已经存在,并且打开方式是写入(O_WRONLY),则将文件长度截断为零。
  • O_APPEND:以追加方式打开文件

定义文件打开方式的选项是如何通过int 类型进行传参的呢?

常用选项都是大写字母进行定义,我们见过的都是宏定义,这些选项其实也是宏定义,通过位图的方式向整形进行映射。

fopen和open的联系

fopen通过 r 的方式进行打开文件对应的系统的选项是 O_RDONLY;fopen通过 w 的方式进行打开文件对应系统的选项为 O_WRONLY,O_CREAT,O_TRUNC;fopen通过a的方式进行打开文件对应系统中的选项为O_WRONLY,O_CREAT,O_APPEND。语言层面看似非常简单的操作实际上都是对底层的持续封装。

返回值解析

这里的返回值类型和语言层面的返回值类型不同,这里是通过整形进行返回,其实这只是记录文件的方式,C语言中选用了结构体指针的方式,而系统相当于选用了通过数字的方式进行记录打开文件的方式,这个数字称为文件描述符。FILE* 指向的结构体中肯定包含文件描述符。

关闭文件(close)

写入文件(write)

参数解析

fd:需要进行写入的已经打开的文件描述符

buf:指向要写入数据的缓冲区的指针。buf 应包含要写入的内容。

count:需要写入的数据的字节数。

通过umask进行修改文件的权限


文件描述符

文件描述符的初识

我们进行打开文件时,文件描述符为什么是从3开始进行连续,0、1、2这三个文件描述符为什么会消失??

其实我们在进行打开使用系统时,0,1,2这三个文件描述符就已经打开了,分别是标准输入(0),标准输出(1),和标准错误(2)。

文件描述符的理解

当时我们进行学习进程的时候,进行进程的管理时就是通过进程控制块(task_struck进行管理),task_struct中还存在一个指向files_struct的结构体指针,files_struct结构体中还存在文件指针数组,文件指针数组中又存放了每个文件的结构体信息。

进程管理:
进程通过fopen进行进行调用系统接口open,open通过文件描述符(fd)进行找到FILE结构体,然后通过FILE结构体中的FILE然后进行文件管理

文件管理:
fwrite进行调用系统接口write进行读取fd进行执行内部操作找到task_struct结构体然后通过task_struct中的指针找到files_struct结构体,然后从files_struct结构体中找到fd_array,进而根据fd_array[ fd ]找到文件的file结构体,内存文件就被找到了,从而对文件进行操作。

进程要想进行访问文件,首先需要先进行打开文件,一个进程可以打开多个文件,所以说进程:文件=1:n,所以操作系统要进行管理文件,维管理文件的方式---先描述再组织。

输出重定向

现象

通过关闭流输出的文件操作符,通过系统接口open打开log.txt文件,然后对log.txt进行写入可以发现本来应该打印到屏幕上的文件操作符fd的值并没有进行打印,字符串的内容被写到了log.txt文件中。

本质

操作系统默认打开标准输入、标准输出、标准错误通过close进行关闭标准输出,然后fd_array[1]=nullptr,通过open打开新文件,fd_array[1]存的就是新文件的struct file的地址。

注:fd的分配规则:最小的没有被占用的文件操作符

重定向的本质就是操作系统更改fd的指向

通过手动去关闭fd_array数组中文件文件操作符的指向是非常挫的,我们可以通过dup2函数进行

对于参数的理解

通过dup函数进行重定向本质就是更改fd_array数组中文件操作符的索引及对应的内核对象的关联信息指向,将老的文件操作符的指向换成新的,将数组中fd_array中原来文件操作符索引及对应的内核对象的关联信息指向新文件。oldfd改成新的文件指向

关于向显示器进行重定向

虽然标准输出和标准错误都是在显示器上进行打印,但是这两个显示器文件并非是一个文件,而是两个不同的文件,一般我们将可能含有错误的信息通过stderr或者cerr进行打印,一般的文本内容通过stdout和cout进行打印.将文件内容进行重定向到文件中,其实是将1号文件描述符所在的数组位置的指号针进行重新指向到新文件,2>文件名是将2号文件描述符所在的数组位置的指号针进行重新指向特定文件.可以通过这种方式进行错误日志的填写

一切皆文件的理念

在软件层面我们可以将所有的所有的软件都看成文件,当磁盘文件被加载到内存中就变成了进程,但是一些硬件在Linux下也是文件,这就比较有说法了,其实就是C++中的面向对象的思想,但是Linux操作系统是用C语言写的,面向对象都是通过类实现的,C语言中没有类只能通过结构体进行实现,结构体中可以包含成员属性,但是不能直接进行包含成员方法,成员方法的实现是通过函数指针进行实现的,通过结构体中的成员属性和成员方法进行实现硬件的功能,底层不同的硬件一定对应不同的操作方法,对于外设,每个外设的核心就是访问函数,都可以是write read IO有关,所有的设备都可以有自己的read和write,但是write和read的代码实现一定是不一样的

缓冲区

概念

缓冲区故名思意就是一段空间(这个空间是谁提供的呢??是用户??操作系统??C语言??)

作用及意义

用户进行写入的数据先在缓冲区中进行存放,当满足缓冲区的刷新策略时,数据才被打印到指定的文件,减少IO的次数,提高整机的效率,提高用户的响应速度。

缓冲区的刷新策略

  • 行刷新
  • 满刷新
  • 立即刷新
  • 特殊情况
    • 用户强制刷新
    • 进程退出

在语言层面(C语言)和系统层面执行打印带屏幕上时,能够将内容进行打印,当将内容进行重定向到文件中时,语言层面的内容并没有在文件中进行显现,通过fflush进行刷新之后即可在文件中进行显现

缓冲区的认识

对于所有的外设来说,更加的都倾向于全缓冲,缓冲区满了才会进行刷新,目的是减少IO的次数,在进行IO的过程中最耗时间的不是数据量的大小而是进行IO的次数。

对于显示器来说就是行缓冲设备,之所以是行缓存设备是需要进行照顾用户的体验,极端条件下是可以进行自定义规则的。但是磁盘文件就是全缓冲--主要是处于效率考量。

缓冲的存在位置

现象

通过fork()函数进行创建子进程,像显示器中进行打印和向文件中进行打印,系统层面未出现任何不同,而语言层面出现了不同,通过上面的现象我们可以知道缓冲区其实是C语言层面进行维护的

解释现象

当向显示器进行打印时,显示器的刷新策略是行刷新,当代码进行到fork时,打印的函数都已经执行完了,内容也进行打印到了显示器上,fork函数不会产生任何影响;但是当向文件中进行打印时,此时刷新策略变成了满刷新,此时当代码运行到fork时,代码全部都在C语言的缓冲区中,缓冲区中的代码也是属于父进程,此后共享的代码将进行写实拷贝,就导致了语言层面的打印呈现双倍的状态

FILE结构体中不仅只包含了文件描述符,还包含了缓冲区结构

相关文章:

【Linux】文件描述符

初识文件 之前我们认识到当我们进行创建出一个空文件在磁盘上也是占用一部分空间的,因为文件的组成是由文件内容和文件属性共同构成。 文件内容属性,那我们对文件进行操作无外乎就是对内容和属性两个方面进行操作。 文件在磁盘上进行存储,…...

大语言模型的个性化综述 ——《Personalization of Large Language Models: A Survey》

摘要: 本文深入解读了论文“Personalization of Large Language Models: A Survey”,对大语言模型(LLMs)的个性化领域进行了全面剖析。通过详细阐述个性化的基础概念、分类体系、技术方法、评估指标以及应用实践,揭示了…...

AI 编程工具—Cursor进阶使用 Agent模式

AI 编程工具—Cursor进阶使用 Agent模式 我们在使用Cursor 的是有,在Composer 模式下,提交的是有两种模式 Normal 模式,也就是默认的模式Agent 模式Agent 模式可以帮我们生成代码文件,执行程序,安装依赖,并且完成一些列的工作 这里有个点很重要就是在Agent 模式下,Cur…...

【AI大模型】DeepSeek API大模型接口实现

目录 一、DeepSeek发展历程 2023 年:创立与核心技术突破 2024 年:开源生态与行业落地 2025 年:多模态与全球化布局 性能对齐 OpenAI-o1 正式版​ 二、API接口调用 1.DeepSeek-V3模型调用 2.DeepSeek-R1模型调用 三、本地化部署接口调…...

Qt展厅播放器/多媒体播放器/中控播放器/帧同步播放器/硬解播放器/监控播放器

一、前言说明 音视频开发除了应用在安防监控、视频网站、各种流媒体app开发之外,还有一个小众的市场,那就是多媒体展厅场景,这个场景目前处于垄断地位的软件是HirenderS3,做的非常早而且非常全面,都是通用的需求&…...

Kafka分区策略实现

引言 Kafka 的分区策略决定了生产者发送的消息会被分配到哪个分区中,合理的分区策略有助于实现负载均衡、提高消息处理效率以及满足特定的业务需求。 轮询策略(默认) 轮询策略是 Kafka 默认的分区策略(当消息没有指定键时&…...

【归属地】批量号码归属地查询按城市高速的分流,基于WPF的解决方案

在现代商业活动中,企业为了提高营销效果和资源利用效率,需要针对不同地区的市场特点开展精准营销。通过批量号码归属地查询并按城市分流,可以为企业的营销决策提供有力支持。 短信营销:一家连锁餐饮企业计划开展促销活动&#xf…...

为AI聊天工具添加一个知识系统 之78 详细设计之19 正则表达式 之6

本文要点 要点 本项目设计的正则表达式 是一个 动态正则匹配框架。它是一个谓词系统:谓词 是运动,主语是“维度”,表语是 语言处理。主语的一个 双动结构。 Reg三大功能 语法验证、语义检查和 语用检验,三者 :语义约…...

使用Java操作Redis数据类型的详解指南

SEO Meta Description: 详细介绍如何使用Java操作Redis的各种数据类型,包括字符串、哈希、列表、集合和有序集合,提供代码示例和最佳实践。 介绍 Redis是一种开源的内存数据结构存储,用作数据库、缓存和消息代理。它支持多种数据结构&#…...

一表总结 Java 的3种设计模式与6大设计原则

设计模式通常分为三大类:创建型、结构型和行为型。 创建型模式:主要用于解决对象创建问题结构型模式:主要用于解决对象组合问题行为型模式:主要用于解决对象之间的交互问题 创建型模式 创建型模式关注于对象的创建机制&#xf…...

Hive on Spark优化

文章目录 第1章集群环境概述1.1 集群配置概述1.2 集群规划概述 第2章 Yarn配置2.1 Yarn配置说明2.2 Yarn配置实操 第3章 Spark配置3.1 Executor配置说明3.1.1 Executor CPU核数配置3.1.2 Executor内存配置3.1.3 Executor个数配置 3.2 Driver配置说明3.3 Spark配置实操 第4章 Hi…...

Java集合面试总结(题目来源JavaGuide)

问题1:说说 List,Set,Map 三者的区别? 在 Java 中,List、Set 和 Map 是最常用的集合框架(Collection Framework)接口,它们的主要区别如下: 1. List(列表) 特点&#xf…...

计算机网络 应用层 笔记1(C/S模型,P2P模型,FTP协议)

应用层概述: 功能: 常见协议 应用层与其他层的关系 网络应用模型 C/S模型: 优点 缺点 P2P模型: 优点 缺点 DNS系统: 基本功能 系统架构 域名空间: DNS 服务器 根服务器: 顶级域…...

ES6基础内容

ES 全称 EcmaScript ,是脚本语言的规范&#xff0c;而平时经常编写的 JavaScript 是 EcmaScript 的一种实现&#xff0c;所以 ES 新特性其实指的就是 JavaScript 的新特性。 一、 let变量声明和声明特性 1.1 变量声明 <!DOCTYPE html> <html lang"en">…...

DeepSeek本地部署的一些使用体会

春节期间我也尝试了一下Deepseek的本地部署&#xff0c;方案选用了Ollama Chatbox或AnythingLLM。Chatbox里有很多有意思的“助手”&#xff0c;而AnythingLLM支持本地知识库。 网上教程很多&#xff0c;总的来说还是很方便的&#xff0c;不需要费太多脑子。甚至可以这么说&a…...

鲸鱼算法 matlab pso

算法原理 鲸鱼优化算法的核心思想是通过模拟座头鲸的捕食过程来进行搜索和优化。座头鲸在捕猎时会围绕猎物游动并产生气泡网&#xff0c;迫使猎物聚集。这一行为被用来设计搜索策略&#xff0c;使算法能够有效地找到全局最优解。 算法步骤 ‌初始化‌&#xff1a;随机生成一…...

013-51单片机红外遥控器模拟控制空调,自动制冷制热定时开关

主要功能是通过红外遥控器模拟控制空调&#xff0c;可以实现根据环境温度制冷和制热&#xff0c;能够通过遥控器设定温度&#xff0c;可以定时开关空调。 1.硬件介绍 硬件是我自己设计的一个通用的51单片机开发平台&#xff0c;可以根据需要自行焊接模块&#xff0c;这是用立创…...

在Vue3 + Vite 项目中使用 Tailwind CSS 4.0

文章目录 首先是我的package.json根据官网步骤VS Code安装插件验证是否引入成功参考资料 首先是我的package.json {"name": "aplumweb","private": true,"version": "0.0.0","type": "module","s…...

Leetcode—922. 按奇偶排序数组 II【简单】

2025每日刷题&#xff08;207&#xff09; Leetcode—922. 按奇偶排序数组 II 实现代码 class Solution { public:vector<int> sortArrayByParityII(vector<int>& nums) {for(int i 0, j 1; i < nums.size() - 1; i 2) {// 前奇后偶if(nums[i] % 2) {w…...

一个开源 GenBI AI 本地代理(确保本地数据安全),使数据驱动型团队能够与其数据进行互动,生成文本到 SQL、图表、电子表格、报告和 BI

一、GenBI AI 代理介绍&#xff08;文末提供下载&#xff09; github地址&#xff1a;https://github.com/Canner/WrenAI 本文信息图片均来源于github作者主页 在 Wren AI&#xff0c;我们的使命是通过生成式商业智能 &#xff08;GenBI&#xff09; 使组织能够无缝访问数据&…...

使用Posix共享内存区实现进程间通信

使用Posix共享内存区实现进程间通信 使用Posix共享内存区通常涉以下步骤: 进程A 调用shm_open 创建共享内存区进程A调用ftruncate修改共享内存区大小进程A 调用mmap将共享内存区映射到进程地址空间ptrA进程A 使用ptrA对共享内存区进程更改进程B 使用shm_open打开已有共享内存…...

家政预约小程序12服务详情

目录 1 修改数据源2 创建页面3 搭建轮播图4 搭建基本信息5 显示服务规格6 搭建服务描述7 设置过滤条件总结 我们已经在首页、分类页面显示了服务的列表信息&#xff0c;当点击服务的内容时候需要显示服务的详情信息&#xff0c;本篇介绍一下详情页功能的搭建。 1 修改数据源 在…...

【C语言】指针详细解读2

1.const 修饰指针 1.1 const修饰变量 变量是可以修改的&#xff0c;如果把变量的地址交给⼀个指针变量&#xff0c;通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制&#xff0c;不能被修改&#xff0c;怎么做呢&#xff1f;这就是const的作⽤。 #in…...

MongoDB 聚合

MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值&#xff0c;求和等)&#xff0c;并返回计算后的数据结果。 有点类似 SQL 语句中的 count(*)。 aggregate() 方法 MongoDB中聚合的方法使用aggregate()。 语法 aggregate() 方法的基本语法格式如下所示&#xff1…...

LabVIEW涡轮诊断系统

一、项目背景与行业痛点 涡轮机械是发电厂、航空发动机、石油化工等领域的核心动力设备&#xff0c;其运行状态直接关系到生产安全与经济效益。据统计&#xff0c;涡轮故障导致的非计划停机可造成每小时数十万元的经济损失&#xff0c;且突发故障可能引发严重安全事故。传统人…...

机器学习在地震预测中的应用

## 1. 机器学习与地震预测 地震是自然界的一种极端灾害&#xff0c;其发生常常给人们的生命和财产带来极大的威胁。虽然科学家们一直在寻求可靠的方法来预测地震&#xff0c;但由于地震预测本身的复杂性&#xff0c;长期以来难以取得根本性突破。然而&#xff0c;近年来&#x…...

总结11..

#include <stdio.h> #include <string.h> #define MAXN 1001 #define MAXM 1000001 int n, m; char maze[MAXN][MAXN]; int block[MAXN][MAXN]; // 标记每个格子所属的连通块编号 int blockSize[MAXN * MAXN]; // 记录每个连通块的大小 int dx[] {0, 0, 1, -1};…...

c++ 定点 new 及其汇编解释

&#xff08;1&#xff09; 代码距离&#xff1a; #include <new> // 需要包含这个头文件 #include <iostream>int main() {char buffer[sizeof(int)]; // 分配一个足够大的字符数组作为内存池int* p new(&buffer) int(42); // 使用 placement new…...

Linux 传输层协议 UDP 和 TCP

UDP 协议 UDP 协议端格式 16 位 UDP 长度, 表示整个数据报(UDP 首部UDP 数据)的最大长度如果校验和出错, 就会直接丢弃 UDP 的特点 UDP 传输的过程类似于寄信 . 无连接: 知道对端的 IP 和端口号就直接进行传输, 不需要建立连接不可靠: 没有确认机制, 没有重传机制; 如果因…...

springCload快速入门

原作者&#xff1a;3. SpringCloud - 快速通关 前置知识&#xff1a; Java17及以上、MavenSpringBoot、SpringMVC、MyBatisLinux、Docker 1. 分布式基础 1.1. 微服务 微服务架构风格&#xff0c;就像是把一个单独的应用程序开发为一套小服务&#xff0c;每个小服务运行在自…...