嵌入式Linux系统编程 — 1.2 文件管理与错误处理

目录

1 Linux 系统如何管理文件

1.1 什么是静态文件

1.2 扇区(Sector)和块(Block)概念?

1.3 inode

1.4 进程控制块(PCB)

2 返回错误处理与 errno

2.1 errno变量介绍

2.3 perror函数介绍

3 exit、 _exit、 _Exit函数

3.1 exit函数

3.2 _exit函数

3.3 _Exit函数

3.4 示例代码


1 Linux 系统如何管理文件

当我们使用open函数打开一个文件时,操作系统内核会分配一块内存区域作为缓冲区,并将磁盘上存储的静态文件数据加载到这个内存区域中,以便进行管理和缓存。这个加载到内存中的文件数据被称为动态文件或内核缓冲区。此后,对该文件的所有读写操作都是针对内存中的动态文件进行的,而不是直接操作磁盘上的静态文件。当对动态文件进行修改后,内存中的数据与磁盘上的数据就会不同步了,数据的同步是由内核负责的,内核会在适当的时候将内存中的动态文件内容刷新回磁盘,确保磁盘上的文件与内存中的数据保持一致。

在Linux操作系统中,进程控制块(PCB)是操作系统用来存储和管理每个进程信息的核心数据结构,其中包含文件表,记录了进程打开的所有文件的文件描述符和状态信息。文件表中的每个条目都指向一个inode,inode是文件系统中的一个数据结构,包含了文件的元数据和指向文件数据块的指针。inode table是存储所有inode的表,操作系统通过它来访问和管理文件的inode。磁盘是物理存储设备,由扇区和块组成,文件数据最终存储在这些磁盘块中。

文件描述符表、文件表以及 inode 之间的关系(参考正点原子教程)

1.1 什么是静态文件

在Linux系统中,静态文件通常指的是那些在创建后内容不经常改变的文件,例如系统配置文件、编译后的程序可执行文件、库文件等。这些文件在系统运行过程中保持不变,不需要动态生成或频繁更新,因此被称为静态文件。静态文件的特点是它们在磁盘上占用固定的存储空间,并且可以通过文件路径和inode号直接访问。

1.2 扇区(Sector)和块(Block)概念?

在计算机存储设备中,扇区(Sector)和块(Block)是两个基本的概念,它们用于描述数据存储的基本单位:

扇区(Sector):

  • 扇区是磁盘上最小的物理存储单元,所有数据都存储在扇区中。
  • 扇区的大小通常是固定的,常见的扇区大小有512字节、4KB等。
  • 每个扇区都有唯一的地址,操作系统通过扇区地址来访问磁盘上的数据。
  • 扇区是磁盘读写操作的最小单位,即使是很小的数据也需要占用一个完整的扇区。

块(Block):

  • 块是文件系统中用于分配存储空间的基本单位,块的大小可以由文件系统决定。
  • 块的大小通常大于扇区,例如4KB、8KB、16KB等,这样可以减少文件系统中的碎片。
  • 文件系统中的块大小是可配置的,较大的块大小可以提高大文件的读写效率,但可能会浪费存储空间。
  • 块是文件系统管理数据的方式,操作系统通过块来分配和管理磁盘空间。

扇区和块之间的关系:

  • 一个块可以包含一个或多个扇区。例如,如果块的大小是4KB,而扇区大小是512字节,那么一个块将包含8个扇区。
  • 文件系统在分配存储空间时,会将文件数据存储在连续的块中,而每个块又是由连续的扇区组成的。
  • 当文件大小超过一个块的大小时,文件系统会在磁盘上分配多个块来存储文件数据。

所以由此可以知道,静态文件对应的数据都是存储在磁盘设备不同的“块”中, 那么我们在程序中调用 open 函数是如何找到对应文件的数据存储“块”的呢?

1.3 inode

inode(索引节点)和(inode 表)是什么?我们的磁盘在进行分区、格式化的时候会将其分为两个区域,一个是数据区,用于存储文件中的数据;另一个是 inode 区,用于存放 inode table(inode 表), inode table 中存放的是一个一个的 inode(也成为 inode节点),不同的 inode 就可以表示不同的文件,每一个文件都必须对应一个 inode, inode 实质上是一个结构体,这个结构体中有很多的元素,不同的元素记录了文件了不同信息,譬如文件字节大小、文件所有者、文件对应的读/写/执行权限、文件时间戳(创建时间、更新时间等)、 文件类型、 文件数据存储的 block(块)位置等等信息, 如图所示。

inode table 与 inode(参考正点原子文档)

