【Linux】文件系统

目录

  • 打开的文件
    • 语言层文件操作
    • 系统层文件操作
    • 文件描述符
      • 文件描述符表
      • struct file 文件对象
      • 文件描述符的分配
      • 重定向
      • dup2
      • 缓冲区
    • 自主封装文件接口
  • 磁盘中的文件
    • 磁盘物理结构
    • 物理存储结构
    • 从OS看文件
    • 分区与分组
    • 目录
    • 文件增删查改
    • 直接间接索引
  • 软硬链接
    • 软链接
    • 硬链接
  • 动静态库
    • 设计静态库
    • 设计静态库
    • 库的加载
    • 位置无关码

文件主要是打开的文件即内存文件,和在磁盘中的文件

打开的文件

  • 打开文件本质时加载文件属性到内存
  • OS根据文件属性创建相关内核数据结构(struct file),对打开的文件进行管理
  • 文件操作就是进程与创建的文件对象之间的联系
    【Linux】文件系统_第1张图片

语言层文件操作

  • fopen, fputs, fclose, fprintf
  • sprintf, snprintf, 将字符串信息格式化到一个自定义的缓冲区里,n为缓冲区大小
    snprintf ---> buffer ---> fputs, fwrite
  • fgets, 从指定文件流中按行读取放到缓冲区

系统层文件操作

  • open
    int open(const char *pathname, int flags); 打开已有文件
    int open(const char *pathname, int flags, mode_t mode); 创建并打开
    flags标志位参数采用位图结构,一个比特位表示一个标志位,则一个int类型可以同时传递32个标志位
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);

  • write
    ssize_t write(int fd, const void *buf, size_t count);
    注意写入时考虑’ '\0''\0'是C语言的规定,不是文件的规定,会被解释成乱码,'\n'可以正常识别

  • read
    ssize_t read(int fd, void *buf, size_t count);
    ssize_t n = read(fd, buffer, sizeof(buffer) - 1); //整体读取,无法按行读取

文件描述符

【Linux】文件系统_第2张图片

文件描述符表

文件描述符(open的返回值 fd)本质上是数组下标

【Linux】文件系统_第3张图片

  • 每个 struct file 文件对象都对应一个缓冲区
  • 所谓的 IO类 read, write 函数,本质上是拷贝函数,是对用户空间和内核空间数据进行来回拷贝
  • 缓冲区内同何时刷新到磁盘中指定位置,是由OS自主决定的

struct file 文件对象

Linux下一切皆文件,Linux视角看到的都是经描述后的 struct file 文件对象, 其包含文件属性、文件匹配的缓冲区、文件的操作方法。

文件描述符的分配

文件描述符表中最小的没有使用的数组元素分配给新文件,将新文件的文件对象的地址填入该位置

重定向

重定向:在上层不能感知的情况下,OS内部更改进程对应的文件描述符表中的特定下标位置的内容

./a.out > log.txt //默认重定向的是1号标准输出文件
./a.out > log.txt 2>&1 //先重定向1,再将已重定向到 1 中的内容覆盖到 2
./a.out 1>log.txt 2>err.txt //将1号重定向到log.txt,将2号重定向到err.txt,以此可分别收集输出和错误信息

dup2

int dup2(int oldfd, int newfd);
dup2(fd, 1); //注意使用时两 fd 参数分别指代

缓冲区

  • 语言层C库缓冲区刷新策略:无缓冲,行缓冲,全缓冲
  • 缓冲区旨在节省调用者的时间(系统调用也要花费时间),以提高效率
  • 缓冲区在 fopen 打开文件创建的 struct FILE 结构体中,是C库负责维护的
    对于下面这段代码
	fprintf(stdout, "hello fprintf\n"); //暂存缓冲区
	const char * msg - "hello world\n";
	write(1, msg, strlen(msg)); //直接写给OS
	fork();
  • ./a.out //这里是向显示器打印,为行缓冲策略,直接打印出"hello fprintf",同时write也直接写,fork前两串信息都刷新了
  • ./a.out > log.txt //输出重定向到 log.txt,其为普通文件,刷新策略变为全缓冲,write 是直接写,fork 之前write信息已经刷新,而 fprintf还在缓冲区,fork 创建子进程之后,因为进程结束会清空缓冲区,所以父子进程先结束一方会触发写时拷贝,刷新拷贝内容到 log.txt,后结束的一方再将原来的缓冲区内容刷新到 log.txt,所以最终现象是 fprintf 打印了两份,原因是因为发生了写时拷贝

自主封装文件接口

gitee入口
https://gitee.com/honortech/linux/tree/master/5_26-mystdio

 

磁盘中的文件

磁盘物理结构

  • 磁盘:盘片,磁头,马达
  • 南北极表示0和1,向磁盘写入相当于磁化,N->S, (0->1),反之为删除,(加热会消磁,导致数据丢失)

