首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux】理解Linux中一切皆文件、缓冲区、ext2文件系统、软硬链接

【Linux】理解Linux中一切皆文件、缓冲区、ext2文件系统、软硬链接

作者头像
_小羊_
发布于 2025-01-24 01:06:12
发布于 2025-01-24 01:06:12
16700
代码可运行
举报
文章被收录于专栏:C++C++
运行总次数:0
代码可运行

1、如何理解在Linux中一切皆文件?

1.1 概述

windows中是文件的东西,在linux中也是文件;其次一些在windows中不是文件的东西,比如进程、磁盘、显示器、键盘这样硬件设备也被抽象成了文件,你可以使用访问文件的方法访问它们获得信息;甚至管道,也是文件;将来我们要学习网络编程中的socket(套接字)这样的东西,使用的接口跟文件接口也是一致的。

1.2 文件类型

  1. 普通文件:这是最常见的文件类型,如文本文件、二进制文件、图像文件等。它们存储着数据或程序代码。
  2. 目录文件:目录在Linux中也被视为一种特殊的文件,它包含了目录内各个文件的文件名和指向这些文件的指针。通过访问目录文件,可以浏览和管理目录内的文件。
  3. 设备文件:设备文件代表了系统中的硬件设备,如硬盘、打印机、网络接口等。通过对设备文件的读写操作,可以实现对硬件设备的控制。设备文件通常位于/dev目录下。
  4. 套接字(Socket):套接字是一种特殊的文件,用于网络通信。在Linux中,每个套接字都有一个对应的文件描述符,通过读写这个文件描述符可以实现网络通信。
  5. 管道(Pipe):管道也是一种特殊的文件,用于进程间通信。它将一个进程的输出作为另一个进程的输入,实现了进程间的数据传递。

1.3 优势

  1. 统一接口:“一切皆文件”使得Linux系统提供了一个统一的接口来访问和管理所有资源。这降低了系统的复杂性,并提高了系统的可扩展性。
  2. 简化编程:程序员只需要熟悉文件系统的接口,就可以实现对系统资源的操作和管理。这降低了编程难度,提高了开发效率。
  3. 资源抽象:通过将所有资源都抽象为文件的形式,Linux系统实现了对资源的统一管理和访问。这有助于实现资源的共享和保护,提高了系统的安全性和可靠性。

这样做最明显的好处是,开发者仅需要使用一套API和开发工具,即可调取Linux系统中绝大部分的资源。举个简单的例子,Linux中几乎所有读(读文件,读系统状态,读PIPE)的操作都可以用read函数来进行;几乎所有更改(更改文件,更改系统参数,写PIPE)的操作都可以用write函数来进行。

2、缓冲区

2.1 为什么要引入缓冲区?

缓冲区是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

  • 引入缓冲区是为了减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
  • 调用系统调用,是有成本的,所以有个用户级缓冲区。
  • 内核级缓冲区何时刷新是由OS自主决定的,如果我们想要自己控制也是有办法的,有系统调用fsync,强制刷新。
  • 当一个进程在退出的时候,会自动刷新自己的缓冲区,fclose关闭文件的时候也会自动刷新。
  • 我们用printf等函数,首先写入到用户级缓冲区中,可以通过fflush刷新到内核级缓冲区。用户级缓冲区的作用是减少系统调用次数,内核级缓冲区的作用是减少IO次数。

2.2 缓冲类型

标准I/O提供了3种类型的缓冲区。

  • 全缓冲区:这种缓冲方式要求填满整个缓冲区后才进行IO系统调用操作。对于磁盘文件的操作通常使用全缓冲的方式访问。
  • 行缓冲区:在行缓冲情况下,当在输入和输出中遇到换行符时,标准I/O库函数将会执行系统调用操作。当所操作的流涉及一个终端时(例如标准输入和标准出),使用行缓冲方式。因为标准I/O库每行的缓冲区长度是固定的,所以只要填满了缓冲区,即使还没有遇到换行符,也会执行I/O系统调用操作,默认行缓冲区的大小为1024
  • 无缓冲区:无缓冲区是指标准I/O库不对字符进行缓存,直接调用系统调用。标准出错流stderr通常是不带缓冲区的,这使得出错信息能够尽快地显示出来。

除了上述列举的默认刷新方式,下列特殊情况也会引发缓冲区的刷新:

  1. 缓冲区满时;
  2. 执行flush语句;

