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;//该bio结构所要传输的第一个(512字节)扇区:磁盘的位置

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)    接收到中断后,软件设置命令寄存器的開始/结束位,然后先后读控制器状态、驱动状态,进而确定是否传送成功。

代码例如以下:

时间: 2024-12-31 21:16:30

DMA过程分析的相关文章

arm-linux kernel启动过程分析(1)-start_kernel之前第一步

前段时间移植uboot仔细研究过uboot启动过程,最近耐不住寂寞,又想对kernel下手. Uboot启动过程分析博文连接如下: http://blog.csdn.net/skyflying2012/article/details/25804209 kernel启动过程一般不需要我们修改,研究这个对于编写driver也没有多大帮助,但对了解整个linux架构,各种机制还是非常有用. 如一句心灵鸡汤所说,只有了解一个人如何成长,才能看清他是什么样的人(语文不好,大体意思如此..) 只有知道ker

kobox : dma_s3c.ko -v1 操作寄存器方式操作S3C2440的DMA

平台:TQ2440 linux版本:Linux EmbedSky 3.16.1-svn57 #56 Sat Oct 18 21:46:22 PDT 2014 armv4tl GNU/Linux 目标:v2中改成s3c2410_dma_xxx方式来操作DMA,看这里的寄存器映射是怎么使用系统接口来操作的! #include "dma.h" #define MEM_CPY_NO_DMA 0 #define MEM_CPY_DMA 1 //#define BUF_SIZE (512*1024

OVS处理upcall过程分析

处理upcall的整体框架是: 1.由函数handle_upcalls()批量处理(in batches)的是由内核传上来的dpif_upcalls,会解析出upcall的类型.这里主要看在内核中匹配流表失败的MISS_UPCALL.处理完成后会得到多个flow_miss. 结构体dpif_upcall代表的是由内核传到用户空间的一个包,包括上传原因,packet data,以及以netlink attr形式存在的键值. struct dpif_upcall { /* All types. */

S5PV210-kernel-内核启动过程分析

1.1.内核启动过程分析前的准备 1.拿到一个内核源码时,先目录下的无用文件删除 2.建立SI工程 3.makefile (1)makefile中不详细的去分析,几个关键的地方,makefile开始部分是kernel的版本号,这个版本号比较重要,因为在模块化驱动安装时会需要用到,要注意会查,会改,版本号在makefile中,改直接改的就行 (2)kernel顶层的makefile中定义的两个变量很重要,一个是ARCH,一个CROSS,ARCH表示我们当前的配置编译路径,如果我们的ARCH =AR

国内某公有云 linux云主机开机初始化过程分析和他的镜像制作过程

最近学习了国内某公有云的linux云主机启动之后,在镜像内部的初始化过程,分享出来,仅供参看. 一.开机过程 可以看到开机时候按照数字顺序执行了一连串的脚本,其中也提示的该公有云厂商的名字的ucloud,最后一条显示做了清理工作.进系统一看 果然找不到这些脚本了. 二.进单用户模式找出这些脚本 想让开机的时候不让最后一步 999-clwanup.sh执行的办法很多,我采取的的办法是单用户模式,简单上个图,具体方法大家谷歌下. 成功进入单用户模式,并复制他的初始化脚本 三 初始化过程分析 (一)

Https协议:SSL建立过程分析(也比较清楚,而且有OpenSSL的代码)

web访问的两种方式: http协议,我们一般情况下是通过它访问web,因为它不要求太多的安全机制,使用起来也简单,很多web站点也只支持这种方式下的访问. https协议(Hypertext Transfer Protocol over Secure Socket Layer),对于安全性要求比较高的情况,可以通过它访问web,比如工商银行https://www.icbc.com.cn/icbc/(当然也可以通过http协议访问,只是没那么安全了).其安全基础是SSL协议. SSL协议,当前版

NIOS2随笔——DMA(1)

1. NIOS2 DMA控制器结构框图 与其它IP外设一样,DMA控制器也是通过AVALON MM总线,实现寄存器配置,数据读写功能. 2. NIOS2 DMA三种传输方式 3. NIOS2 DMA API函数 NIOS2 DMA的API函数原型都定义在alt_dma.h头文件中,常用的API函数如下: alt_dma_txchan alt_dma_txchan_open (const char* name); static ALT_INLINE int alt_dma_txchan_send 

ActivityManagerService启动过程分析

ActivityManagerService启动过程 一 从Systemserver到AMS zygote-> systemserver:java入层口: /** * The main entry point from zygote. */ public static void main(String[] args) { new SystemServer().run(); } 接下来继续看SystemServer run函数执行过程: private void run() { // 准备Syst

Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析

http://blog.csdn.net/luoshengyang/article/details/8223770 在前文中,我们分析了Android应用程序窗口的运行上下文环境的创建过程.由此可知,每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一个具体的应用程序窗口.由此又可知,Activity只不过是一个高度抽象的UI组件,它的具体UI实现其实是由其它的一系列对象来实现的.在本文中,我们就将详细分析Android应用程序窗口