嵌入式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,一经查实,立即删除!

相关文章

pg 数据库,获取时间字段值的具体小时,赋值给其他字段

目录 1 问题2 实现 1 问题 pg 数据库&#xff0c;有一个表&#xff0c;其中有2个字段 一个是时间字段obstime &#xff0c;一个是时次ltime字段&#xff0c;int 类型&#xff0c;现在这个表里面是obstime 里面有数据&#xff0c;ltime字段 没有数据&#xff0c;现在就是批量获…

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

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

EmotionPrompt 论文精读

EmotionPrompt: Leveraging Psychology for Large Language Models Enhancement via Emotional Stimulus 论文精读 核心观点&#xff1a; 提出 emotionprompt 作为一种利用情绪刺激来增强大语言模型性能的方式。该方法通过在原始提示中添加情绪刺激&#xff0c;例如“这对我的…

[每日一练]患某种疾病的患者,正则表达式的匹配

该题目来源于力扣&#xff1a; 1527. 患某种疾病的患者 - 力扣&#xff08;LeetCode&#xff09; 题目要求&#xff1a; 患者信息表&#xff1a; Patients ----------------------- | Column Name | Type | ----------------------- | patient_id | int | | pati…

ZED-Mini提示“no GPU detected“错误解决

用ZEDm提示no GPU detected roslaunch zed_wapper zedm.launch的时候会提示no GPU detected 应该是nvidia GPU的问题&#xff0c;于是nvidia-smi检查是否正确安装&#xff0c;结果报错 Failed to initialize NVML: Driver/library version mismatch查了一些博客 解决Failed …

年中企业业绩管理新篇章:用友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;要么自己研究 红胖子网络科技博…

334.递增的三元子序列

给你一个整数数组 nums &#xff0c;判断这个数组中是否存在长度为 3 的递增子序列。 如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k &#xff0c;使得 nums[i] < nums[j] < nums[k]&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例…

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

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

java异常习题

1.有关下述Java代码描述正确的选项是 public class TestClass {private static void testMethod(){System.out.println("testMethod");}public static void main(String[] args) {((TestClass)null).testMethod();}} A.编译不通过 B.编译通过&#xff0c;运行异常…

深入解析Java多线程:源码级别的分析与实践

Java的多线程机制是其并发编程的核心&#xff0c;对于高性能和高并发应用的开发至关重要。 一、Java多线程的基础 1.1 创建线程的几种方式 在Java中&#xff0c;有几种创建线程的方式&#xff1a; 继承Thread类&#xff1a; class MyThread extends Thread {public void run…

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;非常实用。 效果 原理分析 要实现此类效…

docker镜像转移,mac Linux

起因是镜像在互联网&#xff0c;而公司内网没有网络&#xff0c;需要跨平台转移&#xff0c;且系统不一样&#xff0c;一个Arm,一个linux。正常进行后&#xff0c;报错系统编译问题&#xff0c;特将处理过程总结一下 在 mac在pull镜像的时候需要设置参数&#xff0c;确保下载的…

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

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