《Linux是怎样工作的》读书笔记

目录

  • 前言
  • Chapter 1 计算机系统概要
    • 1.1 计算机的硬件
    • 1.2 OS
    • 1.3 用户模式和内核模式
    • 1.4内核
  • Chapter 2 用户模式实现的功能
    • 2.1系统调用
    • 2.2系统调用的包装函数
    • 2.3 OS提供的程序
  • Chapter 3 进程管理
    • 3.1进程与线程
      • 进程
      • 线程
      • 进程和线程的区别
    • 3.2 fork
    • 3.3 execve
    • 3.4 结束进程
  • Chapter 4 进程调度
      • 逻辑CPU
      • 调度器
    • 4.1 上下文切换
    • 4.2 进程状态
    • 4.3 吞吐量与延迟
    • 4.4 多逻辑CPU的调度
    • 4.5 进程调度
  • Chapter 5 内存管理
    • 5.1 内存信息
    • 5.2 逻辑地址
    • 5.3 内存分配
    • 5. 4 页表
    • 5.5 mmap
    • 5.6文件映射
    • 5.7 请求分页
    • 5.8 写时复制
    • 5.9 Swap
      • 内存不足
      • Swap
      • 换出
      • 换入
    • 5.10 多级页表
  • Chapter 6 存储层次
    • 6.1 局部性原理
    • 6.2 Cache
    • 6.3 缓冲区
      • 6.3.1 TLB
      • 6.3.2 页面缓冲
      • 6.3.3缓冲区缓存
  • Chapter7 文件系统
    • 7.1 什么是文件系统
    • 7.2 Linux文件系统
    • 7.3 文件系统不一致
      • 7.3.1为什么会不一致
      • 7.3.2 日志
      • 7.3.3 写时复制
    • 7.4 文件种类
      • 7.4.1 块设备
      • 7.4.2 字符设备
  • Chapter 8 外部存储
    • 8.1 HDD
    • 8.2 SSD

前言

    《Linux是怎样工作的》这本书是以Linux为例,讲了OS相关知识。作为入门书籍,这本书讲的很浅。但它做到了入门书籍应有的通俗易懂,让初学者更容易找到学习的乐趣,而不是被复杂的知识体系劝退。这本书通过大量的实验来证实书中所讲述的原理的正确性。这中简洁明了的方法更容易让读者理解和记住原理性的东西。在看完本书后,你或许不记得那个原理是怎样阐述的,但你可能会记得那个实验给你带来了怎样的启发。为了更好的让初学者理解一些知识点,书中和我的笔记中都省略了一些细节方面的东西,如果非要将这些细节的话,那就不符合这本书的本意和初衷了。

   有关读者的信息和书籍信息我就不在这里写了,相信能看到这篇文章的彦祖们应该对这本书有了一定的了解了。这篇博客是我在读完这本书后的一些个人理解,不一定对,毕竟我也是在学习,欢迎各位彦祖对存在疑惑的地方与我进行亲密有好的交流。

Chapter 1 计算机系统概要

1.1 计算机的硬件

     在了解Linux前,我们先简单的了解以下计算机的硬件结构,大概的结构如图所示。CPU,内存,外部存储设备,还有一些外设。计算机的工作方式就是把储存在外部存储设备(后面为了方便阅读,我们叫它硬盘好了)中的程序读入到内存中,然后把内存中的程序一条条的送到CPU里去处理。CPU在把处理好的结果送到内存里,然后根据需求把内存中的结果进行展示或者再存回到硬盘里。

其实还有一些必须存在的硬件,如Bus,主板等。
CPU执行程序的过程远比这里讲的要复杂的多。
程序的处理过程与书中的描述不太一样,但本质都是差不多的,按照你能理解的方式来阅读。

在这里插入图片描述

1.2 OS

在讲解OS前,我们先来聊一聊计算机软件。计算机软件大致可以分为三大类。

  • 应用程序: 直接与用户打交道的软件。如QQ微信
  • 中间件: 将一些应用程序都使用的功能抽离出来,形成的软件。
  • 操作系统: 与不同型号,不同指令集,不同规格的硬件相连,直接控制硬件的软件。给上层提供运行环境。
    在这里插入图片描述
    OS的位置如上图所示,可能会有人问了。直接在硬件上写应用程序不行吗,为啥非要在操作系统上写。这个问题的答案有很多,我的理解是如下
  • 分配资源。 如果各个程序都在CPU上直接运行,那到底CPU该听谁的,给谁干活
  • 硬件透明化。 如果每个程序都直接调用硬件的话,面对不同规格的硬件,就要针对每种规格的硬件都写代码。如果使用OS中的设备驱动器来帮助我们,在写应用程序的时候就不用考虑硬件规格,直接调用OS给的接口就可以了。
    有点类似与计算机网络中的分层思想。要实现本层的功能,需要使用下面一层提供的服务。下层的实现对本层是透明的。

