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调 ...
随机推荐
- QSettings保存程序设置
今天看了一些QSettings的简单用法,可以用来保存程序的设置,使得程序每次启动都可以显示上次关闭时的状态.我这里实现了一个简单的文本编辑窗口,可以设置文本的字体,字体的颜色和背景色.每次关闭程序都 ...
- Linux中处理需要传输的IP报文流程
本文主要讲解了Linux中处理需要传输的IP报文流程,使用的内核的版本是2.6.32.27 为了方便理解,本文采用整体流程图加伪代码的方式对Linux中处理需要传输的IP报文流程进行了讲解,希望可以对 ...
- 利用navicat for oracle将数据库全部数据移动
话不多说.直接上图. 1.首先选择自己的数据库. 右键,data transfer 2.选择相应源数据库,目标数据库.点击start就可以.假设中间失败,可多尝试几次. 2.
- javascript操作元素的css样式
我们经常要使用Javascript来改变页面元素的样式.当中一种办法是改变页面元素的CSS类(Class),这在传统的Javascript里,我们一般是通过处理HTML Dom的classname特性 ...
- C++ 左值 右值
最近在研究C++ 左值 右值,搬运.收集了一些别人的资料,供自己记录和学习,若以后看到了更好的解释,会继续补充.(打“?”是我自己不明白的地方 ) 参考:<Boost程序库探秘——深度解析C ...
- 程序启动报错:ORA-12505;PL/SQL却可以登录的解决方法
一.异常{ ORA-12505, TNS:listener does not currently know of SID given in connect descriptor The Connect ...
- GAE+bottle+jinja2+beaker快速开发demo - Python,GAE - language - ITeye论坛
GAE+bottle+jinja2+beaker快速开发demo - Python,GAE - language - ITeye论坛 :GAE+bottle+jinja2+beaker快速开发 ...
- 函数指针&绑定: boost::functoin/std::function/bind
see link: https://isocpp.org/wiki/faq/pointers-to-members function vs template: http://stackoverflow ...
- jquery mobile自己定义webapp开发实例(一)——前言篇
用jquery mobile做了一段时间的webapp开发,准备用自己的一个小demo做一个模块化的分享 点击demo演示 手机演示二维码: 此demo已经是比較老的版本号,用户体验流畅度确实还存在非 ...
- poj-3895-Cycles of Lanes 简单DFS
题目链接: http://poj.org/problem?id=3895 题目意思: 在无向连通图中图中找一个经过边数最多的环. 解题思路: 从任意一点直接DFS,不用回溯,注意构成环的话至少有3条边 ...