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调 ...
随机推荐
- Android应用开发:CardView的使用及兼容
引言 在Google I/O 2014上,Google公布了Android L Preview版本,此版本的UI有了非常大的改变,很炫很给力!同时,Google也给出了两个可以向下兼容的控件放到了V7 ...
- mysql启动的四种方式
mysql的四种启动方式: .mysqld 启动mysql服务器:./mysqld --defaults-file=/etc/my.cnf --user=root 客户端连接: mysql --def ...
- 使用linq对字符串1,2,3,4,5,6,7,8,9,10求和
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SumI ...
- cloudflare的新waf,用Lua实现的
我们使用nginx贯穿了我们的网络,做前线web服务,代理,流量过滤.在某些情况下,我们已经扩充了nginx上我们自己的模块的核心C代码,但近期我们做了一个重大举措,与nginx结合使用lua 差点儿 ...
- iOS - 单例传值 (一)
点击打开链接 iOS - 单例传值 (二) 单例只会对某个类实例化一次/单例类,对单例这个类实例化一次有且仅有一个对象 你单例初始化,只能初始化一次,然后你指向的对象,其实都是指向一个内存地址, ...
- Goodle Clean设计架构
Goodle Clean设计架构 23 * @param <P> the response type 24 */ 25 public abstract class UseCase<Q ...
- js实现图片上传预览及进度条
原文js实现图片上传预览及进度条 最近在做图片上传的时候,由于产品设计的比较fashion,上网找了比较久还没有现成的,因此自己做了一个,实现的功能如下: 1:去除浏览器<input type= ...
- 刘德华夏日Fiesta演唱会上那个表演探戈舞的演员是谁啊?_百度知道
刘德华夏日Fiesta演唱会上那个表演探戈舞的演员是谁啊?_百度知道 刘德华夏日Fiesta演唱会上那个表演探戈舞的演员是谁啊? 2008-05-28 00:04 topofhill | ...
- Determine whether an integer is a palindrome. Do this without extra space.
看到这个题目的时候,首先不认识 Determine这个单词.英文不好没办法,查了下是确认的意思,然后不懂 palindrome这个单词, 查了下是回文的意思. 问题是 回文是个什么东西,官方解释: A ...
- POJ - 1006 Biorhythms 周期相遇 两个思路程序
Description Some people believe that there are three cycles in a person's life that start the day he ...