好的代码自己会说话,清晰的逻辑与优雅的结构,是程序员与世界对话的方式。

前言

  这是我自己学习Linux系统编程的第五篇笔记。后期我会继续把Linux系统编程笔记开源至博客上。 

  上一期笔记是关于进程

【Linux】进程-CSDN博客https://blog.csdn.net/hsy1603914691/article/details/147628805

文件

文件的概念

1. 文件存储在 磁盘中,而 磁盘是一种 永久性存储介质,因此文件在磁盘上的保存具有持久性, 断电后数据也不会丢失
2. 磁盘属于 外部设备,既可用于 数据输入,也可用于 数据输出,因此对磁盘文件的所有操作,如读取和写入, 本质上都是对外设的数据传输,统称为 IO操作

文件的性质

1. 即使是0KB的空文件也会占用磁盘空间。因为文件不仅包含实际的 文件数据,还包含 文件属性
2. 换句话说,文件的本质是 "文件数据+ 文件属性"。因此,所有的 文件操作,本质上都是对 文件内容的操作,或是对 文件属性的操作。
3. 对文件的操作,本质上是 进程在操作系统层面对文件进行访问。磁盘由操作系统进行统一管理,因此所有对文件的读写操作, 并不是直接由C/C++的标准库函数完成的,这些库函数只是为用户提供了更便捷的接口。实际上,文件的读写操作最终是通过操作系统提供的 文件相关系统调用接口来实现的。

读写文件库函数

FILE* fopen(char* path,char* mode);
fclose(FILE* fp);
fwrite(void* content, size_t size, size_t num, FILE* fp);
fread(void* content, size_t size, size_t num, FILE* fp);
fprintf(FILE* fp,char* format, ...)

1. 使用"w"模式打开文件时,文件内容会被清空然后从头开始写入新数据,这在功能上等价于Linux中的重定向操作符">"

2. 使用"a"模式打开文件时,数据会被追加到文件末尾原有内容不会被清空,这与Linux中的追加重定向操作符">>"的行为一致。

3. fopenfclosefreadfwrite等函数属于C标准库的一部分,我们称之为库函数。4. openclosereadwrite等则是操作系统提供的接口,称为系统调用接口

5. 可以认为,C标准库中的f系列函数本质上是对底层系统调用的封装,目的是提供更友好、便携的接口,方便开发者进行二次开发和跨平台使用。

文件描述符 

1. 文件描述符(fd)本质上是一个整数。在C语言中,它被封装在FILE结构体中以便于操作。其中,标准输入(stdin)对应的文件描述符是0标准输出(stdout)对应1标准错误(stderr)对应2

2. 对文件进行任何操作之前,必须先将文件加载到内核中对应的文件缓冲区中

3. 文件描述符的分配遵循以下规则:在file_struct数组中,找到当前未被使用的最小的一个下标,并将其作为新的文件描述符。

重定向原理 

1. 实现输出重定向的关键在于:将文件描述符fd所指向的文件表项复制到下标为1(stdout)的位置。这样一来,原本向1写入的数据就会被写入到fd所指向的文件中。这种操作通常通过系统调用dup2(fd,1)来实现。

2. 追加输出重定向的实现方式与普通输出重定向相同,不同之处在于打开文件时使用了 O_APPEND标志。这样在写入数据时,每次写入的内容都会自动追加到文件末尾,而不会覆盖已有内容。

3. 实现输入重定向的关键在于:将文件描述符fd所指向的文件表项复制到下标为0(stdin)的位置。这样一来,原本从0读取输入的操作就会从fd所指向的文件中读取数据。这种操作通常通过系统调用dup2(fd,0)来实现。

#include <stdio.h>          
#include <sys/types.h>      
#include <sys/stat.h>       
#include <stdlib.h>        
#include <fcntl.h>         int main()
{close(1);             int fd = open("log.txt", O_WRONLY | O_CREAT, 00644);                     printf("fd--->%d\n", fd); return 0;
}

缓冲区