通过下面这个例子来理解一下缓冲区的刷新:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	//c库函数
	printf("hell printf\n");
	fprintf(stdout, "hello fprintf\n");
	const char *str = "hello fwrite\n";
	fwrite(str, 1, strlen(str), stdout);

	//系统调用
	const char *w = "hello write\n";
	write(1, w, strlen(w));

	return 0;
}

当我们往显示器上写入时是行刷新,打印顺序和我们代码一致,如果将往显示器上写入重定向到往文件中写入,就不是行刷新了,而是缓冲区满了才刷新,所以最后先刷新了系统调用write,最后进程结束的时候再自动刷新的用户级缓冲区

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	//c库函数
	printf("hell printf\n");
	fprintf(stdout, "hello fprintf\n");
	const char *str = "hello fwrite\n";
	fwrite(str, 1, strlen(str), stdout);

	//系统调用
	const char *w = "hello write\n";
	write(1, w, strlen(w));

	//子进程
	fork();
	
	return 0;
}

如果我们在最后创建一个子进程,对于往显示器上写入时的行刷新不影响,不过将往显示器上写入重定向到往文件中写入时就和上面不一样了,因为C库函数先将字符串写入到用户级缓冲区中,最后父子进程各自把它们的缓冲区刷新了一遍,所以就有了两份数据,而系统调用write写入到用户级缓冲区后自己就将数据拷贝到内核级缓冲区了,不等到进程结束再刷新。

我们自己封装一个简单的stdio.h,来深刻理解一下数据写入的过程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once 

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>

#define SIZE 1024

#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2

struct IO_FILE
{
	int flag; // 刷新方式
	int fileno; //文件描述符
	char outbuffer[SIZE];
	int size;
	int capacity;
};

typedef struct IO_FILE mFILE;

mFILE *mfopen(const char *filename, const char *mode);
int mfwrite(const void *ptr, int num, mFILE *stream);
void mfflush(mFILE *stream);
void mfclose(mFILE *stream);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "mystdio.h"

mFILE *mfopen(const char *filename, const char *mode)
{
    int fd = -1;
	if (strcmp(mode, "r") == 0)
	{
		fd = open(filename, O_RDONLY);
	}
	else if (strcmp(mode, "w") == 0)
	{
		fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
	}
	else if (strcmp(mode, "a") == 0)
	{
		fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
	}
	if (fd < 0)
	{
		return NULL;
	}
	mFILE *mf = (mFILE*)malloc(sizeof(mFILE));
	if (mf == NULL)
	{
		close(fd);
		return NULL;
	}

	mf->flag = FLUSH_LINE;
	mf->fileno = fd;
	mf->size = 0;
	mf->capacity = SIZE;

	return mf;
}

void mfflush(mFILE *stream)
{
	if (stream->size > 0)
	{
		//刷新的本质
		write(stream->fileno, stream->outbuffer, stream->size);
		stream->size = 0;
	}
}

int mfwrite(const void *ptr, int num, mFILE *stream)
{
	//1、库函数写方法:将数据拷贝到缓冲区
	memcpy(stream->outbuffer+stream->size, ptr, num);
	stream->size += num;

	//2、检测是否刷新
	if (stream->flag == FLUSH_LINE && stream->size > 0 && stream->outbuffer[stream->size - 1] == '\n')
	{
		mfflush(stream);
	}
	return num;
}

void mfclose(mFILE *stream)
{
	if (stream->size > 0)
	{
		mfflush(stream);
	}
	close(stream->fileno);
}

3、Ext系列文件系统

文件有被打开的文件未打开的文件,前面我们介绍的都是被打开的文件,其实对于文件来说未打开的文件是占多数的,未打开的文件在磁盘上。 文件存在就要被访问,访问文件的前提是要打开文件,打开文件的前提是先要找到这个文件,那么对于未打开的文件我们如何找到它,就是文件管理系统需要做的工作。不管是内存中已打开的文件还是磁盘中未打开的文件,都要被文件系统管理起来。整个过程可以类比快递的管理系统。

我们需要讨论下面两个问题:

  1. 文件系统是怎么管理磁盘中未打开的文件的?
  2. 如何理解文件的路径?