物理存储结构

  • 磁盘的基本单元:扇区(sector),512字节,or 4kb,一般磁盘所有扇区都是512字节
  • 同半径的所有扇区构成一个磁道(柱面)(cylinder)
  • 定位扇区:先由伴经定位所处磁道,再根据磁道上扇区编号定位扇区,
    用磁头(head)编号来定位面,即确定是哦那个哪一个磁头读取,CHS定位法

从OS看文件

  • OS实际进行IO的基本单位是4kb,一个OS级别的文件块包含8个扇区
  • 可以将磁盘看作一个大数组,计算机常规通过起始地址+偏移量的方式进行访问
  • OS是通过LAB逻辑块地址来进行块级别的访问的

分区与分组

一块磁盘会被分区成多个分区,每个分区都有自己独立的一套文件系统
下图是一个分区
【Linux】文件系统_第4张图片
分组中:

  • super block
    文件系统的所有属性信息,包含文件系统的类型,所在区分组情况
    super block在该区内每个分组都有备份,并且是统一更新的,防止某份损坏导致故障
  • Group Descriptor Table
    组表述符,保存所在组的属性信息
  • inode
    128字节,记录文件的所有属性,一个文件对应一个inode,同时记录了该文件的数据所在的数据块编号
  • inode table
    保存组内所有文件的inode number
  • Data Blocks
    保存文件内容,每个数据块有其对应的编号,inode 中会保存Data Blocks中其文件内容对应数据块的编号
  • inode Bitmap
    位图结构,记录inode table 中 inode 使用情况使用率,每个比特位表示一个 inode 是否空闲可用
  • Block Bitmap
    每个比特位表示一个数据块是否空闲可用

目录

一个目录是一个文件,有其对应的 inode,和其文件容对应的数据块
目录对应的数据块中保存的是在该目录下的文件的文件名和文件 inode number 的映射关系,文件名和 inode number 互为 key 值

文件增删查改

  • 文件的删除是通过修改bitmap位图结构完成,将对应的 inode Bitmap 和 Block Bitmap 中对应比特位置为 0,
  • inode number 不能跨分区使用,因为一个分区一套文件系统

直接间接索引

【Linux】文件系统_第5张图片

软硬链接

软链接

ln -s myfile.txt my-sort //my-sort 软链接到myfile

  • 软链接是一个独立的文件,有自己的 inode number,和 inode 对应的文件属性和文件内容
  • 软链接的文件内容保存的是指向文件的路径
  • 应用:windows下的快捷方式

硬链接

ln myfile.txt my-hard //建立硬链接

  • 硬链接与目标文件同用一个 inode number,同一个 inode
  • inode 中有一项引用计数 ref_cnt,记录了当前 inode 的硬链接数
  • 硬链接本质是建立了新文件名与目标文件 inode 的映射关系,只是更改了目录
  • unlink my-hard//删除硬链接
  • 应用:当前目录 . ,上级目录 ..
  • 目录的硬链接数可推测该目录下有多少个子目录,子目录的..是当前目录的硬链接
  • 不能给目录建立硬链接,容易造成环路路径问题

动静态库

编译器的语法报错:编译器除命令行启动模式外,还会有其他自动化模式,可以不断进行语法检查
语法提醒动能:编译器会预先包含语言库头文件,编译器会将输入的内容不断在包含的头文件中进行搜索

设计静态库

ar -rc libmymath.a *.o //将所有 .o 文件打包成 mymath 静态库
gcc -o mytest main.c -L ./lib -l mymath -I ./include -static //指定使用当前目录下 lib 中第三方库进行静态链接

设计静态库

gcc -fPIC -c myadd.c //形成含与位置无关码的二进制 .o 文件
gcc -shared -o libmymath.so *.o //将 .o 文件打包成动态库

静态库链接原则是将二进制代码直接拷贝到可执行程序中
动态库链接是在运行是去相关路径中进行查找

三种配置动态库的方式:

  • 1…环境变量
    将第三方动态库路径追加到 LD_LIBRARY_PATH 环境变量中(修改环境变量仅在本次会话内有效)
  • 2…软链接
    在默认库路径下创建第三方动态库的软链接
  • 3…配置文件
    /etc/lib.so.conf.d路径下sudo touch一个.conf文件,将第三方动态库路径写入到该文件,再sudo ldconfig使配置文件起效

库的加载

  • 静态库的加载
    直接拷贝,占用内存,下载周期变长,占用网络资源,但是可执行程序独立性强,不依赖于源库
  • 动态库的加载
    • 动态链接将可执行程序中的外部符号替换成库中该符号所对应方法的地址
    • 代码执行方法时,因为方法已经建立了映射,所以是直接再所处地址空间内跳转的
    • 多个进程调用同一份方法,该方法再内存中只会加载一份,其他程序调用方法也是在其地址空间内跳转,这就实现了库的共享
      【Linux】文件系统_第6张图片

位置无关码

相对编址:动态库中的地址都是偏移量,默认从零开始
链接库时,首先会维护库,这时便确定的起始地址,调用某方法时,加载到内存,根据其偏移量在地址空间中对应与库的起始地址的相对位置建立映射

你可能感兴趣的:(linux,运维,服务器)