其实这种思想 在计算机领域里很常见。曾经就有人因讨厌跨平台开发,于是想到使用性能强劲的,开源的Chromium为平台,来开发自己的程序。这样跨平台的重任就交给了Chromium的开发团队。而自己只需要在Chromium更新时维护一下自己的代码就可以了。机智.png

1.3 用户模式和内核模式

现在我们使用的都是多任务操作系统,每个任务都会需要使用到一些硬件资源。为了防止某些软件的BUG或者病毒软件导致硬件损坏,我们不能让应用程序直接操作硬件。操作硬件这种容易出现危险的事情应当让我们信的过的软件来操控,这个软件就是操作系统的内核。
那我们该如何区分某个操作是来自OS的还是应用程序的呢?办法就是给操作加等级属性。将操作分为不同的等级,OS的操作我们给他的等级高一点,应用程序的等级低一点。

  • 特权指令:只能由操作系统内核部分使用,不允许用户直接使用的指令。如,I/O指令、置终端屏蔽指令、清内存、建存储保护、设置时钟指令(这几种记好,属于内核态)。
  • 非特权指令:所有程序均可直接使用。
    用户模式下只能执行非特权指令,而内核模式则可以执行特权指令和非特权指令。
    在这里插入图片描述

1.4内核

内核是操作系统最基本的部分。它是为众多应用程序提供对计算机硬件的安全访问的一部分软件。简单来说内核就是访问硬件的软件。例如IO访问。内存管理,进程调度。
在这里插入图片描述

Chapter 2 用户模式实现的功能

2.1系统调用

系统调用是什么,我从网上照了一张快被盘包浆了的图来展示一下它所处的位置。简单来说就是为了给应用程序访问硬件资源提供的一个接口,应用程序与内核交互的接口。
在这里插入图片描述
一个程序的运行需要下面这些系统调用,当程序使用系统调用会发生CPU模式切换。

  • 进程控制
  • 内存管理
  • 进程间通信
  • 网络管理
  • 文件系统操作
  • 文件操作

CPU的运行模式和系统调用
在这里插入图片描述
接着书中就是用示例来展示了如何将CPU从用户态切换到内核态。

2.2系统调用的包装函数

系统调用并不能被高级语言直接调用(如C语言),只能由汇编代码发起。但是编写汇编代码耗时耗力,为了解决这个问题,OS提供了一套被称为系统调用的包装函数。如C语言中的glibc就包含了系统调用函数。
在这里插入图片描述
在这里插入图片描述

2.3 OS提供的程序

OS还提供了一些简单的程序供用户或开发人员使用
在这里插入图片描述

Chapter 3 进程管理

进程管理是内核中一个重要的部分

3.1进程与线程

进程

进程的概念有很多,这里讲两种定义

  • 进程是允许并发执行的程序在某个数据集合上的执行过程。

这个定义强调的是进程是个过程,进程可以并发执行,同样的程序在不同的数据集上就是不同的进程。

  • 进程是由用户数据,系统数据和程序构成的实体。

线程

定义:线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程只拥有在运行中必需的资源,包括程序计数器、一组寄存器和栈,但它可以与同属一个进程的其他线程共享进程的全部资源,如:虚拟地址空间、文件。

进程和线程的区别

  • 线程是进程的子集,一个进程可以拥有多个线程,但一个线程只能属于一个进程。
  • 线程是被系统调度的最小单位。

在这里插入图片描述

3.2 fork

fork函数是基于当前进程,创建一个新的进程,当前进程为父进程,新创建的为子进程。fork的过程如下

  • 申请一样大小的内存空间,父进程中的内容全部复制过去
  • 根据进程ID的不同执行不同的代码
    我们简单看下下面这个代码,调用fork后,进程变为两个,且两个进程中的ret不相等。根据ret的值不同,执行不同的代码
    在这里插入图片描述
    当程序运行到fork()函 数时,就会如下图所示,创建新的进程,且ret不同。上面的是父进程,它的ret等于子进程的pid,子进程的pid为0。
    在这里插入图片描述

3.3 execve

execve函数就是将已存在的进程重新加载一下,重新读取数据,从头开始执行代码。
启动一个程序的流程就是从硬盘中读取程序和数据,然后添加一些用于控制进程的辅助信息。
在这里插入图片描述
在我们打算创建新的进程的时候,通常会采用fork and execve的方式。即是用fork创建新的进程,由execve为新的进程初始化。

关于这部分内容,书中采用了实验的方式。

3.4 结束进程

使用_exit()(底层为exit_group()系统调用)来结束,但一般使用c语言的exit()来结束。

Chapter 4 进程调度

进程调度也是内核中一个重要的部分

