【Linux从青铜到王者】 基础IO

本篇重点:文件描述符,重定向,缓冲区,磁盘结构,文件系统,inode理解文件的增删查改,查找一个文件为什么一定要有路径,动静态库,有的时候为什么找不到库,动态库的加载。

目录

需要大概了解的知识

IO接口简单介绍

read,write和open的初次使用

文件fd

0&1&2

重定向

struct file

磁盘的物理结构和逻辑结构

软硬链接

动静态库

静态库

动态库

2.生成动态库

3.使用动态库

总结


需要大概了解的知识

文件=内容+属性

内容和属性本质上都是数据,都存储在磁盘中

由冯诺依曼可得,文件想要被打开一定要先被加载到内存中

所以文件可被分为被打开文件(内存中)和未被打开的文件(磁盘中)

文件在被加载到内存之前,肯定会在内存中生成对应的描述结构体 struct file,和进程类似

对于被打开的文件我们也要进行管理,怎么管理?先描述,再组织!

一个进程可能打开多个文件,所以进程和被打开文件的关系是1:n

本篇先要大概了解进程和被打开文件的关系

IO接口简单介绍

之前我们学习过c语言的文件操作,但那是语言级别的。由之前所学知道中间肯定需要操作系统参与,所以c语言的文件操作必定封装了系统调用

read,write和open的初次使用

read和write 

 open

这里注意一下mode的使用即可

重点:为什么open的返回值是int???

文件fd

open的返回值int,被成为文件描述符fd

当使用open函数打开文件再将对应的fd打印出来我们可以发现,fd是一个较小的大概率连续的整数

这比较符合日常使用的数组的下标的特征

我们之前说过一个进程要打开一个磁盘中的文件的时候,要先将磁盘中的文件加载到内存中,在这之前我们要先建立对应的struct file 结构体(先将里面默认为有属性和struct file*指针和其他字段),除此之外还要建立一个数组

对应的结构体也要被管理起来?怎么管理?先描述,再组织!

被打开的文件也算一个小的进程了

在进程的对应的描述对象task_struct 结构体中,我们可以发现一个数组指针struct file_struct *file指针一个数组

这个数组默认0,1,2下标被填充。

进程每打开一个文件,对应的struct file就会被生成,然后指向磁盘中的对应部分,该数组从0开始找到未被填充的部分将其填充,这样就能通过数组找到对应的结构体struct file,对应的下标作为返回值返回给fd

这样进程就能通过该数组对struct file实现管理

文件描述符fd的本质:就是数组的下标!!!

那c语言的FILE又是什么鬼?

由上可以推测出FILE是一个结构体,里面必定封装了文件描述符fd

操作系统访问文件只认文件描述符

0&1&2

为什么该数组的0,1,2位置会被默认填充呢?填充的是什么呢?

填充的分别为stdin(键盘),stdout(显示器)和stderr(显示器),目的是为了方便程序员进行读写操作

stderr是什么鬼?又如何进一步理解一切皆文件呢?

struct file里除了属性,还有对应的读写等操作方法集和缓冲区。

因为已经默认填充好0,1,2位置了并且有了对应的struct file,所以当我们敲键盘的时候,本质就是向对应的键盘设备写入数据;当进行打印操作的时候,本质就是向显示器写入数据

stdout和stderr都是显示器,这是为什么呢?为了将代码编辑运行的错误信息和普通的打印信息进行分类,更好地为程序员定位错误位置和写日志 

文件fd的分配规则:从小到大进行寻找没有被使用数据的位置,然后分配给指定的打开的文件

重定向

重定向可以分为清空重定向和追加重定向

1.清空重定向>

第一次写的hello world被第二次写的你好呀覆盖了,这就是清空重定向

2.追加重定向>>

第一次写的hello没有被第二次写的你覆盖,而是追加在hello后面,这就是追加重定向。

重定向本质:上层fd不变,fd指向的内容进行改变

struct file

struct file里主要包含文件的属性,文件的操作方法集和文件缓冲区

磁盘中的文件要加载到内存中,其实就是加载到对应的文件缓冲区中,这一工作由操作系统来做

  • 读数据:要将数据读到内存中——将磁盘中数据拷贝到对应的struct file 里的文件缓冲区
  • 写数据:要将数据读到内存中——将磁盘中数据拷贝到对应的struct file 里的文件缓冲区,然后再进行修改

我们在应用层进行数据读写的本质是什么?将文件缓冲区中的数据进行来回拷贝

