从fread 到 磁盘驱动

author: hjjdebug
date: 2024年 03月 28日 星期四 16:49:14 CST
description: 从fread 到 磁盘驱动

文章目录

    • 1. linux 内核调用栈
    • 2. 读中断服务程序.
    • 3. 何时计算的柱面,磁头,扇区号? 现代磁盘还有柱面,磁头,扇区概念吗?
    • 4. 固定的dev,block, 是不是每次都能找到固定的buffer缓冲区 bh ?

fread 函数到底是怎样工作的? 看下面代码

char buf[1024];
FILE *fp=fopen("1.txt",1); // 从文件系统中找到了1.txt 所对应的磁盘位置.
int n=fread(buf,1,sizeof(buf),fp); //从磁盘中读取1024个字节到内存,返回实际读取的字节数.

fread 通过 libc 库进入内核调用, libc部分咱就不说了,过程而已,看内核调用.

1. linux 内核调用栈

// 这里向硬盘控制器芯片设置了磁道,磁头,扇区号,设置了硬盘中断服务程序回调函数,并发命令为读数据,让磁盘作出反应,
//然后一路凯歌返回
0 in hd_out of hd.c:192
1 in do_hd_request of hd.c:341 //返回
2 in add_request of ll_rw_blk.c:76 // 返回
3 in make_request of ll_rw_blk.c:143 // 返回
4 in ll_rw_block of ll_rw_blk.c:155 // 返回
// 要等待数据到达,数据更新后返回 , 当磁盘迟迟不响应时,系统就不得不在这里等待了.
// 一次只能读一块数据到磁盘缓冲块中,早期的磁盘缓冲块大小时1024bytes, 2个扇区大小.
// 这就是内存cache, 当下一次再读该块数据时,就不用读磁盘了,而是可以直接从缓存中拿数据.
//bread 把数据读取到了磁盘缓冲区,有可能需要从磁盘读,也可能不用读了(刚读过,数据还有效)
5 in bread of buffer.c:285
// 当读取数据很大时,就只能循环一次次的调用bread 函数了.
// 每次把数据读取到缓冲块,再从缓冲块中把数据copy到用户区(file_read函数)
6 in file_read of file_dev.c:27
7 in sys_read of read_write.c:77 //直接返回
8 in system_call of system_call.s:94 // 判别一下是否需要调度进程,然后返回

2. 读中断服务程序.

当磁盘把数据准备好,发磁盘中断请求, CPU 响应中断,进硬盘中断服务程序
执行设定的回调函数void read_intr(void)
从端口读取一个扇区数据
port_read(HD_DATA,CURRENT->buffer,256); //从端口读取256个word,一个扇区,当时的总线16bits!
CURRENT->buffer += 512; //缓冲指针加512,以便存下一次数据, 这个缓冲区就是bh, bread 等待的就是它.
CURRENT->sector++; // 扇区数加1
//如果请求的扇区数比较多, 会继续设定read_intr 为回调,函数返回,中断服务程序结束.
if (–CURRENT->nr_sectors) {
do_hd = &read_intr; //还有数据需要读,设置read_intr(自己) 为回调,继续读
return;
}

如果磁盘请求的扇区数较多,磁盘数据还没有读完,会再次触发硬盘中断,引起新一轮中断响应.
其实最多也就2个扇区,而且每次也必然是2个扇区,因为内核就是这么设定的. 一次一块.
众多的数据,那是靠file_read 循环来获取的.

3. 何时计算的柱面,磁头,扇区号? 现代磁盘还有柱面,磁头,扇区概念吗?

当你打开文件fopen 时, 文件系统就给你找到了inode号,它记录了文件位置(逻辑地址块号).
在bread 函数中有一个getblk 调用,根据dev(硬盘号例如0x301)和块号查找磁盘缓冲块函数
if (!(bh=getblk(dev,block))) //一定会得到一个bh,并且其bh->count>=1, >1表示不只一处使用 =1是新申请到的.
哦! buffer_head 结构中没有柱面,磁头,扇区信息.

在do_hd_request 函数中有以下代码:

	__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),"r" (hd_info[dev].sect)); //余数在edx,为sector,商在eax,继续做除法__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),"r" (hd_info[dev].head)); //block/sector/head = cylinder, 余数headsec++;

__asm__汇编语法,有2个冒号分割,第一部分是输出寄存器,第二部分为输入寄存器,第三部分(若有的话,那就需要3个冒号了)为其它改动寄存器
第一行的汇编代码的意思是输出把eax赋值给block, 把edx赋值给sec, 输入时把block送eax, 把0送edx. 把hd_info[dev].sec送寄存器
用eax/寄存器, 商在eax, 余数在edx, 注意,做完除法后eax送给了block, edx送给了sec
第二行的汇编代码. 输出eax给cyl, 余数edx给head, 输入部分block送eax,0送edx,hd_info[dev].head送寄存器
此时的block是第一步操作得到的整数,再继续除以head, 则整数是cyl,余数是head.
如果汇编代码你看懂了, 还要理解为什么要这样算, 这涉及到硬盘的构造. 对于一片双面磁盘,上下有2个磁头可以读写上面和下面.
磁盘被划分为很多同心圆叫磁道, 每个磁道又被按512bytes 划分为扇区.
硬盘就是由多个双面磁盘摞在一起组成的,这时候这些同心圆不叫磁道了,叫柱面,对吧,很形象. 多个磁盘的相同的磁道构成柱面.
心存这个硬盘结构,就理解了为什么这样就得到了柱面号,磁头号,扇区号