逻辑CPU

  • cpu核数:单块CPU上面能处理数据的芯片组的数量,如双核、四核等 (cpu cores)
  • 逻辑cpu数:在现在都支持超线程的情况下,一个CPU在逻辑上可以当两个CPU使用。

从下面截图可以看到我的CPU核数为6核心,12个逻辑CPU
在这里插入图片描述

调度器

  • 一个CPU任何时刻只能运行一个进程(逻辑CPU)。
  • 当多个进程运行的时候每个进程都可以获得一定时间运行。这个时间段被称为时间片time slice。

4.1 上下文切换

上下文切换指的是切换正在逻辑CPU上运行的进程。不论此时CPU在做什么,只要当前进程的时间片用完后就一定会发生上下文切换。

我们在排查函数性能的时候,应该考虑到存在上下文切换的因素。

在这里插入图片描述

4.2 进程状态

由于每个时刻只有一个进程可以使用CPU,所以其他进程需要进行等待。而有的进程在运行的时候需要等待某些资源,例如等待输入的程序。进程的状态大致可分为以下几种,在不同的OS中状态的设置不太一样。
在这里插入图片描述

在这里插入图片描述

4.3 吞吐量与延迟

吞吐量: 单位时间内完成作业的个数。
延迟: 当一个进程从载入到内存开始直到运行结束所耗费的时间。(包括等待的时间)
书中对这部分内容进行了许多实验,我这里直接给出结论,想要了解实验内容的同志可以看书。

  • 一个CPU执行一个进程时的吞吐量最大。

该进程不会进入到休眠态

  • 一个CPU执行一个进程的时候延迟最小

在某个进程执行的过程中,不会出现其他进程。当该进程执行完后才出现其他进程,

  • CPU的吞吐量跟空闲时间呈反比。空闲时间越多,吞吐量越低。

当存在多个进程的时候,尽管CPU不空闲,一直在忙,但会因为频繁的发生上下文切换而导致吞吐量降低

  • 延迟与进程的个数大致呈正比。

进程越多,每个进程在使用完时间片后等待的时间就越多,进程完成的时间就越慢。

4.4 多逻辑CPU的调度

当存在多个逻辑CPU的时候(现在的计算机基本都是这样的),进程调度程序会采用负载均衡或者全局调度的方式,在任务较多的时候每个CPU都能干点活。而不是出现经典的一核有难八核围观的场景。
在这里插入图片描述

4.5 进程调度

前面我们知道多任务时,每个任务都会获得运行时间。运行时间到底怎么分配就成了一个问题,这个问题的解决方案有很多。我们应该考虑的问题有哪些?

  • 优先级。某些事件比较紧急,我们应当优先去做这些事情。
  • 时间片的长度。对于不同的进程,不同的处理能力的CPU,我们应该怎么样去给他们分配时间。
  • 根据OS的所强调的特点去制定规则

总而言之,这里需要考虑的动态太多了,我只简单的列举了一下。

Chapter 5 内存管理

内存管理也是Linux内核中的一个重要功能
我们的程序从硬盘载入到内存后到底放在哪里?程序怎么去查找?怎么分配内存空间?这些问题都会在这一章得到答案(得不到的你们再想想办法)。

5.1 内存信息

我们使用free命令可以查看到本机上的内存信息。单位是KB。我们先看看Men中的信息,至于Swap我们后面再看。

  • total:本机一共的可用内存(我这是虚拟机,只给了4G)
  • used:已使用的内存
  • free:表面可用内存,未被任何程序使用过的内存。
  • shared:共享使用的内存
  • buff/cache:可以先理解为缓冲区,具体细节后面再说
  • availabe:实际可以用的空间。为了提升读写性能,会使用一部分内存作为缓冲区,当内存不够用的时候,这部分是可以释放掉的。后面会讲。
    在这里插入图片描述

5.2 逻辑地址

回忆一下在你编写程序需要操控内存的时候,你是怎么操作的?在高级语言中(C/C++等较为偏底层的语言),我们操控内存地址其实都是逻辑地址。我们程序的每一行代码,每一个变量都是需要内存的,每个程序开始的地址都是0,然后依次往后排。与之对应的是物理地址,就是内存条上的内存地址。逻辑地址组成的空间叫逻辑空间空间。内存条上的地址组成的空间成为物理地址空间。为了保证程序的安全性,防止某些BUG或者病毒破坏其他进程内存中的数据,利用逻辑地址这种方法使得程序只能访问自己内存空间内的地址。在把程序载入到内存后通过某种映射方法使得我们可以找到某个变量的物理地址,从而读取到他得值。
逻辑地址的出现解决了一些问题

  • 防止程序读取其他程序甚至是内核的地址上的内容
  • 便于执行多任务,每个程序在编写的时候都不用考虑自己的地址会被挡在内存上的哪个位置。

书中则是将逻辑地址称为虚拟地址。

在这里插入图片描述

5.3 内存分配

