DMA过程分析
1.1
当我们在应用程序中编写write系统调用,向磁盘中写入数据时,写入请求会先调用底层写函数,将请求先写入内存中的页快速缓存(page cache)中,写入成功则立马返回,真正的写入磁盘操作会延迟运行。Page cache是硬盘在内存中的一个缓存,是linux内核所使用的主要磁盘快速缓存,在绝大多数情况下,内核在读写磁盘时都引用page cache(极少数应用会绕过页快速缓存,如数据库软件)。
当把page cache中的一页数据写到块设备之前,内核首先检查相应的页是否已经在快速缓存中,假设不在,就要先在当中添加一个新项,并用要写到磁盘中的数据填充该项。I/O数据的传送并非立即開始,而是要延迟几秒后才对磁盘进行更新,从而使进程有机会对要写入磁盘的数据做进一步的改动(也就是内核进行延迟写操作)。
当内核以文件系统、虚拟内存子系统或者系统调用的形式决定从块I/O设备输入、输出块数据时,它将再结合一个bio结构,用来描写叙述这个操作。该结构被传递给 I/O代码,代码会把它合并到一个已经存在的request结构中,或者依据须要,再创建一个新的request结构。bio结构包括了驱动程序运行请求的所有信息,而不必与初始化这个请求的用户空间的进程相关联。
内核中块设备的I/O操作基本容器由bio结构体表示,定义在<linux/bio.h>中,该结构体代表了正在现场的(活动的)以片段(segment)链表形式组织的块I/O操作。一个片段是一小块连续的内存缓冲区。这种优点就是不须要保证单个缓冲区一定要连续。所以通过片段来描写叙述缓冲区,即使一个缓冲区分散在内存的多个位置上,bio结构体也 能对内核保证I/O操作的运行,这种就叫做聚散I/O(scatter/gather).
bio为通用层的主要数据结构,既描写叙述了磁盘的位置,又描写叙述了内存的位置,是上层内核vfs与下层驱动的连接纽带。
struct bio {
sector_t bi_sector;字节)扇区:磁盘的位置
struct bio *bi_next; //请求链表
struct block_device *bi_bdev;//相关的块设备
unsigned long bi_flags//状态和命令标志
unsigned long bi_rw; //读写
unsigned short bi_vcnt;//bio_vesc偏移的个数
unsigned short bi_idx; //bi_io_vec的当前索引
unsigned short bi_phys_segments;//结合后的片段数目
unsigned short bi_hw_segments;//重映射后的片段数目
unsigned int bi_size; //I/O计数
unsigned int bi_hw_front_size;//第一个可合并的段大小;
unsigned int bi_hw_back_size;//最后一个可合并的段大小
unsigned int bi_max_vecs; //bio_vecs数目上限
struct bio_vec *bi_io_vec; //bio_vec链表:内存的位置
bio_end_io_t *bi_end_io;//I/O完毕方法
atomic_t bi_cnt; //使用计数
void *bi_private; //拥有者的私有方法
bio_destructor_t *bi_destructor; //销毁方法
};
文件系统须要写到硬盘的数据保存在page cache里面,那么这个过程又是怎么和dma建立关系的呢?
DMA写磁盘过程概述:
若硬盘支持DMA,而且在操作系统中打开了DMA,则每次读写磁盘,都会涉及到DMA操作。尽管文件系统对硬盘的I/O请求不是连续的,数据所在的物理内存页也是不连续的,可是操作系统会将这些不连续的内存页组合到一起,再启用DMA操作(启用DMA的过程开销较大,须要设置一系列寄存器),那么这些数据就行一次传输完毕,这样也就能高效的数据传输。内核中有个物理设备描写叙述符表(physical region descriptor table,PRDT),要进行数据的传输必需将对应的物理页以及物理页内数据长度填充到PRDT里面。,PRDT结构例如以下:
Figure 1说明:
每一个PRDT大小为8字节,0-3字节说明物理页的内存地址,4-5字节说明内存区域的数量,以字节为单位,全零表示64K大小。最后一个字节的最后一位表示PRDT表的结束。
scsi层的scsi_init_io函数把bio封装,然后将其映射给DMA的scatterlist结构体,该结构体即PRDT中的一项,(内核中dma_desc_array相应PRDT),用来指向每一个内存块。剩余工作就是设置DMA寄存器,然后发送,我们后面将具体分析该部分代码。
下面是write系统调用内核态处理函数的路径:
经过一系列处理,write系统调用处理结束后,若须要写磁盘数据终于会经过下面路径:
scsi_scan_target(scsi扫面函数)——》__scsi_scan_target ——》scsi_sequential_lun_scan ——》scsi_probe_and_add_lun ——>scsi_alloc_sdev ——》scsi_alloc_queue(scsi分配队列),从这里分开,一条路径是设置DMA并发送命令到DMA控制器(路径一),还有一条是初始化函数路径(路径二)。
路径一:scsi_request_fn——>scsi_dispatch_cmd——》scsi_log_send——》(.queuecommand =ata_scsi_queuecmd,)ata_scsi_queuecmd——》__ata_scsi_queuecmd——》ata_scsi_translate——》ata_qc_issue——》ata_bmdma_qc_issue——》(bfin_bmdma_setup:设置DMA寄存器/ bfin_bmdma_start:開始DMA)
路径二:scsi_prep_fn——>scsi_setup_blk_pc_cmnd ——》scsi_init_io ——》scsi_init_sgtable ——》blk_rq_map_sg(该函数的參数request这个结构体封装了bio结构体).
下面主要分析bfin_bmdma_setup和bfin_bmdma_start函数,即DMA操作过程:
(1) 软件准备好一个PRD Table放在内存中,每一个8字节,对齐到4字节边界。
(2) 软件把PRD table的起始地址设置好,同一时候通过设置读/写控制位设置数据和传输方向,清除状态寄存器中的中断位和错误位。
(3) 软件发出DMA传送指令到disk设备。
(4) 向总线控制器IDE命令寄存器的相应通道中写入1,使能总线控制器。
(5) DMA从IDE设备中请求控制器传送数据到/从内存中
(6) 传送结束,IDE设备发出中断
(7) 接收到中断后,软件设置命令寄存器的開始/结束位,然后先后读控制器状态、驱动状态,进而确定是否传送成功。
代码例如以下:
DMA过程分析的更多相关文章
- STM32之ADC实例(基于DMA方式)
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zouleideboke/article/details/75112224 ADC简介: ADC(An ...
- u-boot-2016.09 make编译过程分析(二)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/guyongqiangx/article/ ...
- u-boot-2016.09 make编译过程分析(一)
https://blog.csdn.net/guyongqiangx/article/details/52565493 综述 u-boot自v2014.10版本开始引入KBuild系统,Makefil ...
- STM32基于HAL库通过DMA读写SDIO
通过STM32CUBEMX生成DMA读写sdio的工程,再读写过程中总会卡死在DMA中断等待读写完成的while中,最终发现while等待的标志在SDIO的中断里置位的,而SDIO中断优先级如果小于或 ...
- freeswitch注册过程分析
操作系统:debian8.5_x64freeswitch 版本 : 1.6.8 本文仅描述sip注册的简单场景,即话机直接向处于同一个局域网的fs进行注册. SIP协议的消息结构 消息框架 SIP协议 ...
- GDB调试汇编堆栈过程分析
GDB调试汇编堆栈过程分析 分析过程 这是我的C源文件:click here 使用gcc - g example.c -o example -m32指令在64位的机器上产生32位汇编,然后使用gdb ...
- 20145212——GDB调试汇编堆栈过程分析
GDB调试汇编堆栈过程分析 测试代码 #include <stdio.h> short val = 1; int vv = 2; int g(int xxx) { return xxx + ...
- z-stack协议uart分析(DMA)
1.从ZMain里面的main函数开始分析 2.进入int main( void ); HalDriverInit(); //硬件相关初始化,有DMA初始化和UART初始化 3.进入HalDriv ...
- 20145223《信息安全系统设计基础》 GDB调试汇编堆栈过程分析
20145223<信息安全系统设计基础> GDB调试汇编堆栈过程分析 分析的c语言源码 生成汇编代码--命令:gcc -g example.c -o example -m32 进入gdb调 ...
随机推荐
- windows安装Apache,注册服务出现“(OS 5)拒绝访问。 : AH00369: Failed to open the WinNT service manager..."错误
原文:http://blog.csdn.net/jaray/article/details/9950211 在安装Apache的时候,我下载的是zip格式,不是msi安装版,需要自己注册服务,才能在桌 ...
- getComputedStyle与currentStyle
currentStyle:获取计算后的样式.也叫当前样式.终于样式. 长处:能够获取元素的终于样式.包含浏览器的默认值,而不像style仅仅能获取行间样式.所以更经常使用到. 注意:不能获取复合样式如 ...
- 《Javascript权威指南》学习笔记之十二:数组、多维数组和符合数组(哈希映射)
Array(数组)是JavaScript中较为复杂的数据类型,同Java.C#.VB等程序语言的数组相比.Javascript数组中的元素不必为同样的数据类型,能够在数组每一个元素上混合使用数字.日期 ...
- BZOJ 2750: [HAOI2012]Road( 最短路 )
对于每个点都跑最短路, 然后我们得到了个DAG, 在这DAG上更新每条边的答案. 考虑e(u, v)∈DAG对答案的贡献: 假设从S到u得路径数为A[u], 从v出发到达任意点的路径数为B[v], ...
- DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)
函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...
- 语法糖(Syntactic sugar)
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添 ...
- 双绞线的制作,T568A线序,T568B线序
双绞线的制作 1.1 实验目的 双绞线是组建局域网时常常使用的通信传输介质,通过本实验,让学生学会制作双绞线. 1.2 实验任务 (1)了解双绞线的特性及屏蔽与非屏蔽双绞线的区别. (2)了解EIA/ ...
- 华为手机logcat不出日志解决方案
解决方法:在拨打电话界面,录入*#*#2846579#*#* 自动进入开发界面菜单,进入第一个,选择开启logcat.
- EasyUI - Menu 菜单
效果: html代码: <div id="mm" class="easyui-menu"> <div id =">New< ...
- net core与golang web
Asp.net core与golang web简单对比测试 最近因为工作需要接触了go语言,又恰好asp.net core发布RC2,就想简单做个对比测试. 下面是测试环境: CPU:E3-1230 ...