扇区是磁盘存储数据的基本单位,通常是512KB,所以磁盘也叫块设备。磁盘就是一个元素为扇区的一维数组,数组的下标就是每一个扇区的LBA地址,OS使用磁盘,就可以用一个数字访问磁盘扇区。

  • 每个文件都有自己的inode,就像进程都有自己的PCB一样,inode是文件属性数据的集合,是一个结构体,特殊的是文件名属性不在inode中保存。Linux中,文件内容和文件属性是分开存储的。
  • 找一个文件,是通过inode编号找的。删除一个文件,只需要把inode BitmapBlock Bitmap中对应的比特位由1置0即可,所以删除一个文件后也是可以恢复的。
  • 文件系统以分区为单位,不同的分区可以是不同的文件系统。对于Super Block,一个分区内的多个组中一般只有几个组中存在,且都是一样的,这样做的目的为了备份,防止一整个分区都挂掉。
  • 格式化的本质是:向一个分区中写入文件系统。
  • 为了稳定,一个组里面inode个数,block个数都是固定的。inode以分区为单位,一个分区一套inodeinode分配的时候,只需要确定每个组的起始inodeblock也是统一编号的,和inode类似。

inodeblock是怎么映射的?

目录和普通文件类似,也有inodedatablock,其内容是文件名和inode的映射关系

文件的三个时间:

  • Access:文件最后访问时间
  • Modify:文件内容最后修改时间
  • Change:文件属性最后修改时间

4、软硬链接

ln -s file.txt file-soft.link:给对应的文件建立软链接。

1、如何理解软硬链接?

  • 软链接有独立的inode,软链接内容保存的是目标文件的路径,就像Windows中的快捷方式。
  • 硬链接不是独立的文件,没有独立的inode,硬链接本质上就是一组文件名和已经存在的文件的映射关系。
  • Linux不允许对目录新建硬链接。

2、为什么要有软硬链接?

  • 软链接最大的作用就是以最简单的方式,快速定位一个文件或目录。
  • 硬链接可以做文件备份。
  • ...就是硬链接。

unlink删除软链接。

