文件系统与inode编号

news2024/12/24 8:41:43

文件描述符fd

0&1&2

Linux 进程默认情况会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2. 0,1,2对应的物理设备一般是:键盘,显示器,显示器 所以输入输出还可以采用如下方式

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

int main()
{
    char buf[1024];
    ssize_t s = read(0, buf, sizeof(buf));
    if (s > 0)
    {
        buf[s] = 0;
        write(1, buf, strlen(buf));
        write(2, buf, strlen(buf));
    }
    return 0;
}

文件描述符就是从 0 开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表 files_struct, 该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

文件描述符的分配规则

我们通过一段代码来比较直接的观察
演示代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd = open("myfile", O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    close(fd);
    return 0;
}

运行结果:

因为0,1,2文件描述符都已经被占用了,直接从3开始好像挺好理解的。我们猜测文件描述符是从没有被占用的数字从小到大分配的,所以是不是呢?我们通过下面这段代码来验证一下
演示代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd1 = open("log1.txt", O_WRONLY | O_CREAT);
    int fd2 = open("log2.txt", O_WRONLY | O_CREAT);
    int fd3 = open("log3.txt", O_WRONLY | O_CREAT);

    if (fd1 < 0 || fd2 < 0 || fd3 < 0)
    {
        perror("open");
        return 1;
    }

    printf("fd1: %d\n", fd1);
    printf("fd2: %d\n", fd2);
    printf("fd3: %d\n", fd3);
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
}
运行结果
那如果我们先关掉 0,2呢(1是标准输出,为了方便观察结果,就不关闭它啦)
来看下代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(0);
    close(2);
    int fd1 = open("log1.txt", O_WRONLY | O_CREAT);
    int fd2 = open("log2.txt", O_WRONLY | O_CREAT);
    int fd3 = open("log3.txt", O_WRONLY | O_CREAT);

    if (fd1 < 0 || fd2 < 0 || fd3 < 0)
    {
        perror("open");
        return 1;
    }

    printf("fd1: %d\n", fd1);
    printf("fd2: %d\n", fd2);
    printf("fd3: %d\n", fd3);
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
}

运行结果

结论:文件描述符是按照未分配的数字从小到大分配的。

重定向

那如果关闭1呢?
演示代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(1);
    int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    printf("hello dear programmer");
    fflush(stdout);//刷新缓冲区⭐

    close(fd);
    exit(0);
}

运行结果:
显示器上什么页没有输出,但当我们打开文件myfile发现,原本要输入到显示器的内容全部输入到文件中了,这就叫重定向。
通过这张图片,我们来了解一下重定向的本质

dup2系统调用

man dup2 
使用示例:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
    int fd = open("./log", O_CREAT | O_RDWR);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    close(1);
    dup2(fd, 1);
    for (;;)
    {
        char buf[1024] = {0};
        ssize_t read_size = read(0, buf, sizeof(buf) - 1);
        if (read_size < 0)
        {
            perror("read");
            break;
        }
        printf("%s", buf);
        fflush(stdout);
    }
    return 0;
}

运行结果:

FILE
因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。 所以C库当中的FILE必定封装了fd。
#include <stdio.h>
#include <string.h>
int main()
{
    const char *msg0 = "hello printf\n";
    const char *msg1 = "hello fwrite\n";
    const char *msg2 = "hello write\n";
    printf("%s", msg0);
    fwrite(msg1, strlen(msg0), 1, stdout);
    write(1, msg2, strlen(msg2));
    fork();
    return 0;
}

运行结果:

但如果对进程实现输出重定向呢?执行 ./test > file 后,结果变成了

我们发现 printf fwrite (库函数)都输出了 2 次,而 write 只输出了一次(系统调用)。为什么呢?这里我们猜测可能和fork有关,通过屏蔽fork,我们发现结果是
(1)一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
(2)printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据 的缓冲方式由行缓冲变成了全缓冲。
(3)我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后
(4)但是进程退出之后,会统一刷新,写入文件当中。
(5)但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的 一份数据,随即产生两份数据。
(6)write 没有变化,说明没有所谓的行缓冲
综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不在我们讨论范围之内。那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr;   /* Current read pointer */
char* _IO_read_end;   /* End of get area. */
char* _IO_read_base;  /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr;  /* Current put pointer. */
char* _IO_write_end;  /* End of put area. */
char* _IO_buf_base;   /* Start of reserve area. */
char* _IO_buf_end;    /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base;  /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */

理解文件系统