两个缓冲区 

1. 缓冲区是内存中预留的一块存储空间,用于暂存输入或输出的数据。根据其对应的是输入设备还是输出设备,缓冲区可分为输入缓冲区输出缓冲区。 

2. 在读写文件时,如果没有缓冲区,则每次操作都需要通过系统调用直接访问磁盘,这会导致CPU频繁切换状态并降低效率。采用缓冲机制可以一次性加载大量数据到缓冲区,减少磁盘访问次数,加快数据处理速度。

3. 使用stdio.h库进行输入输出操作时,数据首先进入语言层面的缓冲区,并仅在用户强制刷新进程正常退出时,才会将这些数据从缓冲区写入文件的内核缓冲区

4. 在使用C语言库函数进行输入输出操作时:

  • 写入文件一般采用全缓冲机制,数据会在缓冲区填满或手动刷新时写入文件。
  • 写入到显示器则通常采用行缓冲机制,数据会在遇到换行符或缓冲区满时自动刷新到屏幕。
全缓冲区写满再刷新
行缓冲区写满再刷新,遇到换行就刷新
无缓冲区没用缓冲区
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{close(1); int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); printf("hello world!\n"); fflush(stdout);close(fd);return 0;
}

模拟封装libc库

<mylibc.h>

#include <stdio.h>#define SIZE 1024
#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2struct IO_FILE
{int flag;//打开方式int fileno;//文件描述符char buffer[SIZE];//用户层语言缓冲区int bufferlen;//缓冲区有效字符个数int flush_method;
};
typedef struct IO_FILE myfile;myfile* myfopen(const char* filename,const char* mode);
int myfwrite(const char* ptr,size_t len,myfile* stream);
void myfflush(myfile* stream);
void myclose(myfile* stream);

<mylibc.c> 

#include "mylibc.h"
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>myfile* myfopen(const char* filename,const char* mode)
{int fd=0,flag=0;if(strcmp(mode,"r")==0){flag=O_RDONLY;fd=open(filename,O_RDONLY);}else if(strcmp(mode,"w")==0){flag=O_CREAT|O_WRONLY|O_TRUNC;fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC, 0666);}else if(strcmp(mode,"a")==0){flag=O_CREAT|O_WRONLY|O_APPEND;fd=open(filename,O_CREAT|O_WRONLY|O_APPEND, 0666);}myfile* my=(myfile*)malloc(sizeof(myfile));my->fileno=fd;my->flag=flag;my->bufferlen=0;my->flush_method=FLUSH_LINE;memset(my->buffer,0,sizeof(SIZE));return my;
}int myfwrite(const char* ptr,size_t len,myfile* stream)
{memcpy(stream->buffer+stream->bufferlen,ptr,len);stream->bufferlen+=len;if(stream->flush_method==FLUSH_LINE && stream->buffer[stream->bufferlen-1]=='\n')myfflush(stream);return len;
}void myfflush(myfile* stream)
{if(stream->bufferlen > 0){write(stream->fileno,stream->buffer,stream->bufferlen);fsync(stream->fileno);}stream->bufferlen=0;
}void myclose(myfile* stream)
{myfflush(stream);close(stream->fileno);free(stream);
}

<mytest.c>  

#include "mylibc.c"int main()
{myfile* my=myfopen("log.txt","a");char* msg="hello world!\n";myfwrite(msg,strlen(msg),my);myclose(my);return 0;
}

磁盘

LBA地址和CHS地址

1. LBA地址:是一种通过线性编号直接访问硬盘上数据块的方法,简化了大容量存储设备的管理和访问。

2. CHS地址:是一种基于硬盘物理结构,使用柱面、磁头和扇区三个参数来精确定位数据位置的传统寻址方式。

3. 磁盘CHS定址过程:若要向磁盘写入数据,首先需要移动磁头至对应柱面然后等待盘片旋转使磁头对准目标扇区的起始位置,方可进行数据的读取或写入操作。

磁盘划分