缓冲区是什么呢?由上图可得缓冲区是struct file结构体里的一段空间,所以缓冲区本质上就是内存的一部分空间

为什么要有缓冲区呢?缓冲区就等于现实生活中的菜鸟驿站,我们要把一个东西送给朋友,有了菜鸟驿站就不用亲自开车去送,而是到驿站去寄快递,到驿站寄了以后,就会说已经把东西送了出去了,此时东西也不再属于我们了。所以缓冲区的最主要作用就是提高效率——提高使用者的效率

因为有了缓冲区的存在,可以积累一部分数据然后统一发送,减少了IO的次数,提高了发送效率

缓冲区因为可以暂存数据,必定有一定的刷新方式

  • 无缓冲(立即刷新)
  • 行缓冲(行刷新,\n)
  • 全缓冲(等缓冲区满了再进行刷新)

一般策略,特殊情况

  • 强制刷新
  • 进程退出的时候,一般要刷新缓冲区

一般对于显示器文件——行缓冲

对于磁盘上的文件—— 全缓冲(重定向后往往由行缓冲变成全缓冲)

一段样例

#include<stdio.h>
#include<string.h>
#include<unistd.h>

int main()
{
    fprintf(stdout,"C:hello fprintf\n");
    printf("C:hello printf\n");
    fputs("C: hello fputs\n",stdout);
    const char* str="system call:hello write\n";
    write(1,str,strlen(str));
    
    fork();
    return 0;
}

测试结果

1.当我们向显示器文件进行打印的时候,显示器文件的缓冲区是行缓冲,每一个字符串都有\n,fork之前所有数据都已经被刷新,有第一次的结果出现

2.当进行重定向后,磁盘文件的缓冲区是全缓冲,遇到\n不再刷新,也意味着缓冲区变大,这些字符串不足以将缓冲区写满让其刷新,fork执行的时候,数据仍然存在缓冲区中

3.我们目前谈的缓冲区和OS无关,只和c语言有关

4.当进程退出的时候,一般都要刷新缓冲区,哪怕没有满足对应的条件

5.fork之后立马退出,当一个进程退出的时候,刷新缓冲区,此时就要发生写时拷贝

缓冲区实际上分为用户缓冲区(C语言提供)和文件缓冲区

上面所谈以及日常使用最多的其实都是C/C++提供的语言级别的缓冲区,上面所说的刷新规则也都是C语言缓冲区的刷新规则

上文所说的刷新,就是把C缓冲区的数据拷贝到文件缓冲区

假如我们调用printf的函数,将要打印的数据拷贝到C语言缓冲区,这一函数就返回了

然后进行刷新,调用write函数,将C缓冲区的数据拷贝到文件缓冲区

如果直接调用系统调用write,它没有语言缓冲区,所以是直接写到文件缓冲区的

至于从文件缓冲区拷贝到磁盘这一刷新,就要视系统而定了

那么C语言的缓冲区具体是在哪?当调用C的文件操作的时候,返回值都是FILE,所以FILE结构体里不仅含有fd,还必定含有缓冲区

以上就是对打开文件的大概介绍,接下来谈谈在磁盘中存储的文件

虽然磁盘中的文件没有被打开,那要不要对其进行管理呢?答案是肯定需要的

对于这部分文件,最核心的工作就是对其进行快递定位,怎么做到?路径

在了解如何管理磁盘文件中,我们要先大概了解一下磁盘的物理结构抽象成逻辑结构

磁盘的物理结构和逻辑结构

众所周知,磁盘是一种硬件结构,那么如何存储数据呢?

下面是磁盘的物理结构示意图

一个盘面可以有很多的同心磁道,一圈磁道可以有很多相同的扇区

扇区是最小的存储单元,通常为512字节,即4kb

我们想要向一个扇区进行写入,那么要怎么寻址呢?

我们可以将磁盘结构进行逻辑抽象,这样对磁盘文件的管理就变成了数组的管理

所用的寻址方法是CHS

而文件系统通常以文件块为基本单位,一个文件块通常为4kb

对磁盘中数据的管理和上面类似

管理工作当然没有那么简单,我们肯定要对磁盘进行分类,就和C盘D盘一样

假设总共磁盘有500GB,对其进行分区分为100,100, 100, 200GB的内容

各个分区中又由2GB大小的group组成以及一个启动块Boot Block

这样只要我们管理好着2GB空间,就管理好一个分区,进一步管理好了整个磁盘文件