inode(索引节点)是什么?在Linux系统中,inode(索引节点)是一种存储文件和目录元数据的数据结构,它包含了文件的权限、所有者、大小、创建和修改时间等信息,但不包含文件的实际数据内容。每个文件和目录都有一个唯一的inode号,文件系统通过inode来管理和定位文件。目录项将文件名与inode号关联起来,而硬链接是指向相同inode的多个文件名,共享文件数据;软链接则是指向其他文件inode的文件。了解inode的概念对于管理文件权限、创建链接以及有效管理文件系统至关重要。

如何查看文件的 inode 编号?

每一个文件都有唯一的一个 inode, 每一个 inode 都有一个与之相对应的数字编号,通过这个数字编号就可以找到 inode table 中所对应的 inode。

在Linux系统中,可以使用ls命令结合-i选项来查看文件的inode编号。具体命令如下:

ls -i filename

这里,filename是你想要查看inode编号的文件名。执行这个命令后,ls会列出文件及其对应的inode编号。

如果你想查看一个目录下所有文件的inode编号,可以省略文件名,直接使用:

ls -i
ls -il

1.4 进程控制块(PCB)

在 Linux 系统中, 内核会为每个进程设置一个专门的数据结构用于管理该进程,我们把这个称为进程控制块(Process control block,缩写PCB) 。进程控制块(PCB)是操作系统中用于存储和管理进程信息的关键数据结构,它包含了进程的唯一标识符(PID)、当前状态、程序计数器、寄存器集合、调度信息、内存管理信息、文件表、I/O状态、信号处理以及上下文切换信息等,是操作系统调度进程、管理资源和处理进程切换的基础,确保了进程的正确执行和管理。

2 返回错误处理与 errno

2.1 errno变量介绍

在Linux中,错误处理通常通过返回一个错误代码来实现,而更具体的错误信息则存储在全局变量errno中。errno是一个宏,它在头文件<errno.h>中定义。当系统调用或库函数失败时,它们会设置errno为特定的错误代码。

以下是处理错误的一般步骤:

  1. 调用系统函数:执行如打开文件、读取数据等操作。

  2. 检查返回值:如果函数返回-1或其他错误指示值,表示函数执行失败。

  3. 检查errno:调用perror()strerror()strerror_r()函数来获取errno对应的错误描述。

  4. 错误处理:根据错误类型决定后续操作,如重试操作、记录日志、清理资源或向用户报告错误。

以下是一个简单的示例代码,演示了如何在Linux程序中使用错误处理和errno

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd;char *filename = "file.txt"; // 一个不存在的文件名// 尝试打开文件fd = open(filename, O_RDONLY);if (fd == -1) {// 如果open失败,打印错误信息fprintf(stderr, "Error opening file '%s': %s\n", filename, strerror(errno));return 1; // 返回非零值表示出错}// 关闭文件close(fd);return 0; // 正常退出
}

在这个示例中,我们尝试打开一个名为file.txt的文件。由于这个文件不存在,open函数将失败并返回-1。然后,我们检查fd是否等于-1,如果是,我们使用strerror(errno)函数来获取与errno相关的错误消息,并打印出来。strerror函数将错误代码转换为可读的字符串。运行结果如下: 

2.2 strerror函数strerror

strerror函数用于将错误代码转换成一个人类可读的字符串。这个函数的原型如下:

char *strerror(int errnum);

其中errnum是错误代码,通常是errno宏的值,errno在发生系统调用错误时被设置。

以下是一个简单的strerror示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>int main() {// 假设我们有一个错误的errno值errno = EACCES; // 访问被拒绝// 打印错误消息printf("Error: %s\n", strerror(errno));return 0;
}

在这个示例中,我们手动设置errnoEACCES(访问被拒绝),然后使用strerror获取对应的错误消息,并打印出来。运行结果如下:

2.3 perror函数介绍

perror函数用于输出当前errno值对应的错误消息到标准错误(stderr)。这个函数的原型如下:

void perror(const char *str);

其中str是一个可选的前缀字符串,它会在错误消息之前输出,以提供上下文信息。

以下是一个简单的perror示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>int main() {FILE *fp;// 尝试打开一个不存在的文件fp = fopen("nonexistent_file.txt", "r");if (fp == NULL) {// 文件打开失败,使用perror打印错误消息perror("Failed to open file: ");} else {// 文件成功打开,执行其他操作fclose(fp);}return 0;
}

在这个示例中,我们尝试打开一个不存在的文件nonexistent_file.txt。如果文件打开失败(fpNULL),perror将输出一个带有前缀"Failed to open file: "的错误消息。运行结果如下:

3 exit、 _exit、 _Exit函数

在 Linux 系统下, 进程正常退出除了可以使用 return 之外, 还可以使用exit_exit_Exit,是3个函数用于终止进程的函数,它们的行为略有不同:

3.1 exit函数