1. 一块磁盘可以被划分为多个"分区"。从Windows的角度来看,会将一块磁盘划分为C盘、D盘、E盘等,这些盘符对应的就是不同的分区。

2. 磁盘的每个分区被划分为多个"块"。其大小在格式化时确定且不可更改,最常见的大小为4KB,即由连续的八个512字节的扇区组成一个""

3. 磁盘作为典型的块设备,其数据读取方式并非以扇区为单位逐个进行,而是由操作系统按块批量读取,以此提升I/O效率和整体性能。

文件系统 

ext2文件系统

1. 在 ext2文件系统中,根据分区大小会被划分为 若干个块组(Block Group) 每个块组都具有相同的结构组成。
2.  Inode和数据块跨组但不跨分区 。因此在同一个分区内部, Inode和数据块都是唯一的
3.  每个块组的开头都有一份超级块的副本,但只有第一个块组的超级块是必须存在的,其他块组可以没有。这是为了防止单个扇区损坏导致整个文件系统无法使用。
4. 分区完成后的 格式化操作,是对该分区进行分组,并在每个块组中写入超级块、块组描述符表、块位图、Inode 位图等管理信息。

1. Data Block:用于存放文件的实际内容,由一个个数据块组成。

2. Inode Table用于存储文件的属性信息,包括文件大小、所有者、权限、时间戳等属性。

3. Block Bitmap :用于追踪数据块的使用状态,它记录了哪些数据块已被占用,以及哪些数据块仍处于空闲状态。
4. Inode Bitmap :用于指示每个Inode的分配状态,其中每一位表示一个Inode是否空闲可用。
5. GDT 用于记录每个块组的属性信息。当一个分区被划分为多个块组时,每个块组都会对应一个块组描述符,每个块组描述符包含了该块组的数据信息:
  • Inode表的起始位置。
  • 数据块区的起始位置。
  • 当前块组中剩余的空闲Inode数量。
  • 当前块组中剩余的空闲数据块数量。
6. super block :用于存储文件系统的整体结构信息,描述该分区上文件系统的核心属性信息。 由于超级块保存着整个文件系统的关键结构信息,一旦其内容被损坏,将可能导致整个文件系统无法识别或访问,甚至造成数据丢失。 它记录了包括以下内容在内的关键信息:
  • 数据块和Inode的总数及当前未使用的数量。
  • 每个Block和Inode的大小。
  • 最近一次挂载的时间。
  • 最近一次写入数据的时间。
  • 其他与文件系统相关的配置和状态信息。

inode和datablock映射 

1. 由于每个分区拥有独立的Inode和数据块,所以只需知道Inode编号,就能在分区内确定其所在的组号和具体位置。然后,通过Inode中记录的映射关系,可以找到存储文件数据的具体数据块。

2. 目录本质上也是一种文件,但在磁盘上并不存在“目录”这一特定概念,只有文件属性和文件内容的区分。目录的属性与其他文件类似,其内容保存的是该目录中的文件名与Inode号之间的映射关系。

3. 因此,访问一个文件时,必须能够打开当前所在目录。具体来说,就是需要打开该目录对应的目录文件,根据其中保存的文件名与 Inode 号的映射关系,找到目标文件的 Inode,进而完成文件的访问。

软硬链接

软链接

ln -s xxx yyy创建一个指向文件xxx的软链接yyy

1. 软链接是一种特殊的文件类型,它作为一个独立的实体存在,拥有自己独立的inode编号。

2. 软链接的内容实际上是其所指向的目标文件或目录的路径。这意味着当你访问软链接时,系统会自动重定向到该链接所指向的实际文件或目录。

硬链接 

ln xxx yyy创建一个文件xxx的硬链接yyy

1. 硬链接是指向同一个inode的另一个文件名。这意味着多个文件都指向存储在磁盘上的同一份实际数据。

