Linux文件系统与基础IO

文章目录

  • 1 C文件接口
    • 1.1 `fopen`
    • 1.2 `fwrite`、`fread`、`rewind`、`fclose`
  • 2 文件系统调用
    • 2.1 `open`
      • 2.1.1 参数2:`flags`
      • 2.1.2 参数3:`mode`
      • 2.1.3 返回值——`file descriptor`
    • 2.2 write
    • 2.3 read
    • 2.4 close
  • 3 文件的本质
    • 3.1 `struct file`
    • 3.2 一个进程如何与多个文件相关联?
  • 4 重定向
    • 4.1 文件描述符对应的分配规则?
    • 4.2 `dup2`
    • 4.3 重定向`stdout`和`stderr`
  • 5 缓冲区
  • 6 硬盘(固态硬盘(SSD)/机械硬盘(磁盘))
    • 6.1 磁盘
    • 6.2 对磁盘的抽象
  • 7 如何理解目录
  • 8 软硬链接
    • 8.1 建立软连接
    • 8.2 建立硬链接
  • 9 动/静态库
    • 9.1 静态库
    • 9.2 动态库
      • 9.2.1 如何让可执行程序找到动态库
      • 9.2.2 动态库时怎么被加载的
      • 9.2.2 动态库时怎么被加载的

1 C文件接口

1.1 fopen

  • fopen新建的文件,如果是相对路径,在进程的工作路径下创建-
  • w:清空写入
  • a:追加

1.2 fwritefreadrewindfclose

  1 #include <stdio.h>2 #include <string.h>3 #include <stdlib.h>4 int main() {5     FILE* f = fopen("bite.txt", "w+");6     const char* msg = "linux so easy\n";7     fwrite(msg, strlen(msg), 1, f);8 9     rewind(f);   //重置偏移量!!!                                                                                                         10     char buffer[strlen(msg)];11     fread(buffer, 1, strlen(msg), f);12     printf("%s\n", buffer);13     fclose(f);14 }

2 文件系统调用

2.1 open

//头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char* pathname, int flags);
int open(const char* pathname, int flags, mode_t mode);

2.1.1 参数2:flags

  1. O_RDONLY:只读
  2. 0_WRONLY: 只写
  3. O_CREAT:如果文件不存在,则创建文件到path路径下
  4. 0_TRUNC:打开的时候先清空(truncate)
  5. O_APPEND:在追加模式下打开,写入时在已有内容后追加