我们先使用简单的方法给一个一个需要100KB的程序分配内存。步骤如下

  • 计算程序需要的内存大小。(这里指的是静态的,不动态申请内存程序)
  • 在内存中找到一片大于100KB的空间分给它。
  • 程序结束释放掉这100KB的空间。

这种方式会出现一些问题

  • 内存碎片化:先来一堆程序申请内存,把内存都用完了,然后几个离得很远的程序运行结束释放空间了。这几片内存相隔较远。假如每个都是100KB,有5个,此时来了一个300KB的程序,内存够不够?够啊,5*100 > 300,但不连续啊,这怎么管理。
  • 动态申请内存:程序需要动态申请内存的时候,前后的内存空间都分给别人了,这个时候怎么给他申请啊,也不是不行,找一个别的区域分配,但这咋管理啊
    在这里插入图片描述

5. 4 页表

为了解决内存碎片化的问题,提出了页表的概念。
我们把物理内存分割成无数片大小相同的区域,每个区域称为页框。把程序也分割成一样大小的区域,叫。然后一个页放到一个页框里,每个页和页框都有编号,我们在把他们的映射关系用页表存储。这样我们解决了内存碎片化的问题。
强调一下各个名词的含义

  • 页: 逻辑地址空间的分割。
  • 页框: 物理地址空间的分割
  • 页表: 存储逻辑地址和物理地址之间映射关系的表
  • 页表项: 页表里的每一条记录都成为页表项。每个页表项都说明了一个映射关系。

不够一页的给它分一页
当一个程序访问的逻辑地址在页表中查询不到时候,就会发生缺页中断,然后启动内核的相关机制判断是否为非法访问,如果是,通常会给这个程序干掉。叫你瞎访问。

在这里插入图片描述

5.5 mmap

书上通过一个实验分配了100MB空间,这个实验没啥可讲的,大家看书就行,这里想说的是实验中用到的一个函数mmap()。 在实验中,使用mmap函数可以通过系统调用向Linux内核发起申请内存的请求。同样的,C语言标准库中的malloc()函数也是可以申请内存的。malloc函数在Linux中,也是通过mmap来申请内存的。

  • mmap:以页为单位申请内存
  • malloc:以字节为单位申请内存
    为了获取字节为单位的内存,glibc先通过mmap获取一片内存区域,作为内存池,然后再把内存池中的内存以字节为单位分配给申请者。当内存池中的内存不够分或者分配完的时候,glibc会再通过mmap申请。
    在这里插入图片描述
    在这里插入图片描述

5.6文件映射

文件的读写通常都是通过read,write等的函数调用来访问硬盘,这样做的一个缺点就是速度慢。Linux提供了一种功能,可以将硬盘上的文件载入到内存中,然后映射到逻辑地址空间。书中通过实验验证了这个方法的可行性。
在这里插入图片描述

5.7 请求分页

我们前面提到的内存分配是

  • 内核分配内存
  • 内核设置页表,完成映射

这样做有一个缺点,还记得之前爆出的gta的屎山代码吗,里面存在大量的if,else,假如我们有一段代码,if语句内的内容写了很长很长,比你的命都长。但是!它基本每次都不执行,那这个语句所在的页,分到的页框,到运行结束都不会用一次,这简直就是暴殄天物啊
请添加图片描述
为了解决这种现象,Linux使用了一种机制,请求分页。页对应的物理内存只有在第一次访问的时候才会进行分配。也就是说,如果这个页从头到尾都没有访问过,那就不会给他分配物理内存。在页表中会用一个标记来记录是否分配物理内存
我们再从头捋一下

  • 计算程序所需大小并分配页框(此时并不会真的分配,在页表中标记一下)
  • 执行代码
  • 发现没有分配物理内存
  • 引发缺页中断
  • 分配物理地址(这个时候才真正的分配了物理内存)
  • 接着执行刚刚的代码

书中用mmap函数申请了100MB的空间,从头到尾访问内存。另一边开着监视内存资源的窗口观察,证实了上面的理论。并且还查看了发生缺页中断的频率。

5.8 写时复制

与请求分页一样,写时复制也是一种节省空间的做法。
利用fork创建新的进程时,并不会直接把父进程所有的内存数据复制一份给子进程,而是只复制页表。共享父进程的内存数据。当父进程和子进程都是在读数据的时候,共享内存。当任何一个进程有写入操作的时候,就会缺页中断,进入到内核态。缺页中断机制会把被访问的页复制一份给这个准备写入的进程,原来的那个页则留给另一个进程。然后修改父子进程的页表项。
在这里插入图片描述

5.9 Swap

内存不足