exit函数是标准C库函数,当调用时会执行一些清理操作,比如关闭所有打开的文件描述符、刷新标准I/O库的缓冲区、调用所有注册的退出处理程序等。exit的原型如下:

void exit(int status);

参数status是一个整数,通常用于表示程序的退出状态,0通常表示成功,非0表示出错。

3.2 _exit函数

_exit是POSIX标准的函数,与exit类似,但它不会执行任何清理操作,直接终止进程。这使得_exitexit更快,因为它跳过了所有清理步骤。_exit的原型如下:

void _exit(int status);

参数status同样用于表示程序的退出状态。

3.3 _Exit函数

_Exit_exit的一个宏定义,行为与_exit完全相同。在某些系统中,_Exit可能被定义为_exit的宏,或者两者都是系统调用。

3.4 示例代码

以下是一个简单的示例,演示了如何使用exit_exit函数:

#include <stdio.h>
#include <stdlib.h>void cleanup(void) {// 这里可以执行一些清理工作printf("Cleanup resources...\n");
}int main() {// 注册exit函数atexit(cleanup);printf("Normal exit using exit()\n");exit(0); // 正常退出,执行清理工作// 下面的代码不会被执行,因为exit已经终止了程序printf("This will not be printed\n");return 0;
}
#include <stdio.h>
#include <stdlib.h>// 一个示例函数,演示_exit的使用
void immediate_exit() {printf("Immediate exit using _exit()\n");_exit(1); // 立即退出,不执行任何清理工作
}int main() {immediate_exit(); // 调用上面的函数// 下面的代码不会被执行,因为_exit已经终止了程序printf("This will not be printed\n");return 0;
}

在这个示例中,main函数中使用了exit,它在退出之前会调用所有注册的退出处理程序(例如cleanup函数)。而immediate_exit函数中使用了_exit,它将立即终止程序,不会执行任何清理工作。在实际使用中,如果你需要立即退出程序且不需要执行任何清理工作,可以使用_exit_Exit;如果你希望在退出前执行一些清理工作,应该使用exit。第一段代码运行结果如下:

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

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

相关文章

SwiftUI 利用 Swizz 黑魔法为系统创建的默认对象插入新协议方法(三)

功能需求 在 SwiftUI 的开发中,我们往往需要借助底层 UIKit 的“上帝之手”来进一步实现额外的定制功能。比如,在可拖放(Dragable)SwiftUI 的实现中,会缺失拖放取消的回调方法让我们这些秃头码农们“欲哭无泪” 如上图所示,我们在拖放取消时将界面中的一切改变都恢复如初…

年中企业业绩管理新篇章:用友BIP收入云助力高效管理!

随着市场竞争的加剧&#xff0c;年中时刻对于企业而言&#xff0c;不仅是评估上半年业绩的节点&#xff0c;更是调整策略、确保全年目标达成的关键时期。高效的业绩管理不仅需要明确的目标设定和计划制定&#xff0c;更需要借助先进的信息技术工具来提升管理效率和决策质量。在…

Java项目:98 springboot在线教育系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统共有管理员、用户等角色 本在线教育系统管理员功能有个人中心&#xff0c;用户管理&#xff0c;讲师管理&#xff0c;普通管理员管理&#xff…

Qt开发技术:Q3D图表开发笔记(四):Q3DSurface三维曲面图颜色样式详解、Demo以及代码详解

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139424086 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子网络科技博…

Linux云计算架构师涨薪班课程内容包含哪些?

第一阶段&#xff1a;Linux云计算运维初级工程师 目标 云计算工程师&#xff0c;Linux运维工程师都必须掌握Linux的基本功&#xff0c;这是一切的根本&#xff0c;必须全部掌握&#xff0c;非常重要&#xff0c;有了这些基础&#xff0c;学习上层业务和云计算等都非常快&#x…

LangChain框架介绍

LangChain 的核心组件 模型 I/O 封装 LLMs&#xff1a;大语言模型Chat Models&#xff1a;一般基于 LLMs&#xff0c;但按对话结构重新封装PromptTemple&#xff1a;提示词模板OutputParser&#xff1a;解析输出 数据连接封装 Document Loaders&#xff1a;各种格式文件的加载…

指纹考勤系统

目录 1.课题研究目的和内容 1.1 课题研究目的 1.2 课题研究内容 2.系统总体方案设计及功能模块介绍 2.1总体方案设计 2.2 ATK-301模块介绍 2.3 TFTLCD显示功能模块介绍 2.4 蜂鸣器报警功能模块介绍 2.5 时钟模块介绍 3.系统硬件设计与实现 3.1 系统硬件电…

控制应优先

