嵌入式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。第一段代码运行结果如下:

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

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

相关文章

python-验证子串

题目描述 输入两个字符串&#xff0c;验证其中一个串是否为另一个串的子串。 输入两个字符串&#xff0c; 每个字符串占一行&#xff0c;长度不超过200且不含空格。 输出 若第一个串s1是第二个串s2的子串&#xff0c;则输出(s1) is substring of(s2)否则&#xff0c;若第二个串…

【云原生】kubernetes中secret原理详解与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

LayUI的暗淡:错误的押宝了前后端不分离

LayUI是一个不错的中后台UI框架&#xff0c;贝格前端工场用的CMS就是基于layUI的&#xff0c;可以说简单轻便。除此之外&#xff0c;贝格前端工场很少接到客户要求升级LayUI界面&#xff0c;或者采用LayUI框架的。 一、LayUI官网的谢幕&#xff0c;吹起了前后端不分离模式没落…

拓扑排序详解

目录 一、拓扑排序前置知识 1.1 定义&#xff1a; 1.2 AOV网&#xff1a; 二、如何拓扑排序 2.1 运用 kahn 算法&#xff1a; 2.2 实现拓扑排序&#xff1a; 2.3 拓扑排序的应用&#xff1a; 2.4 拓扑排序编写模板&#xff1a; 三、例题练习 3.1 例题1&#xff1a;课…

浙江大爱遮阳新材料股份有限公司新品发布会圆满成功

5月29日,浙江大爱遮阳新材料股份有限公司新品发布会在上海国家会展中心举办。本次会议出席的嘉宾有浙江大爱遮阳新材料股份有限公司总经理俞彬军,常务副总王志华,上海大爱益可美遮阳科技有限公司总经理陆俊青,浙江大爱遮阳新材料股份有限公司销售经理平鸿烈,销售经理蒋扬锋和玛…

【Python】轻松打包:CentOS7上使用PyInstaller将Shell脚本转换为可执行文件的完美指南

【Python】轻松打包&#xff1a;CentOS7上使用PyInstaller将Shell脚本转换为可执行文件的完美指南 大家好 我是寸铁&#x1f44a; 总结了一篇【Python】轻松打包&#xff1a;CentOS7上使用PyInstaller将Shell脚本转换为可执行文件的完美指南✨ 喜欢的小伙伴可以点点关注 &#…

C# 生成解决方案时出现的一些异常及解决方法

一、ResolveAssemblyReference任务意外失败 在使用VS2022生成C#解决方案时&#xff0c;出现如下错误&#xff1a; 解决方法&#xff1a; 项目的依赖项出现问题&#xff0c;重新更新一下依赖项即可 二、生成Win32资源时出错 产生这个原因的主要原因是配置的应用程序的图标文…

Thesios: Synthesizing Accurate Counterfactual I/O Traces from I/O Samples——论文泛读

ASPLOS 2024 Paper 论文阅读笔记整理 问题 在设计大规模分布式存储系统时&#xff0c;I/O活动的建模至关重要。具有代表性的/O跟踪&#xff0c;可以对现有硬件、配置和策略进行详细的性能评估。假设跟踪进一步支持分析假设情况&#xff0c;例如部署新的存储硬件、更改配置和修…

【机器学习】机器学习在深度学习领域中的作用:半监督学习的视角

&#x1f440;时空之门&#x1f440; &#x1f50d;引言&#x1f388;半监督学习概述&#x1f69d;机器学习在深度学习领域中的作用☘特征提取与表示学习&#x1f340;复杂任务建模❀结合半监督学习提升性能 &#x1f680;半监督学习在深度学习中的应用场景&#x1f4d5;图像识…

使用import语句导入模块

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 创建模块后&#xff0c;就可以在其他程序中使用该模块了。要使用模块需要先以模块的形式加载模块中的代码&#xff0c;这可以使用import语句实现。im…

智能体应用开发:构建各类垂直领域的ai智能体应用

最近在做个类似的项目&#xff0c;有用到这方面的知识&#xff0c;顺便做一些记录和笔记吧&#xff0c;希望能帮到大家了解智能体应用开发 目录 引言 AI原生应用的兴起 智能体在AI中的角色 实现原理详解 机器学习基础 数据管理与关联数据库 数据结构 Embedding 检索方…

CSS - 元素竖向百分比的基准值是什么?

例如有一个外层DIV元素&#xff0c;设定width为500px&#xff0c;height为300px。然后在其内部添加一个DIV元素&#xff0c;这个时候&#xff0c;内部的DIV元素&#xff0c;如果设定height margin-top padding-top 百分比之后&#xff0c;他们的百分比基准值是什么呢&#xff1…

基于JSP的母婴用品网站系统

你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有需求可以文末加我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 管理员功能界面 用户功能界面 前台首页功能界面 …

2024-6-4 石群电路-23

2024-6-4&#xff0c;星期二&#xff0c;13:16&#xff0c;天气&#xff1a;晴&#xff0c;心情&#xff1a;晴。今天又是阳光明媚的一天&#xff0c;没有什么特别的事情发生&#xff0c;加油学习喽~ 今日观看了石群老师电路课程的第39和第40个视频&#xff0c;继续第九章的学…

C语言笔记23 •文件操作•

1.为什么要使用文件&#xff1f; 文件&#xff0c;顾名思义就是存储我们所写在电脑上的文本内容。如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失 了&#xff0c;等再次运⾏程序&#x…

视频如何转换成音频?音视频转换,4个方法

在当今数字化时代&#xff0c;我们常常需要处理各种不同格式的音视频文件。可能您有一个视频文件&#xff0c;但是您需要它的音频部分&#xff0c;或者您有一个音频文件&#xff0c;但您希望将其转换为视频格式。 无论您的需求是什么&#xff0c;音视频转换已经成为我们数字生…

人脸识别系统之动态人脸识别

二&#xff0e;动态人脸识别 1.摄像头人脸识别 1.1.导入资源包 import dlib import cv2 import face_recognition from PIL import Image, ImageTk import tkinter as tk import os注&#xff1a;这些导入语句允许您在代码中使用这些库和模块提供的功能&#xff0c;例如创建…

联邦学习数据集划分Dirichlet划分法及其可视化

文章目录 前言图片效果&#xff1a;独立同分布效果非独立同分布效果 一、参数输入输出 二、代码可视化:标签划分&#xff1a;代码调用 前言 用于实现并控制联邦学习客户端之间数据集非独立同分布&#xff0c;并将效果可视化 图片效果&#xff1a; 独立同分布效果 对不同类别…

python中的循环控制语句break与continue

学习这两个语句之前&#xff0c;我们要先了解这两个语句是什么意思&#xff1a; break&#xff1a;中断、打破的意思。所以它的跳出循环的意思 continue&#xff1a;继续的意思&#xff0c;意思是跳过当前条件&#xff0c;继续循环 新需求来了&#xff01;我们不仅要告诉 Py…

运营干货:用户运营体系

1、用户生命周期 2、用户引入阶段 3、用户留存阶段 4、用户回流阶段