内存不足有两种情况。第一种就是逻辑地址空间不足,在x86架构的,逻辑地址空间最大为4GB(2的32次),但这个问题在现在64位的架构上基本不会出现。第二种是物理内存不足,任务太多,内存用完了!这种情况下不论系统做什么都会出现内存不足OOM Out Of Memory。进入OOM状态后,内存管理系统会启动一个非常可怕的功能: OOM killer,随机挑选一名幸运观众干掉。这对于服务器来说简直是一种灾难级的事情。
在这里插入图片描述

Swap

Linux为了补救OOM状态,使用了虚拟内存机制。简单来说就是把外存划分出来一点当内存用。当物理内存不够的情况下,有任务申请内存时,把一些页放到外存里,从而腾出一些内存空间。放页的外存区域称为Swap分区(交换分区)

换出

假如此时内存已满,但有程序申请空间,内核将会根据一定的算法(页置换算法)选中一些页,给它扔Swap里,这不就有空闲的内存了嘛。因为是从内存往外放,这个过程叫换出。

换入

当每个程序执行的时候,发现自己要执行的这条指令(或数据)并不在内存里,在Swap里。那就想想办法搞点内存空间,然后把这个页弄进来。这个过程叫换入。

需要注意的时,外存的速度很慢的。当内存不足时,会频繁的发生换入换出,发生系统抖动

5.10 多级页表

书中讲的多级页表,标准大页,透明大页什么的,根本思想跟页表的思想大差不差。这里就不展开说了。

Chapter 6 存储层次

这张图都见过吧,没见过的现在也见到了。计算机发展到现在阻碍最大的一个问题就是存储墙。简单来说就是数据的读写速度严重低于数据的处理速度。也就是内存的速度远低于CPU的速度。存储器的分层结构就是为了解决这个问题。(并不是解决,其实更像是一种妥协,一种折中的方法)本章中的所有知识点和方法都是为了提升读写速度。
在这里插入图片描述

6.1 局部性原理

局部性原理说的很简单,就两个方面

  • 时间局部性:如果程序中的某条指令一旦执行,则不久之后该指令可能再次被执行;如果某数据被访问,则不久之后该数据可能再次被访问。
  • 空间局部性:是指一旦程序访问了某个存储单元,则不久之后,其附近的存储单元也将被访问。
    这看起来很简单,但局部性原理不止一次的救了我们。

6.2 Cache

CPU处理指令的速度远比从内存中读出指令的速度快。为了减小这两个速度之间的差距,人们在CPU中加入了一个速度比内存块,容量比内存小的存储体:高速缓存(Cache),来平衡这两者的速度差。
当CPU读取指令或者数据的时候,会先到Cache中查看有没有,如果没有,则到内存中查找,并把这个地址相邻的内容都一起拿到Cache里。至于拿多少,取决于Cache块的大小。会根据Cache块的大小拿数据,然后放到Cache块里。根据局部性原理可知,访问了某个存储单元后不久,附近的存储单元也会被访问。那大概率下一条指令或数据也会在这个Cache块里。下条指令或者数据就不用去内存里找了,可以直接在Cache里找到,这样访存的速度就会得到提升。

实际上的过程是内存把数据送到cache,cache把数据送到寄存器,CPU从寄存器中拿数据。

在这里插入图片描述
把内存中的内容拿到Cache里面后,如果是条指令的话,那就直接执行就可以了。但如果是数据,并且是写数据,那这个时候Cache里的数据就会和内存中的数据不一致,为了防止出现错误,我们就把这个Cache块标记为“脏块”
等到一定的时间后我们再把这个Cache块写回内存。这种方式叫回写(write back),还有一种方式就是当这个数据被修改后,立即写回内存,这种方式叫做直写(write through)。

当经过一段时间后,Cache用完了,这个时候就会把一部分很久没用的Cache块释放掉来获得新的空间,当然具体释放什么样的Cache块有很多种评判标准,这里就不展开说了。但是在释放前一定要将脏数据写回。

CPU中一般都会存在多级缓存,例如此时我使用的CPU就是三级缓存。级数越低,速度越快,容量越小。
在这里插入图片描述

6.3 缓冲区

6.3.1 TLB

在讲了内存,页表,Cache后,我们再来捋一下CPU访存的过程。

  • 根据逻辑地址,访问页表,的到物理地址
  • 根据物理地址先去Cache里找,存在直接返回,不存在再去内存找
  • 在内存找到后,拿Cache块大小的内容放到Cache里。
  • 把Cache里返回给CPU

在上面过程中,Cache的作用是为了加快访问内存的速度。但根据逻辑地址在页表里获得物理地址这一步,仍然是要访问内存的,这一步可没有Cache。为了解决这个问题,CPU上有个专门的内存区用来存放最近用过的页表,这块区域的速度跟Cache差不多快。这个内存区域叫TLB(Translation Lookaside Buffer),由于这块区域是用来存页表(页表就是将逻辑地址和物理地址映射的表)的,也叫快表或页表缓冲。

6.3.2 页面缓冲