我想查看一下我的磁盘参数, 发现fdisk, smartctl 都不再提供柱面,磁头参数,只提供存储容量,难道是这些参数不使用了吗?
查了一下互联网:
CHS编址方式在早期的小容量盘中非常流行.
目前的大容量硬盘的设计已经有所改变,转为LBA编址方式。不再划分柱面和磁头号, 这些数据由硬盘自身保留,
而磁盘对外提供全部为线性的地址,即LBA地址。(Logical Block Address)

是啊, 柱面,磁头,扇区是磁盘厂家的事,没必要暴露给外边, 所以改良后,直接传逻辑地址, 磁盘就可以工作了.

sudo fdisk -L /dev/sda命令(输入 m 获取帮助): p
Disk /dev/sda:931.53 GiB,1000204886016 字节,1953525168 个扇区
Disk model: ST1000DM010-2EP1
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 4096 字节
I/O 大小(最小/最佳):4096 字节 / 4096 字节
磁盘标签类型:dos
磁盘标识符:0xe264da3d设备       启动      起点       末尾       扇区   大小 Id 类型
/dev/sda1            2048  838862847  838860800   400G 83 Linux
/dev/sda2       838862848 1953525167 1114662320 531.5G 83 Linux命令(输入 m 获取帮助): q

可见,现在大容量磁盘都是一次读取4K字节, 512字节已成过去式.
为啥?原来的磁盘数据总线才16bits,现在都是64bits的天下, 原来接口读一次才能读2bytes, 现在接口读一次就能读8bytes.

4. 固定的dev,block, 是不是每次都能找到固定的buffer缓冲区 bh ?

答, 不是. 磁盘buffer缓冲区被串成一个链表来管理, 空闲的缓冲区也被串成一个链表, 当(dev,block)对申请一块缓存存放数据时,
从闲置的缓冲区中拿一个最闲的就可以了. 就是拉郎配! 不过要把配对的bh挂在由(dev,block)构建的hash 表中. 这样当再次读取
(dev,block)数据时,先到hash表中看一下,有bh, 就不用到磁盘读了,直接去用就可以了.

那bh 什么时候失效呢?
嗯, 磁盘缓存是循环着使用的,当bh变成最闲的那一个,被新的(dev,block)拉郎配的时候,那旧hash表中记录的信息就必需要删除了.
代码也是现实世界的反映, 搞清了外部道理,看代码也就看懂了.

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

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

相关文章

算法笔记之蓝桥杯pat系统备考(3)

算法笔记之蓝桥杯&pat系统备考(2) 多训练、多思考、多总结٩(๑•̀ω•́๑)۶ 八、深搜和广搜 8.1DFS dfs是一种枚举完所有完整路径以遍历所有情况的搜索方法,可以理解为每次都是一条路走到黑的犟种。 以老朋友斐波那契额数列为例&a…

numpy入门及和列表的比较

创建一个np的数组。 例子&#xff1a; import numpy as np# 创建一个列表a [1,2,3,4]# 用列表创建一个numpy数组b np.array(a)print(a的类型,type(a))print(a)print(b的类型,type(b))print(b) 结果&#xff1a; a的类型 <class list> [1, 2, 3, 4] b的类型 <cl…

Docker-compose管理工具的使用

华子目录 容器编排工具docker composecompose介绍compose使用的三个步骤docker-compose.yml文件案例compose具有管理应用程序整个生命周期的命令 docker compose安装安装条件在Linux系统上安装composedocker compose卸载 docker compose运用演示修改compose配置&#xff0c;添加…

【手册】——mq延迟队列

目录 一、背景介绍二、思路&方案三、过程1.项目为啥用延迟队列&#xff1f;2.项目为啥用三方延迟队列&#xff1f;3.项目中为啥用rabbitmq延迟队列&#xff1f;4.rabbitmq延迟队列的安装5.rabbitmq的延迟队列配置方式5.1.exchange配置5.2.queues配置5.3.exchange和queues的…

初识C++ · 入门(2)

目录 1 引用 1.1引用的概念 1.2 引用的特性 2 传值&#xff0c;传引用的效率 3 引用和指针的区别 4 内联函数 4.1 内联函数的定义 4. 2 内联函数的特性 5 关键字auto 5.1关于命名的思考 5.2 关于auto的发展 5.3 auto使用规则 6 范围for的使用 7 空指针 1 引用 …

web渗透测试漏洞复现:docker API未授权漏洞复现并getshell

web渗透测试漏洞复现 1. docker API未授权漏洞复现1.1 dokcer的概念1.2 dokcer API的概念1.3 dokcer API未授权漏洞浮现1.3.1 验证是否存在漏洞1.3.2 未授权复现步骤1.3.2 未授权操作容器1.4 扩展方法--docker未授权getshell1.4.1 获取shell的方法一:反弹shell1.4.2 挂载宿主机…

