Linux操作系统基础I/O

Posted Ricky_0528

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux操作系统基础I/O相关的知识,希望对你有一定的参考价值。

文章目录

4. 文件系统

4.1 磁盘

文件=文件内容+文件属性

笼统的讲磁盘是我们计算机中的一个机械设备(SSD等等除外)

磁盘写入的基本单位是:扇区

站在操作系统的角度,认为磁盘是线性结构的

磁盘大带来了管理成本的高,可以将磁盘进行分区,即大磁盘、小空间

每个单独的分区再写入文件系统

使用磁盘需要经历两个步骤:分区和格式化(写入文件系统)

4.2 inode

Linux特有的EXT系列的文件系统

Boot Block:启动分区,保存于启动相关的内容

Block Group:文件系统会根据分区的大小划分为多个Block Group,每个Block Group都有着相同的结构组成

Super Block(超级块):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量、未使用的block和inode的数量、一个block和inode的大小、最近一次挂载的时间、最近一次写入数据的时间、最近一次检验磁盘的时间等其他文件系统的相关信息。如果Super Block的信息被破坏,可以说整个文件系统结构就被破坏了

Group Descriptor Table(GDT):块组描述符,描述块组属性信息

Block Bitmap(块位图):为一个二进制序列,从右向左,比特位的位置含义:block编号;比特位的内容含义:特定block是否被占用

inode Bitmap(inode位图):为一个二进制序列,从右向左,比特位的位置含义:inode编号;比特位的内容含义:特定inode是否被占用

inode Table:里面被分为一块一块的inode,用来存放文件的属性

Data blocks:里面被分为一块一块的block,用来存放文件的内容

在Linux中,文件名在系统层面没有意义,这是给用户使用的,Linux中真正的标识一个文件,是通过文件的inode编号,一个文件一个inode

inode里面会保存有int blocks[32];,与Data blocks相对应,这样拿到inode之后就能获取到文件的内容

注:

目录也是文件,同样也有inode和数据,目录的数据块内存放的是文件到inode编号的映射关系,因为我们创建的所有文件,全部都在一个特定的目录下

创建文件:

inode Bitmap找到空闲位置 -> 将文件属性导入到inode Table -> inode Bitmap找到空闲的位置 -> 与inode建立关系 -> 向Data blocks中写入 -> 在目录下建立该文件的inode与文件名的映射关系

查看文件的流程:

cat test.c -> 查看该文件所在目录对应的Data blocks -> 通过建立的映射关系查询到该文件名对应的inode -> 在inode Table找到对应的inode -> blocks[] -> 在Data blocks中找到对应的数据块 -> 打印文件内容

删文件速度快的原因:

只需要将inode Bitmap中该文件对应的比特位置0,再通过inode里面的blocks[]将Block Bitmap中使用到的数据块对应的比特位置0,后面有新的文件会覆盖inode Table和Data blocks的内容,因此这两个不需要修改

创建文件的整个流程:

  • 存储属性

    内核先找到一个空闲的i节点(这里是263466),内核把文件信息记录到其中

  • 存储数据

    该文件需要存储在三个磁盘块,内核找到了三个空闲块:300、500、800

    将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推

  • 记录分配情况

    文件内容按顺序300、500、800存放,内核在inode上的磁盘分布区记录了上述块列表

  • 添加文件名到目录

    新的文件名abc,内核将入口(263466,abc)添加到目录文件,文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来

4.3 软/硬链接

创建软链接

ln -s [文件名] [软链接文件的名字]

删除软链接

unlink [软链接文件的名字]

使用场景

类似于快捷方式,可以快速调用一个其它目录的可执行程序

软链接

软连接是有自己独立的inode的,软链接是一个独立的文件,有自己的inode属性,也有自己的数据块(保存的是指向文件的所在路径+文件名)


创建硬链接

ln [文件名] [硬链接文件的名字]

删除硬链接

直接使用rm

硬链接

硬链接本质根本就不是一个独立的文件,而是一个文件名和inode编号的映射关系,因为自己没有独立的inode

这个操作就相当于重命名了,删除源文件硬链接文件仍然可以使用

红色框出来的表示的是文件的硬链接数,表示有几个文件名与这个文件建立了映射关系

文件的inode结构体内有一个引用计数int ref;表示硬连接数,建立一个硬链接该值就会++,删除一个就会减少,当减为0的时候就表示该文件以及被删除了


创建的一个新目录的硬链接数为2

一个是目录本身还有一个是目录内部的.表示当前目录

如果在目录内部再新建一个目录,硬链接数变为了3

创建一个新目录之后,新目录内部还有一个..表示上级目录,就相当于又建立了一个硬链接指向dir

5. 文件的ACM

Access:文件最近被访问的时间

Modify:最近一次修改文件内容的时间

Change:最近一次修改文件属性的时间