先从大体上的去找规律&#xff0c;然后才是数字归纳&#xff08;更为详细的&#xff09;&#xff0c;同时控制关系应该优先&#xff08;这里是天数和位置&#xff09;。是否涉及所有对象不是广泛&#xff0c;如果是具体的数值就不是广泛。

指针的认识(传值调用和传地址调用)

学习指针的目的是使用指针解决问题&#xff0c;那什么问题&#xff0c;非指针不可呢&#xff1f; 当要求写个函数来交换两个变量的值时&#xff0c;我们稍加思索&#xff0c;可以写成如下函数&#xff1a; void Swap1(int x, int y) {int tmp x;x y;y tmp; } 那么我们来进…

Linux学习笔记8

介绍man命令 在Linux中&#xff0c;man命令用于查看系统手册页&#xff08;manual pages&#xff09;。系统手册页是关于各种Linux命令、函数库以及系统调用的详尽文档&#xff0c;能够提供关于命令的使用方法、参数说明、示例以及其他相关信息 可以利用man xxx的命令去查找某…

QT之动态加载树节点(QTreeWidget)

之前写过一篇动态加载ComboBox&#xff0c;可参见下面这篇文章 QT之动态加载下拉框&#xff08;QComboBox&#xff09; 同理QTreeWidget也可以实现动态加载&#xff0c;在一些异步加载数据&#xff0c;并且数据加载比较耗时&#xff0c;非常实用。 效果 原理分析 要实现此类效…

数字展示具有广阔发展空间 市场规模保持增长态势

数字展示具有广阔发展空间 市场规模保持增长态势 数字展示是指以数字图像为核心&#xff0c;将触摸屏、红外线感应器、三维数字图像等相结合的高层次展示行业。与传统展示方式相比&#xff0c;数字展示能够突破时间、空间及形态的局限&#xff0c;将文字、图像等各种信息转化为…

vue 封装水球图

1、 安装 echarts 与 echarts-liquidfill pnpm i echarts pnpm i echarts-liquidfill 2、组件中引入 import * as echarts from echarts import echarts-liquidfill 3、封装通用组件 <div class"waterball-chart"><div ref"chartContainer" s…

从C到C++,C++入门篇(1)

1.什么是C C是一种通用编程语言&#xff0c;由Bjarne Stroustrup在1980年代初开发&#xff0c;作为C语言的扩展。 C支持多种编程范式&#xff0c;包括过程式编程、数据抽象、面向对象编程和泛型编程等。 这种语言在操作系统、游戏开发、图形界面、嵌入式系统、分布式系统、网…

变压器中的磁化和励磁电流波形

导出变压器中的磁化电流和励磁电流的图形方法具有说明性&#xff0c;因为它们使我们能够同时可视化产生这些波形的元素&#xff0c;例如磁通波形和磁性材料的典型 BH 曲线。 所得波形表明它们不是正弦波&#xff0c;并且对于励磁电流而言&#xff0c;它也不对称。 使用傅里叶…

Facebook代运营 | Facebook广告投放步骤及要点

Facebook体量大&#xff0c;素材的更新频率快&#xff0c;通过Facebook进行广告投放的用户也越来越多&#xff0c;Facebook坐拥大量用户&#xff0c;同时有着非常科学的用户画像构建系统和推送机制&#xff0c;对于很多广告涉足的伙伴来说&#xff0c;更加的友好。 1. 创建广告…

ubuntu系统 kubeadm方式搭建k8s集群

服务器环境与要求&#xff1a; 三台服务器 k8s-master01 192.168.26.130 操作系统&#xff1a; Ubuntu20.04 k8s-woker01 192.168.26.140 操作系统&#xff1a; Ubuntu20.04 k8s-woker02 192.168.26.150 操作系统&#xff1a; Ubuntu20.04 最低配置&#xff1a;2…

斯坦福 AI 团队被曝抄袭中国大模型开源成果;Zoom 创始人谈视频会议未来丨 RTE 开发者日报 Vol.218

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

2.29G大小Win 11系统来了,老爷机也能焕发第二春

Win 11 推出近三年以来微软大搞特搞的份额入侵计划&#xff0c;以目前情况来看算是彻底失败了。 国外知名机构 statcounter 最近公布了 4 月份 Windows 各版本份额调查结果。 其中显示&#xff0c;Win 11 占比 26% 左右&#xff0c;相较 2 月份 28% 反而开起了倒滑车。 来源&…

技术管理革新:三品PLM在电子电气行业的应用

随着科技的不断进步和市场需求的日益增长&#xff0c;电子电气行业正经历着前所未有的变革。产品生命周期的缩短和更新换代的加速&#xff0c;对企业的产品研发管理提出了更高的要求。在这样的背景下&#xff0c;产品生命周期管理PLM解决方案应运而生&#xff0c;旨在帮助电子电…