win10如何开启麦克风权限,win10麦克风权限设置

手机下载软件后,总是会跳出各种权限需要,例如访问通讯录、读取位置信息、启动相机等等。电脑上的应用也有这些权限设置,比如说玩游戏、直播、或录制视频时,我们需要打开麦克风权限,否则无法进行交流和录音。但是,win10如何开启麦克风权限呢?针对这个问题,小编已整理了两…

构建现代Web应用:JavaScript与Node.js的完美搭档

文章目录 JavaScript基本解释补充前端开发定义DOM树和JavaScript的角色浏览器引擎的进化动态内容更新SPA Node.js JavaScript 基本解释 javaScript的出现使得前端变的强大了很多。前端开发指的是&#xff0c;写一些代码&#xff0c;这些代码最终可以转化为浏览器可以懂的代码…

《自动机理论、语言和计算导论》阅读笔记:p115-p138

《自动机理论、语言和计算导论》学习第 6 天&#xff0c;p115-p138 总结&#xff0c;总计 24 页。 一、技术总结 1.associativity and comutativity (1)commutativity(交换性): Commutativity is the property of an operator that says we can switch the order of its ope…

python flask生成被控服务端 开放接口 可以调用本地程序启动D盘的app.py文件,并生成一个前端文件,有一个启动按钮

要创建一个使用 Flask 的被控服务端&#xff0c;开放接口以调用本地程序并启动 D 盘的 app.py 文件&#xff0c;以及生成一个带有启动按钮的前端文件&#xff0c;你需要做以下几个步骤&#xff1a; 设置 Flask 服务端&#xff1a;创建一个 Flask 应用&#xff0c;并定义一个 A…

大型C++代码一些英文缩写理解

1.Cfg"通常是指"Configuration"&#xff0c;即"配置"的意思 2.“info"通常是指"information”&#xff0c;也就是"信息"的意思 3.DLG通常表示"Dialog"&#xff0c;即对话框的意思 4.opt表示选择 5"Img"可能表…

比KMP简单的Manacher

P3805 【模板】manacher - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) “没时间悼念KMP了&#xff0c;接下来上场的是Manacher&#xff01;” 什么是Manacher? 历史背景&#xff1a; 1975 年&#xff0c;一个叫 Manacher 的人发明了这个算法&#xff0c;所以叫Manacher 算…

Kratos 基础学习记录

一、安装golang环境 golang的安装和系统变量配置的教程有很多&#xff0c;简单列举几个教程: windows: 超详细Go语言环境安装(有图详解)_go环境安装-CSDN博客 linux: 【Linux — 安装 Go】Linux 系统安装 Go 过程总结_linux 安装go-CSDN博客 mac: mac安装Golang开发环境…

财务管理系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 目录 1. …

Python类的构造方法 __init__及super().__init__

__init__ 是 Python 中特殊的方法&#xff0c;__init__ 构造方法在对象被创建时自动调用&#xff0c;用于在创建类的实例时进行初始化新创建的对象的状态。在这个方法内部&#xff0c;可以设置对象的初始属性&#xff0c;或者执行其他必要的初始化操作。 class ClassName:def …

02-JDK新特性-接口新特性

接口新特性 接口组成更新概述 接口的组成 常量 public static final String ZERO "0";抽象方法 public abstract void dance();默认方法&#xff08;JAVA8新增&#xff09; public default void dance(){}静态方法&#xff08;JAVA8新增&#xff09; public stat…

leecode 331 |验证二叉树的前序序列化 | gdb 调试找bug

计算的本质是数据的计算 数据的计算需要采用格式化的存储&#xff0c; 规则的数据结果&#xff0c;可以快速的按照指定要求存储数据 这里就不得不说二叉树了&#xff0c;二叉树应用场景真的很多 本题讲的是&#xff0c;验证二叉树的前序序列化 换言之&#xff0c;不采用建立树的…

Logback日志框架常见配置

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl logback简介 Logback是一个高性能、功能强大的日志框架&#xff0c;专为Java应用程序设计。它由Log4j的创始人Ceki Glc创建&#xff0c;并被视为Log4j的继承者和改进版。Lo…

3D Web轻量化平台HOOPS Web Platform在大型水利工程中的应用和价值

随着科技的不断进步和互联网的普及&#xff0c;数字化技术在各个领域的应用日益广泛&#xff0c;大型水利工程也不例外。作为一种先进的3D Web轻量化引擎&#xff0c;HOOPS Web Platform在大型水利工程中扮演着重要的角色&#xff0c;为工程设计、施工管理、运行维护等方面提供…

Ubuntu部署BOA服务器

BOA服务器概述 BOA是一款非常小巧的Web服务器&#xff0c;源代码开放、性能优秀、支持CGI通用网关接口技术&#xff0c;特别适合用在嵌入式系统中。 BOA服务器主要功能是在互联嵌入式设备之间进行信息交互&#xff0c;达到通用网络对嵌入式设备进行监控&#xff0c;并将反馈信…