2. 硬链接具有以下性质:

  • 共享数据:xxxyyy共享相同的inode编号和数据块。对任一名称所做的更改都会反映在另一名称上,因为它们实际上指向的是相同的数据。
  • 独立性:虽然xxxyyy共享数据,但它们是彼此独立的文件名。删除其中一个文件名不会影响另一个文件名及其指向的数据,除非所有指向该inode的文件名都被删除,这时数据才会真正被释放。
  • 限制:硬链接只能在同一文件系统内创建,并且不能用于目录(不允许用户自己建)。

致谢

  感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能!

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

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

相关文章

【C语言】学习过程教训与经验杂谈:思想准备、知识回顾(二)

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为…

AD8021ARZ-REEL7【ADI】300MHz低噪声运放放大器,高频信号处理的性价比之选!

AD8021ARZ-REEL7&#xff08;ADI&#xff09;产品解析与推广文案 1. 产品概述 AD8021ARZ-REEL7 是 Analog Devices Inc.&#xff08;ADI&#xff09; 推出的一款 高速、低噪声运算放大器&#xff08;Op-Amp&#xff09;&#xff0c;属于 ADI的高性能放大器系列&#xff0c;专为…

WPF学习笔记(11)数据模板DataTemplate与数据模板选择器DataTemplateSelector

数据模板DataTemplate与数据模板选择器DataTemplateSelector 一、DataTemplate1. DataTemplate概述2. DataTemplate详解 二、DataTemplateSelector1. DataTemplateSelector概述2. DataTemplateSelector详解 总结 一、DataTemplate 1. DataTemplate概述 DataTemplate 表示数据…

【V6.0 - 听觉篇】当AI学会“听”:用声音特征捕捉视频的“情绪爽点”

系列回顾&#xff1a; 在上一篇 《AI的“火眼金睛”&#xff1a;用OpenCV和SHAP洞察“第一眼缘”》 中&#xff0c;我们成功地让AI拥有了视觉&#xff0c;它已经能像一个严苛的“质检员”一样&#xff0c;评判我视频的画质和动态感。 但我的焦虑并没有完全消除。因为我发现&a…

(5)pytest-yield操作

1. 简介 上一篇中&#xff0c;我们刚刚实现了在每个用例之前执行初始化操作&#xff0c;那么用例执行完之后如需要清除数据&#xff08;或还原&#xff09;操作&#xff0c;可以使用 yield 来实现。fixture通过scope参数控制setup级别&#xff0c;既然有setup作为用例之前前的操…

C++中的cmath库

在C编程中&#xff0c;数值计算是科学计算、工程应用及算法开发的基础。cmath库作为C标准库的重要组成部分&#xff0c;提供了丰富的数学函数和工具&#xff0c;能够高效处理各种数值计算任务。本文将全面解析cmath库的核心功能&#xff0c;并通过实战案例展示其强大威力。 一…

python包管理工具uv VS pip

在 Python 中&#xff0c;uv 和 pip 都是包管理工具&#xff0c;但它们的定位和特性有所不同。以下是主要区别&#xff1a; 1. pip&#xff08;传统工具&#xff09; 定位&#xff1a;Python 官方的包安装工具&#xff0c;是 Python 生态中最基础的包管理器。特点&#xff1a;…

OpenCv基础(C++)

1.图像读取与显示 #include<opencv2/opencv.hpp> using namespace cv;Mat src imread("C:/Users/16385/Desktop/new/photo/1.jpg");//读取图像 Mat src imread("C:/Users/16385/Desktop/new/photo/1.jpg",IMREAD_GRAYSCALE); //将读取的图像转为灰…

MySQL非阻塞创建索引的方法

文章目录 1. Online DDL (MySQL 5.6)2. pt-online-schema-change 工具3. gh-ost 工具4. 对于MySQL 8.0注意事项 在MySQL中创建大型表索引时&#xff0c;传统方式会阻塞表的写操作&#xff0c;影响生产环境使用。以下是几种非阻塞创建索引的方法&#xff1a; 1. Online DDL (My…