本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux 之 详谈系统I/O文件及内核级缓冲区(看这一篇就够了)
打开文件的方式不仅仅是fopen, ifstream等流式, 语言层的方案, 其实系统才是打开文件最底层的方案. 不过, 在学习文件IO之前, 先要了解一下如何给函数传递标志位, 该方法在系统文件IO接口中会使用到:
用户11317877
2025/02/16
1640
Linux 之 详谈系统I/O文件及内核级缓冲区(看这一篇就够了)
【Linux系统#5】基础 IO(文件描述符fd & 缓冲区 & 重定向)
✨ 黄粱一梦终须醒,镜花水月总是空 🌏
IsLand1314
2025/06/02
1990
【Linux系统#5】基础 IO(文件描述符fd & 缓冲区 & 重定向)
浅入缓冲区
对应在内存中预留的一段空间;用来缓冲输入或者输出的数据;也就是对应的输入缓冲区和输出缓冲区。
羑悻的小杀马特.
2025/01/23
1150
浅入缓冲区
【Linux】基础 IO(文件描述符fd & 缓冲区 & 重定向)
💫 那么我们现在有个问题,我们编好了代码,这个文件是不是就打开了 -- 没有,因为我们把代码写好之后,这个还只是一个文本,那是不是把代码编译成可执行程序,文件就打开了 -- 答案也是没有的,把原代码编译成可执行程序仅仅是跑起来了。
IsLand1314
2024/10/23
4740
【Linux】基础 IO(文件描述符fd & 缓冲区 & 重定向)
【Linux】动静态库
静态库是一种在编译阶段将库文件的内容直接整合到目标程序中的库文件形式。使用静态库后,库的代码会成为可执行文件的一部分,运行时不需要依赖外部库。
用户11305458
2025/01/09
3600
【Linux】动静态库
【Linux篇】革新编程方式:如何开发让人眼前一亮的库
库(Library)是指一组已封装、可复用的代码集合,通常以函数、类或模块的形式提供。它将常见或复杂的功能抽象出来,供开发者直接调用,而无需重复实现。根据链接方式,可分为静态库(在编译时合并到可执行文件)和动态库(在运行时加载);根据用途,又可细分为通用工具库、领域专用库等。通过引入库,项目能够保持结构清晰、降低耦合、加快开发速度,同时也便于维护和升级。
熬夜学编程的小王
2025/04/15
1090
【Linux】基础I/O>文件系统&&软硬链接&&动静态库详解
操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问,先来直接以代码的形式,实现和上面一模一样的代码
用户10925563
2024/06/04
2150
【Linux】基础I/O>文件系统&&软硬链接&&动静态库详解
【Linux】 基础IO——自己实现文件接口FILE
创建MY_FILE结构体 内部包含文件描述符fd,输出缓冲区ou'tputbuffer 、flags刷新方法
lovevivi
2023/05/03
6950
【Linux】 基础IO——自己实现文件接口FILE
【Linux】动静态库(超详细)
💢 由于CentOS 8系统2021年12月31日已停止维护服务,CentOS 7系统将于2024年06月30日停止维护服务。CentOS官方不再提供CentOS 9及后续版本,不再支持新的软件和补丁更新。CentOS用户现有业务随时面临宕机和安全风险,并无法确保及时恢复。
IsLand1314
2024/11/19
2700
【Linux】动静态库(超详细)
Linux:基础IO(二.缓冲区、模拟一下缓冲区、详细讲解文件系统)
缓冲区作为一块内存区域,提供了一个临时存储数据的空间,帮助程序高效地处理输入和输出
是Nero哦
2024/06/17
4520
Linux:基础IO(二.缓冲区、模拟一下缓冲区、详细讲解文件系统)
[操作系统] 缓冲区的设计与实现
缓冲区是内存中预留的临时存储区域(如char outbuffer[SIZE])。内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
DevKevin
2025/02/19
2530
[操作系统] 缓冲区的设计与实现
Linux系统-基础IO
Linux基础IO 零、前言 一、C语言文件IO 1、C库函数介绍 2、stdin/stdout/stderr 二、系统文件IO 1、系统调用介绍 2、系统调用和库函数 三、文件描述符 1、open返回值 2、fd分配规则 四、重定向 1、概念及演示 2、dup2系统调用 3、重定向的原理 4、缓冲区和刷新策略 五、文件及文件系统 1、FILE 2、文件系统 3、软硬链接 六、动静态库 1、制作使用静态库 2、制作使用动态库 零、前言 本章主要讲解学习Linux基础IO流的知识 一、C语言文件IO 1
用户9645905
2022/11/30
1.5K0
Linux系统-基础IO
【Linux】基础IO(文件描述符、缓冲区、重定向)
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。
秦jh
2024/10/08
2850
【Linux】基础IO(文件描述符、缓冲区、重定向)
Linux【模拟实现C语言文件流】
在 C语言 的文件流中,存在一个 FILE 结构体类型,其中包含了文件的诸多读写信息以及重要的文件描述符 fd,在此类型之上,诞生了 C语言 文件相关操作,如 fopen、fclose、fwrite 等,这些函数本质上都是对系统调用的封装,因此我们可以根据系统调用和缓冲区相关知识,模拟实现出一个简单的 C语言 文件流
北 海
2023/07/01
7460
Linux【模拟实现C语言文件流】
【Linux】重定向与缓冲区
这三个函数 stat、fstat 和 lstat 都是 C 语言中用于获取文件的状态信息(如文件大小、权限、修改时间等)的系统调用。它们用于查询文件或目录的元数据,返回一个 struct stat 结构,结构中包含了该文件的详细信息。
用户11029103
2025/02/25
2990
【Linux】重定向与缓冲区
【Linux】探索文件I/O奥秘,解锁软硬链接与生成动静态库知识
仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针,那什么是FILE类型呢?这是C标准库自己封装的一个结构体。
用户11316056
2024/10/16
1640
【Linux】探索文件I/O奥秘,解锁软硬链接与生成动静态库知识
理解缓冲区
对于这样的代码,首先可以肯定的是printf语句先于sleep执行,既然如此那么就应该是先打印语句然后进行休眠,下面看看结果:
始终学不会
2023/04/28
5690
理解缓冲区
从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区
"Linux下一切皆文件",这是Linux的一个基本设置理念同时也是Linux的设计哲学所在。
再睡一下就好
2025/06/11
1110
从硬件角度理解“Linux下一切皆文件“,详解用户级缓冲区
【Linux系统IO】三、缓冲区
​ 目前打印结果和重定向到文件中都是按我们的预期执行的,接下来我们在代码的最后 fork() 一下,看看发生什么:
利刃大大
2025/03/04
2840
【Linux系统IO】三、缓冲区
Linux:基础IO
           可以用以下的例子去理解:快递(文件)  有被人(进程)取走的快递(打开的文件)和没被取走的快递(没打开的文件),被人取走的快递研究的是人和快递的关系(进程和文件的关系) ,而没被人取走的快递,他会被暂时安防在菜鸟驿站(磁盘)   他的数量很多(文件非常多) 所以我们打算去取的时候其实我们是会收到一个取件码的(查找该文件的信息)  然后我们根据这个号码比方说3-1113   我们会找到这个区域 然后再去找号码  所以最关键的是快递如何被按照区域划分好(对文件分门别类地存储)  这样才能方便人去取(方便我们快速找到文件并对文件的增删查改)
小陈在拼命
2024/10/21
3390
Linux:基础IO
推荐阅读
相关推荐
Linux 之 详谈系统I/O文件及内核级缓冲区(看这一篇就够了)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档