持久化

I/O设备#

系统架构#

计算机的系统架构图

基本上可以分为三层,从上到下的速度依次变慢,最上层的内存总线是不对外暴露的,只用于内存和CPU间的传输,其速度也是最快的,其次是PCI类似的通用I/O总线,所有的外围设备都会通过这条总线与CPU进行信息传输,其中显卡直连在这条线路上,而其他设备还需要一条最慢的外设总线来作为中转,这条外设总线几乎可以支持所有常用的外设协议,包括SATA、USB等。

DMA#

DMA,全称为Direct Memory Access,是一个用于优化I/O设备读写的硬件设备。
通常在一个进程中,CPU在将数据从内存搬运到I/O设备的过程是需要占用CPU时间的,而这部分由于硬件架构的限制,往往较慢,这就拖慢了CPU的执行效率。
CPU可以将数据的内存地址和对应的I/O设备信息发送给DMA,由DMA来不经过CPU直接将数据搬入或搬出I/O设备,在执行这个操作的进程就可以放弃当前时间片,等待数据传输完成就好了,CPU可以去执行其他真正需要CPU参与运算的操作,这就提高了CPU的执行效率。

磁盘#

磁盘是持久化存储数据的I/O设备。磁盘上的最小单位通常是512字节,又称为一个扇区,磁盘就是由大量的扇区组成的。通常,单个扇区的写入是原子性的,只有全部成功或者全部失败两种情况,如果涉及多个扇区的写入,异常情况下,比如突然的断电,如果没有其他策略支持,不能保证全部都能正确写入。

文件与系统#

操作 系统接口
创建和打开文件 open
读取文件 read
偏移读 lseek,有时并不想顺序读取文件的全部内容,就可以用这个接口,通过传入文件描述符、偏移量和相对偏移方式,可以从任意位置开始读取文件。
写入文件 write
同步写 fsync,在调用write接口时,系统为了保证效率,通常都不会立即与I/O设备交互,而是先将数据存到缓冲区中,等待一段时间或数据满了再一次性写入设备,但在一些特殊场景下,例如数据库的存储管理,需要频繁的将数据立即写入磁盘,可以调用fsync直接写入。
文件重命名 rename,在linux系统的命令行中,常用mv进行文件的重命名,实际上就是调用了系统的rename接口。
查看文件信息 statfstat,这将返回一个存储了当前文件系统的stat结构体。
删除文件 unlink,linux系统命令行的rm命令实际上调用的是这个接口。
创建目录 mkdir,传入目录名和权限
打开目录 opendir,这将返回DIR类型的指针,DIR是<dirent.h>中定义的文件夹流对象。
读取目录 readdir读取DIR指针指向的目录,返回一个存储了目录信息的dirent结构体,每次会读取到对应DIR指针指向的文件夹下的一个文件或文件夹,以流的形式传输,读完了会返回个空指针。
关闭目录 closedir
删除目录 rmdir,这个接口只能删除空的目录,如果目录是非空的将会报错。

为什么删除文件的接口名称是unlink,而不是delete或者remove

这需要先解释下文件系统中的链接linklink系统调用有两个入参,分别表示旧路径名和新路径名,当使用一个新的文件名链接到旧的文件名时,实际上是创建了另一种引用同一文件的方法,比如在命令行中使用ln old_file new_file,就是重新建立了一个指向相同文件的路径。link只是创建了一个名称,并将其指向了原有文件的相同inode号,并没有执行任何形式的复制,所以无论使用旧的路径还是新的路径都会访问到相同的inode号对应的文件,这甚至可以使用命令行来验证ls -i可以查看文件对应的inode号。
每当删除一个路径时,只是删除了一个对原有文件的链接,只有当最后一个链接被删除,即再也没有该文件的链接时,这个文件才会被删除。unlink就是取消一个对原有文件的链接,这就可以理解为什么取名为unlink了。
命令行中使用stat可以查看文件当前有多少的链接。

上述的链接方式又被称为“硬链接”,但是存在无法创建目录的链接(目录没有inode号)和无法跨文件系统创建链接的局限,于是人们又创建了一种“软链接”的方式,也就是符号链接。通过ln -s的方式来创建符号链接。
符号链接中存放的其实是文件的路径名。这样删除了符号链接并不会直接删除指向的文件,同样,如果删除了原有的文件,符号链接不会跟着删除,而是变成了一个无效的链接,因为其存储的路径名已经不存在了。

#include <iostream>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <cassert>

using namespace std;

int main() {
    mkdir("mydir", 0777);
    auto fd = open("mydir/myfile", O_CREAT | O_WRONLY | O_TRUNC);
    close(fd);
    struct stat st;
    stat("mydir/myfile", &st);
    cout << st.st_mode << endl;
    cout << st.st_blocks << endl;
    cout << st.st_blksize << endl;
    cout << st.st_uid << endl;
    cout << st.st_size << endl;
    DIR* dp = opendir(".");
    assert(dp != nullptr);
    struct dirent* d;
    while((d = readdir(dp)) != nullptr) {
        cout << d->d_ino << "\t" << d->d_name << endl;
    }
    unlink("mydir/myfile");
    rmdir("mydir");

    return 0;
}

输出

33832
0
4096
1000
0
8126551 a.out
8919082 mydir
8127742 create_process
8142539 float-min
8127611 new_file.cpp
8126969 float-min.cpp
8127700 new_file.out
8126538 ..
8127973 .vscode
8127768 dir.cpp
8127289 create_process.cpp
8126712 .

创建并挂载文件系统#

通常在操作系统中提供了创建文件系统的工具,名为mkfs,通过指定一个设备(磁盘分区)和文件系统类型(比如ext4),会在对应的设备上创建一个空的文件系统,然后调用mount接口将文件系统挂载到执行的路径上。我们访问这个路径就相当于访问这个文件系统的根目录了。
通过这种挂载的方式,可以将各种不同的文件系统统一到一棵文件系统树中,而不是拥有多个独立的文件系统。

作者:cwtxx

出处:https://www.cnblogs.com/cwtxx/p/18718189

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   cwtxx  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示