Linux-0.11 文件系统buffer.c详解

news2024/9/21 4:20:09

Linux-0.11 文件系统buffer.c详解

buffer_init

void buffer_init(long buffer_end)

该函数的作用主要是初始化磁盘的高速缓冲区

刚开始使用h指针指向了start_buffer的位置。

struct buffer_head * h = start_buffer;
void * b;
int i;

start_buffer定义为end的位置,即内存中system模块的结束的位置。

struct buffer_head * start_buffer = (struct buffer_head *) &end;

经过这个步骤之后h实际上指向了内核高速缓冲区的低地址。

接下来使用了b指针指向了内核高速缓冲区的高地址。

if (buffer_end == 1<<20)
  b = (void *) (640*1024);
else
  b = (void *) buffer_end;

这里根据buffer_end的值的不同,决定了b的指向。

在main.c文件中给定了buffer_end的大小定义:

  • 内存大小>12Mb,buffer_end=4Mb
  • 6Mb<内存大小<=12Mb,buffer_end=2Mb
  • 如果内存大小<=6Mb,buffer_end=1Mb

知道了buffer_end的值,那么很容易通过条件语句得到b的值。

那么为什么要对buffer_end的值进行讨论呢?

这主要是因为物理内存中640K-1M的区域内存放了显存和BIOS ROM,因此当buffer_end=1M, 高速缓冲区是一块, 如果buffer_end>1M, 那么高速缓冲区是两块, 这个点通过下面这张图可以清晰的了解到。

在这里插入图片描述

经过上述步骤。 h指针(头指针)指向了高速缓冲区的起点, b指针(数据块指针)指向了高速缓冲区的终点。

buffer_init接下来要做的就是对这些高速缓冲区进行初始化。

while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
  h->b_dev = 0;
  h->b_dirt = 0;
  h->b_count = 0;
  h->b_lock = 0;
  h->b_uptodate = 0;
  h->b_wait = NULL;
  h->b_next = NULL;
  h->b_prev = NULL;
  h->b_data = (char *) b;
  h->b_prev_free = h-1;
  h->b_next_free = h+1;
  h++;
  NR_BUFFERS++;
  if (b == (void *) 0x100000)
    b = (void *) 0xA0000;
}

让每一个buffer_header节点使用b_data指针指向一个数据块block(1k)。然后h指针加1,b指针减1。如此往复,直到h和b指针指向的区别相交。

这些buffer_header用一个双向链表进行串联。

需要注意的是, 当内存大于6Mb时, 高速缓冲区有两块, 当b指针在移动时,如果移动到了1Mb的地址时,也就是到了显存和BIOS ROM的高地址边界时, 需要跳过它,直接来到640Kb的地址。也就是下面这两行代码。

if (b == (void *) 0x100000)
  b = (void *) 0xA0000;

h指针和b指针移动进行初始化的效果如下图所示:

在这里插入图片描述

最后这段代码, 将free_list指向了第一个buffer_header块。然后让首尾两个buffer_header相接, 形成完整的双向链表。 最后的话,将高速哈希表中的每一行初始化为NULL。

free_list = start_buffer;
free_list->b_prev_free = h;
h->b_next_free = free_list;
for (i=0;i<NR_HASH;i++)
  hash_table[i]=NULL;

这里的free_list翻译为自由链表, 实际意思就是所有的高速缓冲区构成的双向链表, 在下面的函数中的将经常出现。

find_buffer

static struct buffer_head * find_buffer(int dev, int block)

该函数的作用是从哈希链表中按照设备号和逻辑块号去查询对应的缓冲区块。

首先根据设备号和块号查找到哈希数组的下标,找到下标对应的bh, 遍历该bh通过b_next连接起来的链表, 看该链表中是否有匹配的。如下图所示:

在这里插入图片描述

这个过程的代码如下:

for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)//计算哈希值, 遍历该哈希桶上的链表
  if (tmp->b_dev==dev && tmp->b_blocknr==block)  //如果设备号和块号相同, 代表找到了, 返回bh块
    return tmp;
return NULL;

get_hash_table