既然可以引入Cache来弥补CPU和内存间的速度差距,那有没有什么办法来弥补外存和内存间的差距呢?其实是有的。在内核占用的内存中,有一片区域叫页面缓存区,这个区域就是用来弥补外存和内存的差距的。
当程序从外存读了一个文件到内存中,第一步不是把文件复制到进程的内存区域里,而是先复制到内核里的页面缓存区,然后再从页面缓存区复制到进程的内存里。
在这里插入图片描述
在此之后,如果有程序还要读这个文件,就不用从外存读取了,而是直接从页面缓存区读取。因为页面缓存区是全局共享的资源。跟Cache一样,当有数据脏了后,也是有回写和直写。

6.3.3缓冲区缓存

缓冲区缓存与页面缓存类似,是跳过文件系统,通过设备文件直接访问外部存储器时使用的区域。

Chapter7 文件系统

7.1 什么是文件系统

我们常说的文件系统到底是什么,有什么用呢?
简单点的说,文件系统就是将我们需要保存的程序,照片,视频等的数据存到外部硬盘里并管理这些数据。而外部硬盘可以永久存储,不会因为断电而丢失(对应的是内存,断电后就会丢失数据)。
为了更直观的了解到文件系统存在的必要性,我们来看看如果没有文件系统,程序将会如何存储数据。
假如(我是说如果啊)你通过浏览器在网上看到一张坤哥的图片(律师函警告!)(狗头保命),你想把他保存到硬盘里,方便以后观摩,那么浏览器会怎么保存呢?
在这里插入图片描述
浏览器会去硬盘里找一个位置,然后把要存的数据放里面。然后把文件的名称,起始地址,偏移量(数据长度,可以通过起始地址加偏移量获得该文件的数据),等的数据记住,然后下次再访问的时候就可以找到了。
再假如,你用C语言写一个很简单的计算器,输入两个数,只能做加法,要求保存结果和查看历史记录。没有文件系统的情况下,你可以想想一下需要自己写底层代码去访问硬盘的复杂程度。

以上两个例子存在以下问题:

  • 浏览器怎么知道哪里有空闲空间?
  • 保存的图片会不会被别的应用写入数据时覆盖掉?
  • 每个程序都要自己写访问硬盘的代码?
  • 如果有病毒恶意破坏硬盘数据怎么办?

文件存储是几乎所有的软件都需要,本着高内聚低耦合的原则,我们是不是可以把跟文件相关的操作都提取出来,单独做成一个软件呢?对,这玩意有,就叫文件系统。
访问所需要的文件要通过相关的系统调用来实现,这一点保证了文件的安全性。
在这里插入图片描述

7.2 Linux文件系统

为了方便管理数据,Linux的文件系统专门设置了一种特殊的文件,目录。就是我们经常在Windows下使用的文件夹(大致的思想还是一致的,尽管细节上各不相同)。目录的存在使得Linux的文件呈现出树状结构。 Linux中可以同时使用多种文件系统。

在这里插入图片描述
文件系统的种类有很多,如FAT32(目前常见于U盘中),NTFS(win上使用的),EXT4(Linux上常用的),ZFS(True NAS使用的)。不同的文件系统有着相似的操作

  • 创建和删除
  • 打开和关闭
  • 读取和写入
  • 移动和复制

尽管有着类似的操作,但实际实现上各不相同。因此,为了能够同时使用多种文件系统,Linux的文件读写流程被设计成下面这个样子。
在这里插入图片描述

7.3 文件系统不一致

7.3.1为什么会不一致

在我们对文件进行一些操作时突然断电将会发生什么?我们举个例子来看看。我们把bar文件从根目录下移动到foo目录下的大致流程如下图所示。

在这里插入图片描述
如果在步骤②完成后断电。 bar就会同时属于两个目录下,我们前面说了,Linux下的文件结构是树状结构。这显然不符合树状结构。也就是所谓的文件系统不一致。为了防止不一致状态出现引起系统出错,可以采取日志和写时复制策略
在这里插入图片描述

7.3.2 日志

对于上面的过程。我们拿出一片临时区域(日志区域)。在临时区写下我们要做的操作,然后去执行这个操作。每次都是先写下来,再去操作。写下操作叫前半部分,执行操作叫后半部分。大致流程如下
在这里插入图片描述
如果这种策略下断电,只有两种情况

    1. 更新日志的时候断电(写下操作)
    1. 执行日志的时候断电(执行操作)
      对于情况1,只需要丢弃日志即可,因为只是记录下来了要做的操作,实际并没做,数据本身并没有发生变化。
      对于情况2,只需要重新执行日志记录即可,在执行操作的时候断电,操作并没有完成,好在日志帮我们记录下了我们要干的事,我们上次没干完的,这次再干一边就好了。
      可见,日志是一种预防不一致的策略。

7.3.3 写时复制