怎么管理好2GB大小的空间呢?当然是要了解这个块里的组成

  • 超级块(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节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
  • 数据区:存放文件内容

文件的属性和数据分开存储,属性存在iNode中,数据存在Data Block中

这里以新建一个文件的过程为例子,讲解每个区域的大概作用

新建一个文件,首先要在磁盘中的一个分区中找到一个group,利用iNode Tables查找struct iNode

 的使用情况,使用了对应位置为1,没使用为0,找到一个或者多个未被使用的struct iNode进行填充属性信息

关于存储的数据,通过块位图知道哪些文件块未被使用,将数据内容填充到对应的文件块,并将文件块下标赋值到iNode里的Blocks数组,这样通过iNode就能知道哪些文件块被使用,哪些文件块没被使用

虽然Blocks数组只有15个内容,但是其中下标0-12为直接映射,13是间接映射,14为三级映射,所以能记录的数据块是很多的

iNode编号在一个分区内具有唯一性,一个分区大概有32000个iNode,文件识别系统不认文件名,只认iNode编号

这里有一个很关键的问题,一开始你怎么知道文件在哪个分区呢?通过路径的前缀判断在哪个分区

上面说的是文件,那要怎么理解目录呢?

目录本质上也是一个文件,存储的是文件名和iNode编号的映射关系

当一个文件被建立好后,对应的目录也要进行记录

软硬链接

我们先对一个目录中的文件进行硬软链接操作,得到以下结果

可以发现,硬链接的iNode编号和原本文件的iNode编号是一样的,软链接的iNode编号和原本文件的iNode编号是一样的

可以得出硬链接不是独立的文件,软链接是独立的文件

当对一个文件建立硬链接的时候,iNode里的引用计数会增加,删除的话会减少,当引用计数为0时磁盘对应文件内容被释放,即相应的iNode BitMap对应的位置改为0

软链接则是一个独立文件,因为iNode编号不同,内容保存的是指向的目标文件的路径,类似Windows的快捷方式

用户无法对目录新建硬链接操作,因为建立一个目录并进去后,发现默认有.和..两个文件表示当前目录和上级目录,这里的.就相当于已经对目录建立硬链接了

动静态库

在理解什么叫做动静态库之前,应该先理解什么叫做库?

库在日常生活中随处可见,我们之前学习的数据结构unordered_map,unoredered_set就需要包含对应的库文件——库其实就是写来给人提供服务的,里面包含各种函数从而实现了强大的功能

根据不同用法又将库分为静态库和动态库

静态库

静态库简单来说就是将库里面的代码拷贝到可执行程序的代码区,程序运行的时候就不再需要静态库了,因为程序里已经有对应的代码了

由于是直接拷贝库的内容,所以静态库就会很大

要是一个库被重复使用很多次,还是静态库,那么很多个可执行程序里都有相同的代码,造成空间的浪费

动态库

动态库简单来说就是可执行程序拿到动态库的首地址,在可执行程序运行的时候再去拿地址去链接库文件进行使用

动态链接的程序加载的时候,不仅程序要被加载,被链接的库也要进行加载,被映射进虚拟地址空间的共享区

动态库的本质:将重复代码放到一个地方,以节省空间

动态库因为不是直接在可执行程序里的,所以链接的时候就要去找库

库的搜索路径

  • 从左到右搜索-L指定的目录。
  • 由环境变量指定的目录 (LIBRARY_PATH)。
  • 由系统指定的目录。
  • /usr/lib
  • /usr/local/lib

本人理解:感觉动态库就和指针一样,需要用的时候通过它去找,代码只存在一份,不会占用空间

                  静态库是直接拷贝的,占用空间更大

静态库把方法加载到可执行程序中,加载几个可执行程序就在内存中有几个静态库的内容,动态库第一次调用加载到内存中,以后在继续使用这个库的可执行程序可以直接找到

静态库和动态库的生成

静态库的生成

写出mylib.h代码

#ifndef __MYLIB_H__
#define __MYLIB_H__ 
#include<stdio.h> 
void print();

#endif

写出mylib.c代码

#include"mylib.h"
void print()
{
    printf("Hello Linux!\n");
}

写出main.c代码

#include"mylib.h"
int main()
{
    print();
    return 0;
}

生成静态库第一步,先生成目标文件: gcc -c mylib.c -o mylib.o

第二步,用目标文件生成静态库:ar -rc libmylib.a mylib.o
        ar是gnu归档工具,rc表示(replace and create)

可以查看静态库中的目录列表:ar -tv libmylib.a

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

第三步运行main.c得到结果。 gcc main.c -L. -lmylib

-L 指定库路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行。

生成动态库

还是上面的代码为例子

生成动态库第一步,先生成目标文件: gcc -fPIC -c mylib.c

2.生成动态库
  • shared: 表示生成共享库格式
  • fPIC:产生位置无关码(position independent code)
  • 库名规则:libxxx.so

第二步,用目标文件生成动态库:gcc -shared -o libmylib.so *.o

3.使用动态库
  •  编译选项
  • I:链接动态库,只要库名即可(去掉lib以及版本号)。
  • L:链接库所在得路径。

第三步运行main.c得到结果。 gcc main.c -o main -L. -lmylib

可以看看这篇文章:https://blog.csdn.net/qq_44918090/article/details/118185830?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170859497116800180637536%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170859497116800180637536&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-28-118185830-null-null.nonecase&utm_term=linux&spm=1018.2226.3001.4450

一些补充:

能够在共享区任意加载的关键:偏移地址

加载进内存的时候,每个指令都有自己的物理地址和虚拟地址(偏移地址)

虚拟地址(偏移地址)初始化页表左边,物理地址初始化页表右边

cpu用的全是虚拟地址,所以调用的时候才会有虚拟地址到物理地址的转变

我们见到的FILE *文件流指针,其实就是_IO_FILE的类型重定义,其中封装包含了文件描述符,因此一个文件流指针一定对应有一个文件描述符。

总结

以上就是今天要讲的内容,本文仅仅简单介绍了进场基础IO的使用,而系统编程提供了大量能使我们快速便捷地处理数据的函数和方法,我们务必掌握。这篇文章写了挺久,如有错误欢迎讨论,也请大家多多支持!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL面试题及答案

介绍 在快节奏的数据管理和信息技术世界中,导航和操作结构化数据的能力是一项非常重要的技能。SQL,即结构化查询语言,是关系数据库的基石,掌握这种语言的专业人员的需求量很大。SQL 面试在科技行业很常见,潜在的候选人会接受测试以展示他们的知识和解决问题的能力。为了帮…

酷开科技丨新年新玩法!酷开系统壁纸模式给客厅“换”新

甲辰龙年即将到来&#xff0c;新年新家新气象&#xff0c;快到酷开系统壁纸模式中挑选一款喜欢的壁纸&#xff0c;为新的一年增添一份美好和喜悦吧&#xff01; 酷开科技将更多的电视新玩法带给你&#xff0c;让你的电视成为家庭中的焦点&#xff01;酷开系统壁纸模式&#xf…

【前端素材】推荐优质后台管理系统APP Zina平台模板(附源码)

一、需求分析 当我们从多个层次来详细分析后台管理系统时&#xff0c;可以将其功能和定义进一步细分&#xff0c;以便更好地理解其在不同方面的作用和实际运作。 1. 功能层次 a. 用户管理功能&#xff1a; 用户注册和登录&#xff1a;管理用户账户的注册和登录过程。权限管…

easyui 手风琴Accordion 面板的高度设置

今天接到一个新的小需求&#xff0c;如下图&#xff0c;当预算表单只有一个时&#xff0c;要求不显示预算表单这块的内容。 考虑到页面创建时用到了表单的回调和点击方法&#xff0c;所以不能单纯的移除&#xff0c;移除右侧表格的创建会报错&#xff0c;所以只能隐藏。 隐藏…

突破挑战:利用沃尔玛跨境智星实现批量注册与下单

在进行沃尔玛测评时&#xff0c;确保环境安全至关重要。每个账号都应使用独立的运行环境&#xff0c;这样可以避免浏览器指纹、字体以及浏览器数据之间的关联问题。此外&#xff0c;账号的资料也需要特别注意。最好是自己注册自己下单&#xff0c;这样可以确保所有资源都掌握在…

JAVA工程师面试专题-并发编程篇

目录 一、线程 1、并发与并行的区别 2、同步和异步的区别 3、Java中创建线程有哪些方式? 4、Thread和Runnable的区别 5、Java中的Runnable、Callable、Future、FutureTask的区别和联系&#xff1f; 6、说一下你对 CompletableFuture 的理解 7、volatile关键字有什么用&…

如何下载 VisualStudio2022 离线包

1.概要&#xff0c;有些场景下&#xff0c;不允许联网&#xff0c;需要下载离线安装包。 2.过程 2.1 下载地址 创建基于网络的安装 - Visual Studio (Windows) | Microsoft Learn 2.2 选择引导程序 2.3 执行命令 2.3.1 下载全部&#xff1a; vs_Enterprise.exe --layout …

芯科科技与Arduino携手推动Matter普及化

双方的合作可助力开发人员在两分钟内将新开发板配置入网 致力于以安全、智能无线连接技术&#xff0c;建立更互联世界的全球领导厂商Silicon Labs&#xff08;亦称“芯科科技”&#xff0c;NASDAQ&#xff1a;SLAB&#xff09;日前宣布&#xff0c;公司与开源硬件和软件领域的…

如何判断领导是在培养你,还是在压榨你?

在职场中&#xff0c;判断领导的真实意图并非易事。有时&#xff0c;他们的行为可能让人困惑&#xff0c;不清楚他们是在真心培养你&#xff0c;还是在过度压榨你。 在职场提高工作效率&#xff0c;我们可以使用进度猫这样的项目管理工具来管理任务。通过使用进度猫甘特图&am…

【linux进程间通信(二)】共享内存详解以及进程互斥概念

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; 进程间通信 1. 前言2. 共享内…

2024高效神器:思维导图,让你的思维飞翔!

在信息爆炸的时代&#xff0c;如何高效地处理、整合和记忆海量信息成为了摆在我们面前的一大挑战。而在这个关键时刻&#xff0c;思维导图凭借其独特的优势&#xff0c;正逐渐成为2024年最受欢迎的高效神器。那么&#xff0c;思维导图究竟有何魅力&#xff0c;能够助我们一臂之…

【Linux进程】冯·诺依曼体系结构以及操作系统的深入理解

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.冯诺依曼体系结构特…

igolang学习2,golang开发配置国内镜像

go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct

问题1-spring-boot版本和org.springframework的spring-web兼容的版本

报错问题如下&#xff1a; Description: An attempt was made to call a method that does not exist. The attempt was made from the following location: org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.createHandlerMethod(AbstractHandlerMeth…

全国均薪20.7W!PMP持证者越来越吃香了!

摘要&#xff1a;现如今&#xff0c;PMP已成为全球公认的项目管理专业资格认证之一&#xff0c;得到许多国家专业人士的认可。在我国&#xff0c;项目经理的平均年薪达到了20.7w&#xff01; PMP最早是由PMI发起&#xff0c;已成为全球公认的项目管理专业资格认证之一&#xff…

音视频技术-电脑连接调音台时交流声的产生与消除

当电脑(笔记本/台式机)声卡通过音频线与调音台(或扩音机)连接时,能听到“交流声”。有时很轻微,有时很明显,甚至干扰正常的演讲或发言。 很多时候,我们在台上演讲时,都会使用电脑播放PPT,遇到视频时,还需要将视频中的音频扩大。电脑音频的输出口一般都是3.5的,我们…

Curfew e-Pass 管理系统存在Sql注入漏洞 附源代码

免责声明&#xff1a;本文所涉及的信息安全技术知识仅供参考和学习之用&#xff0c;并不构成任何明示或暗示的保证。读者在使用本文提供的信息时&#xff0c;应自行判断其适用性&#xff0c;并承担由此产生的一切风险和责任。本文作者对于读者基于本文内容所做出的任何行为或决…

【Git工具实战】实用真实 Git 开发工作流程

前言 最近工作中发现&#xff0c;很多开发人员连最基本的Git怎么使用都不知道&#xff0c;比如什么时候切分支&#xff0c;什么时候合并代码&#xff0c;代码遇到冲突怎么办&#xff0c;经常出现掉代码&#xff0c;代码合并后丢失的情况。以下为个人总结的常规Git开发工作流程…

linux系统消息中间件rabbitmq普通集群的部署

rabbitmq普通集群的部署 普通集群准备环境查询版本对应安装rabbitmq软件启动创建登录用户开启用户远程登录查看端口 部署集群创建数据存放目录和日志存放目录:拷⻉erlang.cookie将其他两台服务器作为节点加⼊节点集群中查看集群状态创建新的队列 普通集群准备环境 配置hosts⽂件…

【Spring】IoC容器 控制反转 与 DI依赖注入 三种实现方式 总结 第五期

目录 1 - 4 期三种配置方式总结1. XML方式配置总结2. XML注解方式配置总结3. 完全注解方式配置总结 整合Spring5-Test5搭建测试环境 1 - 4 期 介绍 IoC DI Xml实现 IoC DI 注解 Xml 实现 IoC DI 完全注解实现 三种配置方式总结 1. XML方式配置总结 所有内容写到xml格式…