struct buffer_head * get_hash_table(int dev, int block)

该函数的作用是从哈希链表中找到指定的bh块。其内部调用了find_buffer函数, 内部增加了如果bh块被其他进程占用情况的处理。

入参中的block指的是磁盘的盘块号

从下面的代码可以看出, 首先调用了find_buffer函数寻找执行的bh块, 如果没有找到, 就直接返回,暗示可能要再去自由链表上去搜索

for (;;) {
  if (!(bh=find_buffer(dev,block)))
    return NULL;

如果找到了bh块,就是看这个块是否有其他进程加锁,如果没有就将引用计数加1。如果有的话就等待锁的释放。注意等待过程中,该bh块可能被修改,因此wake up之后需要重新判断该块的设备号与块号是否相等。如果在等待过程中,该bh块没有被修改, 就直接返回。 如果被修改了,那么则需要重新寻找。

bh->b_count++;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_blocknr == block)
  return bh;
bh->b_count--;//该block在等待中被修改。 减少其引用计数, 进入下一次循环,重新寻找符合要求的bh块。

getblk

struct buffer_head * getblk(int dev,int block)

该函数的作用是从哈希链表自由链表两个地方寻找可用的bh块。

该函数先从哈希链表中寻找指定的bh块。如果找到了就直接返回。

if ((bh = get_hash_table(dev,block)))
  return bh;

当从哈希表中没有找到对应的bh块时, 就需要去自由链表中查找一个品质最好的块。品质是根据bh块的b_dirt属性b_lock属性去计算的。 在遍历过程中, 会查看某个块是否引用计数为0, 为0则放入备选项, 然后继续遍历, 如果后续找到了更品质的块, 就更新该备选项。遍历结束之后就找到了最佳的bh块。

do {
  if (tmp->b_count)
    continue;
  if (!bh || BADNESS(tmp)<BADNESS(bh)) {
    bh = tmp;
    if (!BADNESS(tmp))
      break;
  }
/* and repeat until we find something good */
} while ((tmp = tmp->b_next_free) != free_list);

如果这个过程, 仍然没有找到可用的bh块, 代表现在高速缓冲区很繁忙,则需要等待。

if (!bh) {
  sleep_on(&buffer_wait);
  goto repeat;
}

如果经过上述步骤找到了一个最佳品质的bh块之后,如果该块有锁,则等待锁的释放, 如果没有锁, 但是有脏数据, 就将脏数据写盘。

wait_on_buffer(bh);
if (bh->b_count)
  goto repeat;
while (bh->b_dirt) {
  sync_dev(bh->b_dev);
  wait_on_buffer(bh);
  if (bh->b_count)
    goto repeat;
}

如果该block已经被添加到哈希链表中, 则需要重新寻找。

if (find_buffer(dev,block))
  goto repeat;

到此为止,终于找到了可用的bh块,将其初始化,并且插入到哈希链表中,这里实际上实现了一个LRU缓存。有关remove_from_queuesinsert_into_queues将在对应的函数讲解中详解。

bh->b_count=1;
bh->b_dirt=0;
bh->b_uptodate=0;
remove_from_queues(bh);
bh->b_dev=dev;
bh->b_blocknr=block;
insert_into_queues(bh);

remove_from_queues

static inline void remove_from_queues(struct buffer_head * bh)

该函数的作用是将buffer_header(简称bh)从空闲链表和哈希队列中移除。

下面这段代码作用是将bh从哈希队列中移除。

if (bh->b_next)
  bh->b_next->b_prev = bh->b_prev;
if (bh->b_prev)
  bh->b_prev->b_next = bh->b_next;
if (hash(bh->b_dev,bh->b_blocknr) == bh)
  hash(bh->b_dev,bh->b_blocknr) = bh->b_next;

下面这段代码作用是将bh从自由链表中移除。

if (!(bh->b_prev_free) || !(bh->b_next_free))
  panic("Free block list corrupted");
bh->b_prev_free->b_next_free = bh->b_next_free;
bh->b_next_free->b_prev_free = bh->b_prev_free;
if (free_list == bh)
  free_list = bh->b_next_free;

