操作系统DMA概念问题
本帖最后由 Max472 于 2021-9-10 21:52 编辑这里说的主存是什么?内存条?还是硬盘?往内存条写也要进行I/O操作吗? 本帖最后由 人造人 于 2021-9-10 23:21 编辑
主存指的是内存
先看一个例子
现在有 2 个程序
程序 1 读取硬盘中的 100 个扇区数据,然后计算平均数
程序 2 计算 从 1 到 1000000 的所有质数和
如果不使用 dma,那就是先读取硬盘的扇区 0,然后读取硬盘的扇区 1,然后读取扇区 2,。。。
就是这样
for(size_t i = 0; i < 100; ++i) {
read_sector(&buff, i); // 读取硬盘数据,读取结果存储到 buff 中
}
然后进行计算平均数
然后程序 1 结束
开始运行程序 2,计算 从 1 到 1000000 的所有质数和
如果使用 dma,那就是这样
先执行程序 1
程序 1 这样写
dma_set_address(buff); // 告诉 dma 把数据读取到 buff 中
dma_set_start_sector(0); // 告诉 dma 从扇区 0 开始读取
dma_set_data_length(100); // 告诉 dma 读取 100 个扇区的数据
dma_start(); // 开始读取,上面 3 行只是设置 dma 的参数,这一行直接导致 dma 开始工作
// 上面 4 行代码就是告诉 dma 读取硬盘,从扇区 0 开始读取,读取 100 个扇区,把读取的数据放在 buff 中
然后程序 1,执行一个 wait();
这个函数会挂起程序 1,直到 dma 完成任务
现在程序 1 暂停执行,启动程序 2,计算 从 1 到 1000000 的所有质数和
程序 2 可能计算到 80% 了,就快计算完成了
但是 dma 完成任务了,这时候程序 2 暂停,去执行程序 1,就是程序 1 中的那个 wait 函数返回了,然后执行 wait 函数下面的计算平均数的代码
完成后,程序 1 退出
然后返回到程序 2 继续执行剩下的 20% 的计算任务
第二个方案,因为使用了 dma,在读取硬盘数据的时候 cpu 去执行另一个程序了(程序 2)
而方案一中,程序 1 是一直循环读取硬盘的,读取硬盘是一个非常慢的过程,cpu 一直在等非常慢的硬盘,不能去做其他事(不能去执行程序 2)
肯定方案二更好,因为 cpu 不会去等非常慢的硬盘
其实 dma 也是这样工作的
先读取硬盘的扇区 0,然后读取硬盘的扇区 1,然后读取扇区 2,。。。
只不过是和 cpu 同时工作
dma 做 dma 的事情,读取硬盘的扇区 0,然后读取硬盘的扇区 1,然后读取扇区 2,。。。
cpu 执行 cpu 的程序,(程序 2)
两个同时工作,谁也不打扰谁
下面是程序 1 的两个版本的伪代码
void read_sector(char buff[], size_t index) {
disk_set_sector(i); // 告诉硬盘读取第 i 个扇区
while(disk_status() == 0);// 等待硬盘把数据准备好
disk_read(buff); // 从硬盘读取数据
}
void task1(void) {
char buff; // 一个扇区 512 个字节,一共有 100 个扇区
for(size_t i = 0; i < 100; ++i) {
read_sector(&buff, i); // 读取硬盘数据,读取结果存储到 buff 中
}
// 下面计算 100 个扇区数据的平均数
// ...
}
void task1_dma(void) {
char buff;
dma_set_address(buff); // 告诉 dma 把数据读取到 buff 中
dma_set_start_sector(0); // 告诉 dma 从扇区 0 开始读取
dma_set_data_length(100); // 告诉 dma 读取 100 个扇区的数据
dma_start(); // 开始读取,上面 3 行只是设置 dma 的参数,这一行直接导致 dma 开始工作
wait(); // 等待 dma 完成任务
// 下面计算 100 个扇区数据的平均数
// ...
}
其实学习这些东西之前,应该先学习汇编语言
如果你没有用汇编语言读取过硬盘数据,真的很难理解这些概念
再举一个例子
一个公司有两个人
一个领导(cpu)
一个员工(dma)
有两个任务,写文案和跑业务
有两种方案
一、
领导先出去跑业务,跑完业务回来再写文案
那个员工就在公司坐着看,然后大喊 加油,^_^
二、
领导安排员工去跑业务,然后自己在公司写文案
过了很长时间,领导的文案写的也差不多了,员工也回来了
员工拿出一份文件递给领导说,这是这一次的数据
领导说,嗯,我一会处理这个,现在没你什么事了,一边凉快去,之后有事的时候再找你
然后领导简单的看了看那份数据,他决定先写完当前这个文案,然后再去处理那份数据
cpu 完全可以选择先把程序 2 剩下的 20% 算完,然后再去处理程序 1 剩下的部分
往内存条写也要进行I/O操作吗?
cpu 操作内存中的数据不需要进行 I/O 操作
cpu 自己读取硬盘,需要通过 I/O 端口告诉硬盘一些参数
通过 dma 读取
cpu 需要通过 I/O 端口告诉 dma 一些参数,dma 要通过 I/O 端口告诉硬盘一些参数
我想 dma 应该也是通过 I/O 端口和硬盘打交道的,这个我也不清楚
人造人 发表于 2021-9-10 23:13
主存指的是内存
先看一个例子
不采用dma的时候cpu在读取硬盘扇区的时候,是通过中断方式进行I/O操作的吧
如果这样,在读取的时候,由于进程的并发性,cpu不应该在程序1进行I/O操作的时候切换到程序2进行计算吗
程序1读完1个扇区进行中断返回,cpu切换回程序1再进行读取扇区2
是这样吗?{:10_277:} Max472 发表于 2021-9-11 14:30
不采用dma的时候cpu在读取硬盘扇区的时候,是通过中断方式进行I/O操作的吧
如果这样,在读取的时候,由 ...
嗯,程序 1 也可以这么写,但是还是没有程序 2 效率高
程序 1 的 cpu 会被打扰 100 次
而程序 2 只被打扰 1 次
人造人 发表于 2021-9-11 15:02
嗯,程序 1 也可以这么写,但是还是没有程序 2 效率高
程序 1 的 cpu 会被打扰 100 次
而程序 2 只被打 ...
{:10_256:} Max472 发表于 2021-9-11 18:02
不对,就算是使用中断的方式读取硬盘数据
也是在硬盘准备数据的这段时间可以切换到其他程序去执行
等硬盘准备好了,处理器需要切回来,从硬盘的 io 端口两个字节两个字节的读取 256 次数据到内存
然后再切去别的程序,等下一个中断到来了,还要切回来,循环 256 次读取硬盘 io 端口
就是说,如果不使用 dma,处理器就需要从硬盘 io 端口,两个字节两个字节的读取数据到内存
这几天我找了好多相关的资料,自己也写程序验证了
之前回复你的,有一些是错误的,^_^
还有,书上,百度上说的,PC/AT 机器,dma 通道只有一个 SDLC 通信适配器(通道1) 和 一个 软盘驱动器(通道2),没有硬盘的
我在 bochs 上找到的是软盘的和 sb16(Sound Blaster 16)的,没有硬盘的
我在百度上找到了一个使用 PCI-IDE控制器 的
使用 dma 要枚举 pci 配置空间
或者改用 软盘
两个的代码量都有点多,而且考虑到要用汇编语言,我实在写不下去了,^_^
未来有机会的话,可以考虑用 C语言
学习这些东西之前要先学汇编语言,学了汇编语言你就可以使用汇编语言验证你的猜想
.code16
.globl_start
_start:
ljmpw $0x07c0, $1f
1:xorw %ax, %ax
movw %ax, %ss
movw $0x7c00, %sp
callw interrupt_init
callw register_disk_interrupt
callw enable_disk_interrupt
# 全局变量初始化
# flag = 0;
movb $0, %cs:0 # 就把它放在这里,这不会有任何问题
# 读取从扇区 0 开始的 8 个扇区到 ds:si 指定的位置
movw $0x07e0, %ax
movw %ax, %ds
xorw %si, %si
movw $8, %cx
xorw %bx, %bx
callw read_sect
movw $0xb800, %ax
movw %ax, %ds
movb o', 0x06e0
movb k', 0x06e2
movb $0x04, 0x06e1
movb $0x04, 0x06e3
cli
hlt
3:jmp 3b
# void read_sect(uint8_t *buff, size_t start_sect, size_t count);
# buff -> ds:si
# start_sect -> bx
# count -> cx
read_sect:
pushw %ax
pushw %cx
pushw %dx
pushw %si
pushw %bx
# 读取 count 个扇区
movb %cl, %al
movw $0x01f2, %dx
outb %al, (%dx)
# 发送逻辑扇区号
movb %bl, %al
movw $0x01f3, %dx
outb %al, (%dx)
movb %bh, %al
movw $0x01f4, %dx
outb %al, (%dx)
movb $0, %al
movw $0x01f5, %dx
outb %al, (%dx)
movb $0xe0, %al # 主盘,LBA 模式
movw $0x01f6, %dx
outb %al, (%dx)
# 发送读命令
movb $0x20, %al
movw $0x01f7, %dx
outb %al, (%dx)
movw %cx, %bx
# 等待硬盘准备好
1:movb %cs:0, %al
cmpb $1, %al
jne 1b
movb $0, %cs:0
# 从硬盘读取数据
xorw %cx, %cx
movw $0x01f0, %dx
2:inw (%dx), %ax
movw %ax, (%si)
addw $2, %si
incw %cx
cmpw $256, %cx # 512 字节,256 个字
jb 2b
decw %bx
cmpw $0, %bx
jne 1b
popw %bx
popw %si
popw %dx
popw %cx
popw %ax
retw
# void interrupt_init(void);
interrupt_init:
cli
pushw %ax
pushw %cx
pushw %dx
pushw %si
pushw %ds
# ICW1: 边沿触发/级联方式
movb $0x11, %al
movw $0x20, %dx
outb %al, (%dx)
# ICW2: 起始中断向量
movb $0x20, %al
movw $0x21, %dx
outb %al, (%dx)
# ICW3: 从片级联到IR2
movb $0x04, %al
movw $0x21, %dx
outb %al, (%dx)
# ICW4: 非总线缓存/特殊全嵌套/非自动结束
movb $0x11, %al
movw $0x21, %dx
outb %al, (%dx)
# ICW1: 边沿触发/级联方式
movb $0x11, %al
movw $0xa0, %dx
outb %al, (%dx)
# ICW2: 起始中断向量
movb $0x28, %al
movw $0xa1, %dx
outb %al, (%dx)
# ICW3: 从片级联到IR2
movb $0x02, %al
movw $0xa1, %dx
outb %al, (%dx)
# ICW4: 非总线缓存/普通全嵌套/非自动结束
movb $0x01, %al
movw $0xa1, %dx
outb %al, (%dx)
# 设置默认中断
xorw %cx, %cx
movw %cx, %ds
movw $0x20 * 4, %si
movw $ignore_interrupt, %ax
movw %cs, %dx
1:movw %ax, (%si)
movw %dx, 2(%si)
addw $4, %si
incw %cx
cmpw $16, %cx
jb 1b
popw %ds
popw %si
popw %dx
popw %cx
popw %ax
sti
retw
# void ignore_interrupt(void);
ignore_interrupt:
pushw %ax
pushw %dx
# 发送 OCW2
movb $0x20, %al
movw $0xa0, %dx
outb %al, (%dx)
movb $0x20, %al
movw $0x20, %dx
outb %al, (%dx)
popw %dx
popw %ax
iretw
# void register_disk_interrupt(void);
register_disk_interrupt:
cli
pushw %ax
pushw %dx
pushw %ds
xorw %ax, %ax
movw %ax, %ds
movw $disk_interrupt, %ax
movw %cs, %dx
# IRQ14
movw %ax, 0xb8
movw %dx, 0xba
popw %ds
popw %dx
popw %ax
sti
retw
# void disk_interrupt(void);
disk_interrupt:
pushw %ax
pushw %dx
# 读取硬盘状态
movw $0x01f7, %dx
inb (%dx), %al
andb $0x88, %al
cmpb $0x08, %al
jne 1f
# 硬盘数据准备好了
# flag = 1;
movb $1, %cs:0
1:
# 发送 OCW2
movb $0x20, %al
movw $0xa0, %dx
outb %al, (%dx)
movb $0x20, %al
movw $0x20, %dx
outb %al, (%dx)
popw %dx
popw %ax
iretw
# void enable_disk_interrupt(void);
enable_disk_interrupt:
pushw %ax
pushw %dx
movb $0x00, %al
movw $0x03f6, %dx
outb %al, (%dx)
popw %dx
popw %ax
retw
人造人 发表于 2021-9-13 18:00
不对,就算是使用中断的方式读取硬盘数据
也是在硬盘准备数据的这段时间可以切换到其他程序去执行
等硬 ...
{:10_291:}
页:
[1]