与ext4和XFS等的不同,Btrfs系统在更新数据的时候,并不是直接在原位置上更新的,而是将文件在别的地方再写一份,然后把指针指向新的位置。
在这里插入图片描述
在执行步骤②的时候断电,只需在重启后删除未处理完的数据即可。
在这里插入图片描述

7.4 文件种类

除了最基本的文本,音频这类普通文件和目录外,Linux上万物皆文件的思想告诉我们,设备也是一种文件,被称为设备文件。设备文件又被分为两类,字符设备和块设备。设备文件可以在在/dev目录下查看。

网络适配器是个例外

7.4.1 块设备

如SSD和HDD,可以像常规文件一样打开读写,但实际上是需要通过文件系统进行访问的。

7.4.2 字符设备

常见的字符设备有鼠标,键盘,终端等。
可以通过echo命令向bash的终端写入一些字符串,然后就可以在终端看到了。

Chapter 8 外部存储

8.1 HDD

HDD(Hard Disk Drive)传统硬盘,其基本结构有盘片+磁头组成,盘片上都可以存储信息,磁头用来读取盘片上的信息。每个盘片被分为若干个扇区,如下图所示。HDD读写数据的基本单位是扇区而不是字节。
在这里插入图片描述
在接收到需要读写的地址后,磁头转动到相应的扇区所在的磁道上进行读写。但相比电子器械,物理器械的速度要慢得多。所以HDD的读写上限取决于盘片转动的速度和磁头移动的速度。
在这里插入图片描述

  • 顺序读写:HDD的顺序读写是要比随机读写快的,这是显而易见的,因为磁头转动到对应的磁道上了后,盘片只需要继续转动,就可以到达下一个扇区去读数据。
  • 随机读写:随机读写时,每次读写都需要磁头转动到相应的磁道上,然后磁头需要等到对应的扇区到了才能进行读写,故访问速度较慢。
    在这里插入图片描述

在这里插入图片描述

  • IO调度器:为了减少磁头摆动的次数,我们可以把多次请求累计起来,合并访问,以提高读写速度。
  • 预读:前面我们说了局部性原理,局部性原理在这里再次得到应用,我们只需要在顺序读写后再读一些连续的数据,如果这些数据是下一步需要的,那么我们的速度就得到了提升,如果没有用,丢弃即可。预读机制可以在请求量较小的时候把吞吐量发挥到极致。
    在这里插入图片描述

8.2 SSD

  • SSD的访存全是电子处理,故访问速度较快。
  • SSD的随机读取和顺序读取的差距并没有HDD那么大,原因是HDD的随机访问需要摆动磁头,耗费时间较长
  • 在开启IO支援后,SSD的访存速度可能会下降,这是因为IO调度的时间相较于访存时间并不像HDD的差距那么大,所以性能有有所下降。

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

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

相关文章

用 Real-ESRGAN 拯救座机画质,自制高清版动漫资源

本文约1200字,建议阅读8分钟Real-ESRGAN 是 ESRGAN 升级之作,主要有三点创新:提出高阶退化过程模拟实际图像退化,使用光谱归一化 U-Net 鉴别器增加鉴别器的能力,以及使用纯合成数据进行训练。 相比于如今画面优良精美的…

Git学习

安装Git 可参考:https://blog.csdn.net/mukes/article/details/115693833?ops_request_misc%257B%2522request%255Fid%2522%253A%2522168121346716800192220040%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id16812134671680019…

线性回归 梯度下降原理与基于Python的底层代码实现

线性回归基础知识可查看该专栏中其他文章。 文章目录 1 梯度下降算法原理2 一元函数梯度下降示例代码3 多元函数梯度下降示例代码 1 梯度下降算法原理 梯度下降是一种常用的优化算法,可以用来求解许包括线性回归在内的许多机器学习中的问题。前面讲解了直接使用公…

面渣逆袭:Java集合连环三十问

大家好,我是老三。上期发布了一篇:面渣逆袭:HashMap追魂二十三问,反响很好! 围观群众纷纷表示👇 不写,是不可能不写的,只有卷才能维持了生活这样子。 当然,我写的这一系…

Android-Activity生命周期

文章参考:文章参考1 文章参考:文章参考2 五大状态 StartingRunningStoppedPausedDestroyed 借用一张已经包浆的图 PS:Running和Paused是可视阶段,其余都是不可视 几大函数 onCreate:通过setContentLayout初始化布局…

Java 八股文-集合框架篇

Java 集合框架 一、常见集合 1.说说有哪些常见集合? 集合相关类和接口都在java.util中,主要分为3种:List(列表)、Map(映射)、Set(集)。 其中Collection是集合List、Set的父接口&#xff0c…

python爬虫入门篇

接下来的一些时间会分享一些爬虫相关的代码和知识 有人会问爬虫怎么舔女神? 我只能说浅了 看完伟大的Technical Licking Dog 的文章你将会对舔狗的认知得到一个升华! 目录 接下来的一些时间会分享一些爬虫相关的代码和知识 正文 爬虫的运行原理&…