insert_into_queues

static inline void insert_into_queues(struct buffer_head * bh)

该函数的作用就是将bh插入到空闲链表的尾部,并通入哈希函数插入到指定的哈希队列中。

下面这段代码的作用就是将bh插入到双向链表的尾部

bh->b_next_free = free_list;
bh->b_prev_free = free_list->b_prev_free;
free_list->b_prev_free->b_next_free = bh;
free_list->b_prev_free = bh;

下面这段代码就是将bh插入到哈希表的首部。

bh->b_prev = NULL;
bh->b_next = NULL;
if (!bh->b_dev)
  return;
bh->b_next = hash(bh->b_dev,bh->b_blocknr);
hash(bh->b_dev,bh->b_blocknr) = bh;
bh->b_next->b_prev = bh;

brelse

void brelse(struct buffer_head * buf)

该函数的作用是释放一个bh块。

将该块的引用计数减1。

if (!(buf->b_count--))
  panic("Trying to free free buffer");

bread

struct buffer_head * bread(int dev,int block)

该函数的作用是用于去指定的设备上读取相应的块

首先调用getblk在高速缓冲区中找到一个bh块, 随后调用磁盘读写函数ll_rw_block向磁盘设备发出读请求, 将磁盘内容读取到bh块中。

if (!(bh=getblk(dev,block)))
  panic("bread: getblk returned NULL\n");
if (bh->b_uptodate)
  return bh;
ll_rw_block(READ,bh);
if (bh->b_uptodate)//如果缓冲区已经更新, 则直接返回
  return bh;

bread_page

void bread_page(unsigned long address,int dev,int b[4])

该函数的作用是用于去指定的设备上读取4个逻辑块到内存中, 也就是读取4k磁盘内容一个内存页中。

该函数分为两个过程, 第一个过程是将磁盘数据块拷贝到bh块中。

for (i=0 ; i<4 ; i++)
  if (b[i]) {
    if ((bh[i] = getblk(dev,b[i])))
      if (!bh[i]->b_uptodate)
        ll_rw_block(READ,bh[i]);
  } else
    bh[i] = NULL;

第二个过程是将bh块中的内容拷贝到指定的内存中。

for (i=0 ; i<4 ; i++,address += BLOCK_SIZE)
  if (bh[i]) {
    wait_on_buffer(bh[i]);
    if (bh[i]->b_uptodate)
      COPYBLK((unsigned long) bh[i]->b_data,address);
    brelse(bh[i]);
  }

breada

struct buffer_head * breada(int dev,int first, ...)

breada函数是bread函数的拓展,如果只传递一个块号, 那么就是bread。 如果传递多个块号,就会读取多个逻辑块的值到高速缓存。

这个函数使用了可变参数列表, 但是其功能与bread类似, 不再赘述。

va_list args;
struct buffer_head * bh, *tmp;

va_start(args,first);
if (!(bh=getblk(dev,first)))
  panic("bread: getblk returned NULL\n");
if (!bh->b_uptodate)
  ll_rw_block(READ,bh);
while ((first=va_arg(args,int))>=0) {
  tmp=getblk(dev,first);
  if (tmp) {
    if (!tmp->b_uptodate)
      ll_rw_block(READA,bh);
    tmp->b_count--;
  }
}
va_end(args);
wait_on_buffer(bh);
if (bh->b_uptodate)
  return bh;
brelse(bh);
return (NULL);

wait_on_buffer

static inline void wait_on_buffer(struct buffer_head * bh)

该函数的作用是如果一个bh块被加锁, 那么将等待该bh块解锁。

sys_sync

int sys_sync(void)

该函数的作用是将所有高速缓冲bh块的脏数据写盘。

首先将inode_table表中修改的节点刷新到高速缓冲区的bh块中,

int i;
struct buffer_head * bh;

sync_inodes();		

紧接着对所有的bh块进行循环,将其中有脏数据的bh块调用ll_rw_block发送写磁盘命令,将数据写到磁盘设备中。

