CPU如何感知我们的IO设备呢?
首先要明确一点,CPU所能做的,只是从寄存器中读一个值,或写一个值。我们的设备上通常会有一些芯片,甚至是一个微型的计算机系统,来感知物理状态和读写数据,并且向设备寄存器中读写值,设备向CPU提供的,只是一些设备寄存器接口,CPU通过读写这些寄存器来和IO设备交互。
这些设备寄存器主要分为三类:State(设备状态),Command(指令)和Date(数据)。
看一个真实的例子
static void
readsect(void *dst, uint offset)
{
// Issue command.
waitdisk();
outb(0x1F2, 1); // count = 1
outb(0x1F3, offset);
outb(0x1F4, offset >> 8);
outb(0x1F5, offset >> 16);
outb(0x1F6, (offset >> 24) | 0xE0);
outb(0x1F7, 0x20); // cmd 0x20 - read sectors// Read data.
waitdisk();
insl(0x1F0, dst, SECTSIZE/4);
}
static inline void //outb函数
outb(ushort port, uchar data)
{
asm volatile("out %0,%1" : : "a" (data), "d" (port));
}
static inline void //insl函数
insl(int port, void *addr, int cnt)
{
asm volatile("cld; rep insl" :
"=D" (addr), "=c" (cnt) :
"d" (port), "0" (addr), "1" (cnt) :
"memory", "cc");
}
这段代码从一个磁盘扇区读数据,并将数据放到内存地址 dst的位置。我们的汇编指令集提供了专门向IO寄存器读写的指令,out和in.例如上面两个函数中的out和insl(in指令变体)
outb函数向指定端口(寄存器)写一个值(data),接受两个参数,port(端口号,可以理解为寄存器地址),和data.
insl函数从指定端口读值,copy到内存地址addr的位置。指令前缀rep表示重复读cnt次。
在readsect里,
我们先等待磁盘就位waitdisk()
然后向0x1F2寄存器写值1,表示读一个扇区
后面四行指定了读取扇区的地址
outb(0x1F7, 0x20)向0x1F7端口写0x20,表示读取命令
waitdisk()
insl(0x1F0, dst, SECTSIZE/4);从0x1F0端口读值,指令重复次数为SECTSIZE/4。
由此可以看到,cpu就是通过几组设备寄存器与IO设备交互。