程序人生 - 为什么表情包越转发越模糊,还会变绿?

当代人聊天离不开什么? 表情包!!! 没有表情包,怎么表达我的感情?(当然,我对你基本没什么感情~只是想秀一下沙雕表情包!)在过去的日子里,江湖上流传…

⚡【C语言趣味教程】(1) 深入浅出 HelloWorld | 通过 HelloWorld 展开教学 | 头文件详解 | main 函数详解

🔗 《C语言趣味教程》👈 猛戳订阅!!! ​—— 热门专栏《维生素C语言》的重制版 —— 💭 写在前面:这是一套 C 语言趣味教学专栏,目前正在火热连载中,欢迎猛戳订阅&#x…

正确保护Macbook

MacBook该如何正确保护呢?不是各种键盘膜、保护壳通通用上就是最好的,那么该如何正确做呢?下面是macw小编带来的详细指导,快来学习! 在接下来的文章中,笔者将展示哪些配件是可取的,哪些配件是坚…

从做产品的角度分析吕布为什么非死不可?

这是一篇小品文,作者是“产品家实战营3期”学员…… 马中赤兔,人中吕布,本意虽褒,但个人觉得将人与牲口类比,其段位貌似也没高到哪里,:) 不过说起三国里的武将武力排名,吕…

中国撸串指北:13万家烧烤店的吃货最爱

戳蓝字“CSDN云计算”关注我们哦! 数据分析:还是更爱火锅的朱小五 内容撰写:最爱干豆腐卷的王小九 来源|凹凸数读 对美食最大的肯定无疑就是那操着不同口音说出的“好吃!”二字。 ——《人生一串》豆瓣短评 以美食慰藉夜归人&…

Github上这几个沙雕项目,够我玩三天!

点击上方“码农突围”,马上关注 这里是码农充电第一站,回复“666”,获取一份专属大礼包 真爱,请设置“星标”或点个“在看” 开源最前线(ID:OpenSourceTop) 猿妹综合整理 今天,猿妹再…

几个有趣的Github项目,够你玩一阵了...

点击上方“后端技术精选”,选择“置顶公众号” 技术文章第一时间送达! 来源:开源最前线 今天,给大家整理一份有意思的沙雕项目,顺带分享了我的试用成果,说实话,这些项目够你玩三天了。 亲戚关系…

包浆网图分分钟变高清,伪影去除、细节恢复更胜前辈AI,下载可玩|腾讯ARC实验室出品...

丰色 发自 凹非寺量子位 报道 | 公众号 QbitAI 下面来欣赏一些高糊图片“整个世界都清晰了”的魔法时刻: 无论是动漫还是真实图像,是不是都清晰还原了? 以上就是由腾讯ARC实验室最新发表的图像超分辨率模型完成的。 与前人工作相比&#xff0…

爬虫入门实践 | 利用python爬取彩票中奖信息

系统环境:mac python版本:3.6.2(anaconda) 库:requests、BeautifulSoup 爬取一些简单的静态网站,一般采取的策略为:选中目标,也就是需要爬取的网站url;观察结构,查看网页结构&…

全网超详细的下载与安装VMware虚拟机以及为什么要安装VMware虚拟机

文章目录 1. 文章引言2. 下载VMware3. 安装VMware 1. 文章引言 我们使用最多的系统是windows系统,因为,国内电脑厂商的操作系统(os)基本是windows系统,比如华为、联想、华硕等电脑。 但线上的服务器大多是Linux系统,而我们经常使…

图灵奖得主LeCun:ChatGPT局限巨大,自回归模型寿命不超5年

作者 | 新智元 编辑 | 新智元 点击下方卡片,关注“自动驾驶之心”公众号 ADAS巨卷干货,即可获取 【导读】图灵奖得主Yann LeCun畅谈AI:未来是开源。 今年上半年,可谓是AI届最波澜壮阔的半年。 在急速发展的各类GPT甚至AGI的雏形背…

LeCun畅谈:ChatGPT局限巨大,自回归模型寿命不超5年

点击下方卡片,关注“CVer”公众号 AI/CV重磅干货,第一时间送达 点击进入—>【计算机视觉】微信技术交流群 转载自:新智元 | 编辑:拉燕 【导读】图灵奖得主Yann LeCun畅谈AI:未来是开源。 今年上半年,可谓…

ChatGPT正在改变一切但仍然有其局限性

人工智能聊天机器人已经被证明非常有能力完成技术任务,例如编写和编码。但它还不能做所有的事情。 自11月下旬发布以来,ChatGPT已经席卷全球。这款聊天机器人的高级人工智能能力允许它完全独立完成任务,如撰写论文、电子邮件和诗歌、编写和调…