for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
  wait_on_buffer(bh);
  if (bh->b_dirt)
    ll_rw_block(WRITE,bh);
}

sync_dev

int sync_dev(int dev)

该函数的作用是将所有高速缓冲中某个设别的bh块的脏数据写盘。

该函数总体与sync_sys类似,只不过在其中增加了dev号的判断。

invalidate_buffer

static void inline invalidate_buffers(int dev)

该函数的作用是将所有某个设备的bh块中的b_uptodate和b_dirt置为0。

check_disk_change

void check_disk_change(int dev)

该函数的作用是检查磁盘是否已经更换。 如果已经更换, 就要对更新高速缓冲区的状态。

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

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

相关文章

数据结构与算法基础(王卓)(11):栈的定义及其基础操作(顺序表和链表的初始化、求长度,是否为空,清空和销毁、出栈、压栈)

栈的定义&#xff1a; stack&#xff1a;一堆&#xff0c;一摞;堆&#xff1b;垛; 顺序栈和链栈的设计参考&#xff1a; 数据结构与算法基础&#xff08;王卓&#xff09;&#xff08;7&#xff09;&#xff1a;小结&#xff1a;关于链表和线性表的定义及操作_宇 -Yu的博客-C…

【免费教程】 SWMM在城市水环境治理中的应用及案例分析

SWMMSWMM&#xff08;storm water management model&#xff0c;暴雨洪水管理模型&#xff09;是一个动态的降水-径流模拟模型&#xff0c;主要用于模拟城市某一单一降水事件或长期的水量和水质模拟。EPA&#xff08;Environmental Protection Agency&#xff0c;环境保护署&am…

QTCreator 设置编码格式

显示文件编码格式 选择“工具>首选项>文本编辑器>显示>显示文件编码” 全局设置 选择“工具>首选项>文本编辑器>行为>文件编码” 将文件编码设置为utf-8&#xff0c;UTF-8 BOM 选择存在则保留&#xff0c;最后选择apply。 打开项目设置 选择“项目&…

解析HTTP/2如何提升网络速度

我们知道HTTP/1.1 为网络效率做了大量的优化&#xff0c;最核心的有如下三种方式&#xff1a; 增加了持久连接&#xff1b;浏览器为每个域名最多同时维护 6 个 TCP 持久连接&#xff1b;使用 CDN 的实现域名分片机制。 虽然 HTTP/1.1 采取了很多优化资源加载速度的策略&#x…

[学习笔记]SQL server完全备份指南

方式一&#xff0c;使用SQL Server Management Studio 准备工作 连接目标数据库服务器 在目标数据库上右键->属性&#xff0c;将数据库的恢复模式设置为“简单”&#xff0c;兼容级别设置为“SQL Server 2016(130)” [可选]将表中将无用的业务数据删除&#xff0c;以减…

Java EE|TCP/IP协议栈之传输层UDP协议详解

文章目录一、对UDP协议的感性认识简介主要特点二、UDP的报文结构协议端格式概览报文结构详解源端口目的端口16位UDP报文长度16位校验和参考一、对UDP协议的感性认识 简介 UDP&#xff0c;是User Datagram Protocol的简称&#xff0c;中文名是用户数据报协议&#xff0c;是OSI…

Leetcode力扣秋招刷题路-0081

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 81. 搜索旋转排序数组 II 已知存在一个按非降序排列的整数数组 nums &#xff0c;数组中的值不必互不相同。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 &…

公安局靶场建设规划设计

随着我国国家安全形势的变化&#xff0c;公安工作也面临着越来越严峻的挑战。为了提高公安干警的专业技能和反恐能力&#xff0c;建设一座现代化的靶场已成为公安局的迫切需求。本文将介绍公安局靶场建设的重要性&#xff0c;靶场的规划与设计以及建设过程中需要注意的事项。 一…

Pyspark基础入门4_RDD转换算子

Pyspark 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark Flink Kafka Hbase Hi…

Flex写法系列-Flex布局之基本语法