当我们修改文件内容的时候,有可能修改文件的属性,如:文件的大小属性

在较新的Linux内核中,Access时间不会被立即更新,而是有一定的时间间隔,OS才会自动进行更新时间

Makefile如何判断make出来的文件已经是“is up to date”:编译形成的可执行程序的Modify时间比源文件的新

makefile与gcc会根据时间问题来判断源文件和可执行程序谁更新,从而指导系统哪些源文件需要被重新编译

6. 动静态库

ldd [可执行程序]:显示可执行程序依赖的库

一般库分为两种:动态库和静态库

在Linux中

  • 如果是动态库,库文件是以 .so 作为后缀的
  • 如果是静态库,库文件是以 .a 作为后缀的

库文件的命名:libXXX.so或者libYYY.a-…

库的真实名字:去掉lib前缀,去掉 .a-、.so-(包括)后缀,剩下的就是库名称

默认gcc动态链接编译,加上-static选项变为静态链接,生成的可执行文件会大很多

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间

一套完整的库包括:库文件本身、头文件、说明文档(说明库中暴露出来的方法的基本使用方法)

将自己写的方法给别人用:

  • 方案一:直接将源文件给别人

    mytest.c 为别人写的主函数,在主函数内要调用我们的方法

    obj=mytest.o add.o sub.o
    
    mytest:$(obj)
    	gcc -o $@ $^
    %.o:%.c
    	gcc -c $<
    %.o:./test_lib/%.c
    	gcc -c $<
    .PHONY:clean
    clean:
    	rm -f *.o mytest
    

由此可见,只提供 .o 文件也可以进行链接

  • 方案二:打包成静态库

    将所有的 .o 打包形成的就是静态库,不需要源文件

    libmymath.a:add.o sub.o
    	ar -rc $@ $^
    %.o:%.c
    	gcc -c $<
    
    .PHONY:clean
    clean:
    	rm -rf *.o libmymath.a output
    	
    # 打包到一个文件夹进行发布
    .PHONY:output
    output:
    	mkdir output 
    	cp -rf *.h output 
    	cp libmymath.a output
    
    # 安装到系统目录
    .PHONY:install
    install:
    	cp *.h /usr/include 
    	cp libmymath.a /lib64
    


  • 生成静态库:ar -rc libmymath.a add.o sub.o

    ar是gnu归档工具,rc表示(replace and create)

    • 查看静态库中的目录列表:ar -tv libmymath.a

      t:列出静态库中的文件
      v:verbose 详细信息

    • 使用静态库进行编译:gcc mytest.c -I./lib -L./lib -lmymath -o mytest

      mytest:mytest.c
      	gcc -o $@ $^ -I./lib -L./lib -lmymath
      .PHONY:clean
      clean:
      	rm -f mytest
      

      • -I:指明头文件搜索路径

      • -L:指明库文件搜索路径

      • -l:指明要链接哪一个库

        之前使用库文件时不需要这些选项,是因为那些库都在系统的默认路径下:/lib64、/usr/lib、/usr/include等等,编译器是可以识别的

        如果我们把对应的库和头文件拷贝到默认路径下,就可以不带这些选项,但非常不推荐

        上面这个过程也是软件的安装过程

    • 给别人用的其实就是一个库文件+一套头文件

  • 方案二:打包成动态库

    libmymath.so:add.o sub.o
    	gcc -shared -o $@ $^
    
    # 产生.o目标文件,程序内部的地址方案是:与位置无关,库文件可以在内存的任意位置加载,而且不影响其他程序的关联性
    %.o:%.c
    	gcc -fPIC -c $<
    
    .PHONY:clean
    clean:
    	rm -f libmymath.so *.o
    
    .PHONY:lib 
    lib:
    	mkdir lib 
    	cp *.h lib
    	cp libmymath.so lib
    

    • 使用gcc -o mytest mytest -I./lib -L./lib -lmymath可以编译成功,但无法运行,有两种方法

      运行时会使用加载器,来进一步告知系统我们的库在哪里

      • 设置环境变量LD_LIBRARY_PATHexport LD_LIBRARY_PATH=[动态库所在文件夹路径]

      • 系统配置文件:/etc/ld.so.conf.d/下新建一个[文件名].conf,将动态库的所在文件路径放进去,然后使用ldconfig刷新配置文件

如果只有动态库,是无法-static静态链接的,要想既可以动态链接又静态链接一般需要提供两个版本的库,如果都有且没有指明,gcc/g++会优先连接动态库

以上是关于Linux操作系统基础I/O的主要内容,如果未能解决你的问题,请参考以下文章

Linux基础基础I/O

Linux操作系统基础I/O

Linux中的五种I/O模型

Linux操作系统基础I/O

linux 标准I/O函数详解

超深度解析 Linux I/O 的那些事儿