数字雨动画背景

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>数字雨动画背景</title><style>* {m…

分布式锁的概念与应用场景

一、分布式锁的核心概念 分布式锁是一种在分布式系统环境下&#xff0c;用于保证多个进程/节点对共享资源实现互斥访问的机制。其本质是通过某种中间件&#xff08;如Redis、ZooKeeper等&#xff09;实现跨节点的锁控制&#xff0c;确保在分布式环境中&#xff0c;同一时刻只有…

js代码09

题目 好的&#xff0c;我们继续。 在上一个练习中&#xff0c;我们深入探讨了 this 的复杂性。你会发现&#xff0c;ES6 引入的 class 语法在很大程度上就是为了简化 this 的使用&#xff0c;并为 JavaScript 提供一个更清晰、更熟悉的面向对象编程&#xff08;OOP&#xff0…

基于Airtest的App数据爬取实战:突破传统爬虫的边界

引言:App数据爬取的技术困境 在当今移动优先的时代,App已成为企业核心数据载体,然而​​传统爬虫技术​​在App数据获取上面临三大难题: ​​协议层屏障​​:加密HTTPS、SSL Pinning等技术阻断中间人攻击​​渲染层障碍​​:React Native、Flutter等跨平台框架使DOM解析…

【LeetCode 热题 100】560. 和为 K 的子数组——(解法一)前缀和+暴力

Problem: 560. 和为 K 的子数组 题目&#xff1a;给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。 【LeetCode 热题 100】560. 和为 K 的子数组——&#xff08;解法二&#xff09;前缀和…

android车载开发之HVAC

目前主要在做车载hvac的开发&#xff0c;主要的一些功能主要是hvac&#xff0c;座椅&#xff0c;香氛&#xff0c;设置等的一些模块&#xff0c;具体模块下&#xff0c;比如 1.空调 ac&#xff0c;智能模式&#xff08;极速降温&#xff0c;极速采暖&#xff0c;智能除味&…

深度学习 Diffusers 库(自留)

&#xff08;本文将围绕 安装Diffusers库及其依赖、理解Diffusers核心概念&#xff1a;Pipeline, Model, Scheduler 、使用预训练模型进行推理&#xff08;文生图、图生图等&#xff09; 、 自定义模型和调度器 、训练自己的扩散模型&#xff08;可选&#xff0c;需要大量资源&…

【VPC技术】基础理论篇

文章目录 概述相关基础核心知识软件定义网络SDNOverlay 技术 安全组概述 参考博客 &#x1f60a;点此到文末惊喜↩︎ 概述 相关基础 基本概念 虚拟私有云VPC&#xff1a;是一个隔离的网络环境&#xff0c;每个VPC拥有专属的IP地址范围&#xff08;CIDR&#xff09;、路由表、…

在 RK3588 Ubuntu 上编译 eglinfo:全流程实战 + 常见报错修复

dv1/eglinfo 是一个开源的 EGL 信息检测工具&#xff0c;广泛用于 OpenGL ES 图形栈调试、驱动验证和嵌入式平台图形支持排查。在 Rockchip RK3588 上编译该工具可以协助我们确认 EGL DRM 是否配置正确&#xff0c;尤其在无窗口系统&#xff08;如 eglfs、framebuffer&#xf…

开源推荐:基于前后端分离架构的WMS仓储管理系统

开源推荐&#xff1a;基于前后端分离架构的WMS仓储管理系统 &#x1f525; 在线演示地址&#xff1a;https://tob.toolxq.com/wms/wms.html 点击上方链接可直接体验系统功能和界面&#xff0c;无需安装部署 前言 在企业数字化转型的浪潮中&#xff0c;仓储管理系统&#xff08…

Redis中List类型常见的操作命令有哪些?

Redis中List类型是一个字符串列表&#xff0c;这里是一些常见的命令&#xff1a; 1&#xff09;lpush:将一个或多个值插入到列表头部。列表不存在&#xff0c;一个新的列表会被创建。 2&#xff09;rpush:将一个或多个值插入到列表尾部。 3&#xff09;lpop:移除并返回列表头…