以前的传统布局&#xff0c;依赖盒装模型。即 display position float 属性。但是对于比较特殊的布局就不太容易实现&#xff0c;例如&#xff1a;垂直居中。下面主要介绍flex的基本语法&#xff0c;后续还有二期介绍Flex的写法。一、什么是Flex布局&#xff1f;Flex布局个人…

Vuex的创建和简单使用

Vuex 1.简介 1.1简介 1.框框里面才是Vuex state&#xff1a;状态数据action&#xff1a;处理异步mutations&#xff1a;处理同步&#xff0c;视图可以同步进行渲染1.2项目创建 1.vue create 名称 2.运行后 3.下载vuex。采用的是基于vue2的版本。 npm install vuex3 --save 4.vu…

Frequency Domain Model Augmentation for Adversarial Attack

原文&#xff1a;[2207.05382] Frequency Domain Model Augmentation for Adversarial Attack (arxiv.org)代码&#xff1a;https://github.com/yuyang-long/SSA.黑盒攻击替代模型与受攻击模型之间的差距通常较大&#xff0c;表现为攻击性能脆弱。基于同时攻击不同模型可以提高…

C++8:模拟实现list

目录 最基础的链表结构以及迭代器实现 链表节点结构 构造函数 push_back list的迭代器 增删查改功能实现 insert erase pop_front pop_back push_front clear 默认成员函数 析构函数 拷贝构造函数 赋值操作符重载 list的完善 const迭代器 赋值操作符重…

使用BP神经网络诊断恶性乳腺癌(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 1.1.算法简介 BP&#xff08;Back Propagation&#xff09;网络是1986年由Rumelhart和McCelland为首的科学家小组提出&#xf…

c语言编程规范第三部分

3、头文件应向稳定的方向包含 头文件的包含关系是一种依赖&#xff0c;一般来说&#xff0c;应当让不稳定的模块依赖稳定的模块&#xff0c;从而当不稳定的模块发生变化时&#xff0c;不会影响&#xff08;编译&#xff09;稳定的模块。就我们的产品来说&#xff0c;依赖的方向…

数据复制 软件 SnapMirror:统一复制,更快恢复

数据复制 软件 SnapMirror&#xff1a;统一复制&#xff0c;更快恢复 预测未知领域是一项棘手的工作。让 SnapMirror 软件来处理则轻松得多。 通过数据的高可用性和快速数据复制&#xff0c;可即时访问业务关键型数据。放松一下&#xff0c;它会让你满意的。 为什么用 SnapMi…

3D目标检测(一)—— 基于Point-Based方法的PointNet系列

3D目标检测&#xff08;一&#xff09;—— PointNet&#xff0c;PointNet&#xff0c;PointNeXt&#xff0c; PointMLP 目录 3D目标检测&#xff08;一&#xff09;—— PointNet&#xff0c;PointNet&#xff0c;PointNeXt&#xff0c; PointMLP 前言 零、网络使用算法 …

AQS与Synchronized异曲同工的加锁流程

在并发多线程的情况下&#xff0c;为了保证数据安全性&#xff0c;一般我们会对数据进行加锁&#xff0c;通常使用Synchronized或者ReentrantLock同步锁。Synchronized是基于JVM实现&#xff0c;而ReentrantLock是基于Java代码层面实现的&#xff0c;底层是继承的AQS。 AQS全称…

c++函数对象(仿函数)、谓词、内建函数对象

1、函数对象 1.1 概念 重载函数调用操作符的类&#xff0c;这个类的对象就是函数对象&#xff0c;在使用这个函数对象对应使用重载的&#xff08;&#xff09;符号时&#xff0c;行为类似于函数调用&#xff0c;因此这个函数也叫仿函数。 注意&#xff1a;函数对象&#xff0…

多个任务并行的时候,你是否总是会手忙脚乱?

很多重要事情之所以变得迫在眉睫&#xff0c;需要立刻处理、应付&#xff0c;是因为被延误或没有进行足够的预防和准备&#xff0c;筹划。 面对多个任务并行的时候&#xff0c;你是否总是会手忙脚乱&#xff1f; 在项目工作中&#xff0c;管理者每天要面对各种工作&#xff…