2.1.2 参数3:mode

  • umask:权限掩码:权限 & ~umask —> 最终权限(八进制, eg. 0xxx

  • umask系统调用改变umask

    • 头文件:<sys/types.h> <sys/stat.h>
    • ``mode_t umask(mode_t mask);`
    • 只改变当前进程的umask,不改变系统的, 进程里用自己进程的umask

2.1.3 返回值——file descriptor

  • 实质为一个数组下标(详细看3.2小节)

当调用write时,将fd传递给进程,进程根据files指针找到文件描述符表,然后由对应下标(fd)找到打开的文件file

  • 而C语言打开文件返回的FILE是C语言自己封装的结构体,里面一定含由文件描述符
cout << stdin->_fileno << endl;  //0
cout << stdout->_fileno << endl;  //1
cout << stderr->_fileno << endl;  //2

2.2 write

#include <unistd.h>ssize_t write(int fd, const void* buf, size_t count);
  • 参数1:文件描述符
  • 参数2:写入内容
  • 参数3:写入内容的长度strlen(messsage)

2.3 read

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
  • 返回值
    • 大于0:返回读取的字节数
    • 0:写端关闭情况
    • -1:读取错误

2.4 close

3 文件的本质

3.1 struct file

操作系统维护一个被打开文件的信息:struct file, 包含:

  1. 在磁盘的什么位置
  2. 基本属性:权限,大小,读写位置,谁打开的
  3. 文件的内核缓冲区信息
  4. struct file* next指针,将不同文件链接起来

3.2 一个进程如何与多个文件相关联?

  • task_struct中含有一个stuct files_struct *files记录自己打开文件的信息,stuct files_struct *file里包含一个struct file *fd_array[]指针数组,存放文件指针(所以open时,会选择一个空的fd_array位置的下标返回)
  • struct file *fd_array[]文件描述符表,数组加标0、1、2分别指向三个默认打开的文件:stdin(键盘文件)stdout(显示器文件)stderr(显示器文件)

image-20231102134743325

  1 #include <stdio.h>2 #include <unistd.h>3 #include <string.h>4 #include <sys/types.h>5 #include <sys/stat.h>6 #include <fcntl.h>7 8 int main() {9     const char* msg = "hello\n";10     write(1, msg, strlen(msg));   //想显示器写入11 12     char buffer[1024];13     ssize_t s = read(0, buffer, sizeof(buffer));   //向键盘读数据14     buffer[s] = '\0';                                                                                                                       15     printf("echo : %s\n", buffer);16     return 0;17 }

4 重定向

4.1 文件描述符对应的分配规则?

从0下标开始,寻找最小没有使用的数组位置

int main() {close(1);int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);if (fd < 0) {perror("open");return 1;}const char *msg = "hello\n";for (int i = 0; i < 5; i++) {//因为关闭了1,open文件之后占用1这个位置,写入从显示器重定向到了文件中write(1, msg, strlen(msg));    }close(fd);
}

4.2 dup2

#include <unistd.h>int dup2(int oldfd, int newfd)   //makes newfd be the copy of oldfd      

重定向:将文件描述符对应下标的指针拷贝到要重定向的文件的位值的指针

  • fd_array[oldfd]拷贝到fd_array[newfd], 拷贝之后需要close(oldfd);

4.3 重定向stdoutstderr

int mian() {fprintf(stdout, "normal msg");fprintf(stdout, "normal msg");fprintf(stdout, "normal msg");fprintf(stderr, "error msg");fprintf(stderr, "error msg");fprintf(stderr, "error msg");
}
//gcc test.c -o test
  • ./test > normal.log:normal msg重定向到normal.log, error msg打印到屏幕

stdoutstderr都重定向到一个文件all.log

  1. ./test &>all.log
  2. ./test >&all.log
  3. ./test >all.log 2>&1
  4. ./test 2>all.log 1>all.log

5 缓冲区

  • C的输出接口输出到用户级缓冲区(该缓冲区不在系统中)

  • 显示器的文件的刷新方案是行刷新,所以在printf执行完成遇到\n就会将数据进行刷新

  • 缓冲区刷新策略:

    • 无缓冲——直接刷新
    • 行缓冲——碰到\n刷新 —— 显示器
    • 全缓冲——缓冲区满了才刷新 —— 文件写入
    • 进程退出
  • fprintf/fwrite等向用户级缓冲区中写入,当缓冲区刷新时调用write系统调用接口(因此,C中fflush函数一定封装了write),write向系统级缓冲区中写入

  • 为什么要有用户级的缓冲区

    • 解决效率问题
    • 配合格式化
  • 用户及的缓冲区在哪里?—— 存在FILE结构体中,

  1 #include <stdio.h>2 #include <unistd.h>3 #include <string.h>4 5 int main() {6     const char* fstr = "hello fwrite\n";7     const char* str = "hello write\n";8 9     printf("hello printf, pid:%d, ppid:%d\n", getpid(), getppid());10     fprintf(stdout, "hello fprintf, pid:%d, ppid:%d\n", getpid(), getppid());11     fwrite(fstr, strlen(fstr), 1, stdout);12                       13     write(1, str, strlen(str));14 15     fork();16 17 }

image-20231104152320757

write为系统调用接口,直接刷新,而由于重定向输出到文件,用户级缓冲区的刷新策略更改为全缓冲,fork后子进程写时拷贝 ,而缓冲区也会随着FILE结构体的拷贝而拷贝,当子进程退出后刷新缓冲区,接着父进程退出也刷新缓冲区

  • FILE结构体:
/usr/include/libio.h
struct _IO_FILE {int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//缓冲区相关/* 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. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno; //封装的文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/* char* _save_gptr; char* _save_egptr; */_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

6 硬盘(固态硬盘(SSD)/机械硬盘(磁盘))

  • 磁盘上存储的文件 = 文件的内容 + 文件的属性
  • 文件内容——数据块, 文件属性 —— inode
  • 文件在磁盘当中的存储是将属性和内容分开存储的

6.1 磁盘

定位一个扇区:面(定位该用哪个磁头) -> 磁道(柱面) -> 扇区 (CHS寻址方式)

  • 时间消耗主要来自于寻道时间

6.2 对磁盘的抽象

LBA地址:将磁盘磁头、磁道、扇区逻辑抽象成一个一维数组,通过除模运算计算出CHS

09B31BEA48DF0457251FF93A6F55B0A6

  • 建立联系,

  • 在Linux中,用于标识文件, 找到inode编号->inode table -> struct inode -> blocks[] -> 文件内容

  • struct inode {inode number//文件类型//权限 : w/r/x//引用计数//拥有者//所属组//ACM时间int blocks[N]    //
    }
    
  • inode table: 存放inode, 每个inode有唯一的编号(一个文件一个inode, 一个inode可能对应多个block

    • ls -li: 查看inode编号
  • Block Bitmap:位图,标记块是否被使用

  • inode Bitmap:位图,标记inode编号是否是有效的

  • Group Descriptor Table

  • Super Block:文件系统的基本信息,包含整个分区的基本使用情况

    • 一共有多少个组、每个组的大小,每个组inode的数量、每个组的block数量、每个组的其实inode、文件系统的类型和名称

7 如何理解目录

  • 目录是文件:内容 + 属性,也有inode

  • 目录也有数据块,存放目录下,文件的文件名和对应文件与inode的映射关系

    • 因此同一目录下不能有相同文件名

    • 若该目录没有w权限,无法创建文件:因为无法将文件名与inode写入该目录的数据块

    • 若该目录没有r权限,无法查看该目录

    • 若该目录没有x权限,无法进入该目录

  • dentry缓存

    • 如何知道自己的inode?当前的目录的数据块中存放当前目录下文件名与inode的映射关系,而当前目录又被上级目录视为文件,存放该目录的inode与数据块,所以当访问一个文件的inode时需要递归到根目录再从根目录访问到当前inode

8 软硬链接

8.1 建立软连接

ln -s file.txt soft-link

image-20231107214116494

  • 软连接具有独立的inode,也有独立的数据块,它的数据块里面保存的是指向文件的路径(类似于快捷方式)

8.2 建立硬链接

ln test.txt hard-link

image-20231107214049062

  • 硬链接具有相同的inode,本质上是在当前目录下,建立新的文件名字与inode链接(取别名/引用)
  • 不允许给目录建立硬链接(除非是 ...),不然会造成查找路径的环路问题

9 动/静态库

  • 静态库:libXXX.a
  • 动态库:libXXX.so

9.1 静态库

  • 静态库本质上时一些.o文件的集合
  • ar是gun归档工具, 用于打包静态库rc表示replace and create
lib=libmymath.a$(lib):mymath.o     //可能有多个.o文件ar -rc $@ $^
mymath.o:mymath.cgcc -c $^.PHONY:
clean:rm -f *.a *.o.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mymathlibcp *.h lib/includecp *.a lib/mymathlib

使用库:

  1. 找到头文件路径 —— -I
  2. 找到库的路径(否则链接时报错)—— -L
  3. 并且说明链接该路径下的哪一个库 —— -l (去掉lib,去掉.a,剩下的名字) ;第三方库必须指定库名称
gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -lmymath
  • 查看可执行文件 所用的标准库(动态库)
ldd a.out
  • 库的安装
  1. 拷贝到指定目录
sudo cp lib/include/math.h /usr/include/
sudo cp lib/mymathlib/libmymath.a /lib64/ 
  1. 建立软连接(不建议这么做)

9.2 动态库

  1. 生成.o文件
gcc -fPIC -c mylob.c

(-c 不知名目标文件时,生成的时同名.o文件)

  1. 生成.so文件
gcc -shared -o libmymethod *.o

(不加-shared生成的是可执行文件)

  • 当程序运行动态库中的方法,系统会将动态库加载到内存中执行,所以.so文件自动带有x可执行权限
dy-lib=libmymethod.so
static-lib=libmymath.a.PHONY:all
all: $(dy-lib) $(static-lib)$(static-lib):mymath.oar -rc $@ $^
mymath.o:mymath.cgcc -c $^$(dy-lib):mylog.o myprint.o
mylog.o:mylog.cgcc -fPIC -c $^
myprint.o:myprint.cgcc -fPIC -c $^.PHONY:clean
clean:rm -rf *.o *.a *.so mylib.PHONY:output
output:mkdir -p mylib/includemkdir -p mylib/libcp *.h mylib/includecp *.a mylib/libcp *.so mylib/lib
  • 编译时于静态库相同
  • -fPIC:与地址无关码

9.2.1 如何让可执行程序找到动态库

IMG_2827

四种方法:

  1. 将动态库拷贝到/lib64
  2. 建立在/lib64下的软连接
ln -s xxx(绝对路径) /lib64/xxx
  1. 添加到环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/xxx/xxxx/xx(绝对路径)
    • cd /etc/ld.so.conf.d
    • 创建一个.conf文件
    • 将动态库路径添加到该文件中
    • 执行ldconfig

9.2.2 动态库时怎么被加载的

  • 动态库在系统中加载后,会被所有进程共享

  • 共享库中的全局变量,既然会被共享,那么会不会冲突? 不会,因为会发生写时拷贝

  • 程序在编译好之后,内部有地址,及就是虚拟地址,编译器也要考虑程序内存加载的问题

  • 共享库肯能非常大,所以使用固定位置是不现实的,库可以在虚拟内存的共享区中任意位置加载, 动态库内部的函数不采用绝对编址,只需要表示每个函数在库中的偏移量即可, 通过库的起始地址 + 偏移量找到函数

    • 所以编译形成动态库的链接文件(.o)时,需要带选项-fPIC:(position independent code)
      s xxx(绝对路径) /lib64/xxx

3. 添加到环境变量```bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/xxx/xxxx/xx(绝对路径)
    • cd /etc/ld.so.conf.d
    • 创建一个.conf文件
    • 将动态库路径添加到该文件中
    • 执行ldconfig

9.2.2 动态库时怎么被加载的

  • 动态库在系统中加载后,会被所有进程共享

  • 共享库中的全局变量,既然会被共享,那么会不会冲突? 不会,因为会发生写时拷贝

  • 程序在编译好之后,内部有地址,及就是虚拟地址,编译器也要考虑程序内存加载的问题

  • 共享库肯能非常大,所以使用固定位置是不现实的,库可以在虚拟内存的共享区中任意位置加载, 动态库内部的函数不采用绝对编址,只需要表示每个函数在库中的偏移量即可, 通过库的起始地址 + 偏移量找到函数

    • 所以编译形成动态库的链接文件(.o)时,需要带选项-fPIC:(position independent code)

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

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

相关文章

Python 日志指南

目录 日志指南 日志基础教程 什么时候使用日志 一个简单的例子 记录日志到文件 从多个模块记录日志 记录变量数据 更改显示消息的格式 在消息中显示日期/时间 后续步骤 进阶日志教程 记录流程 记录器 处理器 格式器 配置日志记录 如果没有提供配置会发生什么 …

QLoRA论文概述

QLORA: Efficient Finetuning of Quantized LLMs QLoRA论文概述前言&#xff08;省流版本&#xff09;摘要论文十问实验实验1数据集和模型实验结论 实验2数据集和模型实验结论 局限性模型尺度数据集其他微调方法 QLoRA论文概述 前言&#xff08;省流版本&#xff09; 微调需要…

期末速成数据库极简版【查询】(2)

目录 select数据查询----表 【1】筛选列 【2】where简单查询 【3】top-n/distinct/排序的查询 【4】常用内置函数 常用日期函数 常用的字符串函数 【5】模糊查询 【6】表数据操作——增/删/改 插入 更新 删除 【7】数据汇总 聚合 分类 ​ &#x1f642;&#…

分享十几个适合新手练习的软件测试项目

说实话&#xff0c;在找项目的过程中&#xff0c;我下载过&#xff08;甚至付费下载过&#xff09;N多个项目、联系过很多项目的作者&#xff0c;但是绝大部分项目&#xff0c;在我看来&#xff0c;并不适合你拿来练习&#xff0c;它们或多或少都存在着“问题”&#xff0c;比如…

12v转48v升压电源芯片:参数、特点及应用领域

12v转48v升压电源芯片&#xff1a;参数、特点及应用领域 随着电子设备的日益普及&#xff0c;电源芯片的需求也在不断增长。今天我们将介绍一款具有广泛应用前景的升压电源芯片——12v转48v升压电源芯片。该芯片具有宽输入电压范围、高效率、固定工作频率等特点&#xff0c;适…

mp3的播放

1.这段vue代码会播放声音&#xff0c;但是会有audio标签 <template><div><audio id"myAudio" controls><source src"./test.mp3" type"audio/mp3" />Your browser does not support the audio tag.</audio></…

库卡LBR_iisy_3_R760协作机器人导入到coppeliasim

1.从库卡官网xpert下载模型 一般载都是这个step文件格式&#xff0c;其他的好像不太好用。coppeliasim导入格式用的是stl,需要用freeCAD打开重新转换一下。下载下来后&#xff0c;很多都是一个整体&#xff0c;在freeCAD导入中&#xff0c;导入选择要不勾选合并。 下载完用CAD …

正则表达式详细讲解

目录 一、正则表达式概念 二、八元素 1、普通字符&#xff1a; 2、元字符&#xff1a; 3、通配符 .&#xff1a; 4、字符类 []&#xff1a; 5、量词&#xff1a; 6、锚点 ^ 和 $&#xff1a; 7、捕获组 ()&#xff1a; 8、转义字符 \&#xff1a; 三、日常使用的正则…

markdown记录

文章目录 基础操作使用一级列表、二级列表 博文链接 基础操作 使用一级列表、二级列表 博文链接 CSDN-Markdown语法集锦 CSDN-markdown语法之如何使用LaTeX语法编写数学公式 CSDN Markdown简明教程1-关于Markdown CSDN Markdown简明教程2-基本使用 CSDN Markdown简明教程3-表…

Bash脚本调用百度翻译API进行中文到英文的翻译

写一个bash脚本调用百度翻译API进行中文到英文的翻译&#xff0c;首先需要进行相关的申请。看百度给出的文档链接: 百度翻译API文档 需要先注册一个百度账号&#xff0c;然后申请APPID。脚本中会用到appid和key这两个值。按照文档给出的提示可以获得。如下是脚本&#xff1a; #…

使用VBA创建Excel条件格式

实例需求&#xff1a;数据总行数不确定&#xff0c;现需要将Category区域&#xff08;即C列到J列&#xff09;中第3行开始的区域设置条件格式&#xff0c;规则如下&#xff1a; 只对部分指定单元格应用色阶条件格式&#xff08;3色&#xff09;指定单元格应满足条件&#xff1…

移动距离

//偶数行需要反转&#xff0c;判断行数时,最后一个需要特判,可以用向上取整 //也可以把传入的值减一,下标从0开始 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main{static int w,m,n;static BufferedReader i…

大数据技术1:大数据发展简史

前言&#xff1a;学习大数据技术&#xff0c;知道会用已经够了&#xff0c;但是要想走得更远&#xff0c;应该了解它发展的来龙去脉&#xff0c;为何会有新的技术/工具的出现&#xff0c;相比老的技术有什么样的进步。 1、传统数据处理系统存在的问题 随着信息时代互联网技术爆…

JDBC简介及快速入门-黑马(P1)

黑马视频b站链接&#xff1a; 01-JDBC简介&快速入门_哔哩哔哩_bilibili &#x1f436;一、JDBC 简介 JDBC 概念&#xff1a; JDBC 就是使用 Java 语言操作关系型数据库的一套 API。 全称&#xff08;Java DataBase Connectivity&#xff09; Java 数据库连接 JDBC 本质…

React 笔记 jsx

严格约定&#xff1a;React 组件必须以大写字母开头&#xff0c;而 HTML 标签则必须是小写字母。 React JSX JSX 是由 React 推广的 JavaScript 语法扩展。 用于表达组件的 特殊语法的 js 函数 要求标签必须闭合&#xff1b;返回的组件必须包裹在一个父标签内&#xff1b; …

鸿蒙4.0开发笔记之ArkTS语法基础之数据传递与共享详细讲解(十八)

文章目录 一、路由数据传递&#xff08;router&#xff09;1、路由数据传递定义2、路由数据传递使用方法3、数据传递两个页面的效果 二、页面间数据共享&#xff08;EntryAbility&#xff09;1、定义2、实现案例3、避坑点 三、数据传递练习 一、路由数据传递&#xff08;router…

分布式和微服务区别

1.分布式 微服务和分布式的区别 1.将一个大的系统划分为多个业务模块&#xff0c;业务模块分别部署到不同的机器上&#xff0c;各个业务模块之间通过接口进行数据交互。区别分布式的方式是根据不同机器不同业务。 2.分布式是否属于微服务&#xff1f; 答案是肯定的。微服务的意…

动手制作个人电脑对话机器人transformers+DialoGPT

简介&#xff1a;DialoGPT是一个对话模型&#xff0c;由微软基于GPT-2训练。由于DialoGPT在对话数据上进行了预训练&#xff0c;所以它比原始的GPT-2更擅长生成类似对话的文本。DialoGPT的主要目标是生成自然且连贯的对话&#xff0c;而不是在所有情况下都提供事实上的正确答案…

Numpy数组的运算(第7讲)

Numpy数组的运算(第7讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ…

进制 + 原码,反码,补码

进制转换 整数部分 小数部分 原码 反码 补码 原码转补码&#xff1a; 左边和右边第一个1不变&#xff0c;中间取反。-0 除外。 计算机系统中数值一律用补码来存储的原因 其他 术语 进制表 进制数的表示 详细教程可转 爱编程的大丙