我们使用  ls -l    的时候看到的除了看到文件名,还看到了文件元数据。
 
 
每行包含 7 列:
         模式,硬链接数,文件所有者 ,组,大小文件名,最后修改时间
 
ls -l 读取存储在磁盘上的文件信息,然后显示出来
另外 stat命令也可以查看文件信息

文件系统

Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如 mke2fs -b 选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的
Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子
超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
GDT,Group Descriptor Table:块组描述符,描述块组属性信息
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
i节点表:存放文件属性如文件大小,所有者,最近修改时间等
数据区:存放文件内容
将属性和数据分开存放的想法看起来很简单,但实际上是如何工作的呢?我们通过touch一个新文件来看看如何工作。
[hty@iZ2vcboxg2e41nj4s5s6zrZ test]$ cd day3
[hty@iZ2vcboxg2e41nj4s5s6zrZ day3]$ touch file
[hty@iZ2vcboxg2e41nj4s5s6zrZ day3]$ ls -i file
1581596 file

创建一个新文件主要有一下4个操作:
1. 存储属性
内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。
2. 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据
复制到300,下一块复制到500,以此类推。
3. 记录分配情况
文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
4. 添加文件名到目录
新的文件名abc。linux如何在当前的目录中记录这个文件,内核将入口(263466,file)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。
下面解释一下文件的三个时间:
         Access 最后访问时间
        Modify 文件内容最后修改时间
        Change 属性最后修改时间

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

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

相关文章

晶圆键合对准机的原理与应用

一、晶圆键合设备的工作原理 1、 第一个晶圆面朝下置于晶圆对准设备卡盘并传送到对准机内&#xff1b; 2、对准机内&#xff0c;晶圆在Z轴方向上移动直到被顶部的传输夹具真空吸附固定&#xff1b; 3、被传输夹具固定的第一个晶圆将成为后续对准工艺的基准&#xff0c;确定所…

SAP 获取本机信息(IP及电脑名称)<转载>

原文链接&#xff1a;https://blog.csdn.net/JYH1999/article/details/126489974 导语&#xff1a;最近在做日志的东西&#xff0c;需要记录用户的IP&#xff0c;以及电脑名称&#xff0c;找了一下&#xff0c;SAP有两个类可以实现。 一、效果展示 二、代码 *&----------…

一文学会K8s集群搭建

环境准备 节点数量&#xff1a;2台虚拟机 centos7硬件配置&#xff1a;master节点内存至少3G&#xff08;2G后面在master节点初始化集群时会报错&#xff0c;内存不够&#xff09;&#xff0c;node节点可以2G&#xff0c;CPU至少2个&#xff0c;硬盘至少30G网络要求&#xff1…

双向圆周阵列及阵列间距调整

SOLIDWORKS提供的阵列功能是非常实用的快速建模功能&#xff0c;可以帮助我们快速的形成重复特征。在以往的版本中&#xff0c;无论是线性阵列还是其他阵列都提供了可以双向进行的阵列选项。也就是可以帮助我们保证两个方向的阵列属性。 今天我们要讲的是在圆周阵列中增加双向的…

【EPLAN】统一修改项目中字体大小

需求&#xff1a;当A3图框时&#xff0c;“设备标识符”等字体太小&#xff0c;影响查看图纸。需要统一调大。 通过“层管理”改变字体大小。 “项目数据”->“层管理” 找到需要改变字体的大小如“设备标识符”&#xff1a; Over。 效果&#xff08;2.5mm 调整到了 3.5mm&a…

我国元宇宙专利申请位列全球靠前,UTONMOS元宇宙游戏体验再升级

中青网报道 近日&#xff0c;2023年服贸会数字贸易发展趋势和前沿高峰论坛举办并发布了《中国元宇宙产业发展趋势洞察》报告。报告指出我国元宇宙相关专利申请量位列全球第二。 元宇宙是虚拟世界和现实世界融合的载体&#xff0c;正成为驱动数字经济发展和助力数字中国建设的重…

【python基础知识】0.print()函数

文章目录 前言print()函数无引号单引号的用法双引号的用法三引号的用法转义字符 变量和赋值变量的命名规范 下关预告 前言 Python就是一个能够帮你实现需求的工具&#xff0c;它更像是一把万能钥匙&#xff0c;决定用它来打开哪一扇门的&#xff0c;是你自己。 『千里之行&am…

2.1 PE结构:文件映射进内存

PE结构是Windows系统下最常用的可执行文件格式&#xff0c;理解PE文件格式不仅可以理解操作系统的加载流程&#xff0c;还可以更好的理解操作系统对进程和内存相关的管理知识&#xff0c;在任何一款操作系统中&#xff0c;可执行程序在被装入内存之前都是以文件的形式存放在磁盘…

