从fread 到 磁盘驱动

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


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, 余数head
	sec++;

__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表中记录的信息就必需要删除了.
代码也是现实世界的反映, 搞清了外部道理,看代码也就看懂了.

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值