阿里云2核4G服务器5M带宽五年租用价格表

阿里云2核4G服务器5M带宽可以选择轻量应用服务器或云服务器ECS&#xff0c;轻量2核4G4M带宽服务器297元一年&#xff0c;2核4G云服务器ECS可以选择计算型c7、c6或通用算力型u1实例等&#xff0c;买5年可以享受3折优惠&#xff0c;阿腾云分享阿里云服务器2核4G5M带宽五年费用表&…

Java“牵手”阿里巴巴店铺所有商品API接口数据,通过店铺ID获取整店商品详情数据,阿里巴巴店铺所有商品API申请指南

阿里巴巴平台店铺所有商品数据接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取阿里巴巴整店的商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片、价格信息等详细信息 。 获取店铺所有商品接口API是一种用于获取电商平台…

了解被测系统(一)技术架构

目录 web应用组成 项目实例 系统架构图 整体架构图 web应用组成 从开发者的角度来看&#xff0c;web 应用主要由三部分组成: 用户界面&#xff0c;业务逻辑&#xff0c;数据。 1.用户界面(视图层) 用于将数据展示给用户的地方&#xff0c;采用 HTML&#xff0c;CSS&…

【Linux内核】以共享内存的方式实现进程间通信

现在有很多进程间通信的模式&#xff0c;但是我们选择一个简单的IPC机制&#xff08;共享内存&#xff09;来实现&#xff0c;并让它工作起来。 简单来讲我们实现了两个系统调用&#xff08;不可避免地需要我们完善IDT&#xff09;&#xff0c;发送方查看接受方是否接收&#…

配电室智能运维解决方案

配电智能运维服务是以电易云-智慧电力物联网平台为核心&#xff0c;通过对配电室(或箱变)、高、低压配电柜加装在线监测装置&#xff0c;将运行状态实时传输到电易云平台&#xff0c;人工24小时运维值班&#xff0c;为客户提供大数据时代下的电力运维解决方案&#xff0c;更好的…

基于springboot绩效管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

2.5 PE结构:导入表详细解析

导入表&#xff08;Import Table&#xff09;是Windows可执行文件中的一部分&#xff0c;它记录了程序所需调用的外部函数&#xff08;或API&#xff09;的名称&#xff0c;以及这些函数在哪些动态链接库&#xff08;DLL&#xff09;中可以找到。在Win32编程中我们会经常用到导…

修改 gc2093.c 驱动程序改变摄像头预览的镜像效果

原理 查看gc2093芯片手册&#xff0c;修改寄存器0x0017的数值&#xff0c;可以修改摄像头预览镜像效果。如下&#xff1a; #define GC2093_MIRROR_FLIP_REG 0x0017 #define MIRROR_MASK BIT(0) #define FLIP_MASK BIT(1) 方法 通过修改 gc2093.c 驱动程序可以改变摄像头预览…

NET7快速开发一个商品管理模块-商品列表开发(一)

商品管理模块&#xff0c;一般包含以下几个模块&#xff1a; 商品列表&#xff1a;这里可以看到所有已发布的商品信息列表。 商品管理&#xff1a;添加商品、编辑商品以及删除商品。 具体功能如下图&#xff1a; 1.商品列表 2.添加商品 3.商品SKU编辑

【java】【项目实战】[外卖十]项目优化(mysql读写分离)

目录 一、问题说明 二、读写分离示例 三、Mysql主从复制 3.1 介绍 3.2 配置 3.2.1 前置条件 3.2.2 配置-主库Master 3.2.2.1 第一步 3.2.2.2 第二步 3.2.2.3 第三步 3.2.2.4 第四步 3.2.3 配置-从库Slave 3.2.3.1 第一步 3.2.3.2 第二步 3.2.3.3 第三步 3.2.3.4 …

【TypeScript学习】—编译选项(三)

【TypeScript学习】—编译选项&#xff08;三&#xff09; 一、自动编译文件 tsc xxx.ts -w二、自动编译整个项目 三、编译器选项

3. C++调试时显示代码所在文件 / 函数 / 行号信息

1. 说明 在执行C代码时&#xff0c;有时希望知道当前代码所在的文件名、函数名和对应行号位置信息&#xff0c;方便快速定位到代码所在处。想要获取这些信息&#xff0c;可以使用C提供的一些宏进行获取。 2. 简单说明 __FILE__ : 用于获取当前语句所在源文件的文件名 ——fu…