⚙️ 第十一章 访问硬件的方法
📄 一句话概括

CPU 和设备之间靠端口交换数据,用 in 读、out 写——这是 CPU 指令集里除【搬算比跳】之外的第五类动作。

📝 本节和前面几节的关系

11.1 说了中间隔着驱动,这一节掀开驱动底下那一层:它到底在对什么说话。

一、CPU 和设备靠端口说话

第一章说 CPU 只会【搬、算、比、跳】四类动作——那是就程序的数据而言。 要和设备打交道,还得有第五类动作:对端口读写。 in——从端口读一个值进来。 out——把一个值写到端口去。 端口就是一个编号,CPU 认号不认名字,哪个号是谁,由硬件设计定死。

二、端口和内存不一样

两者都是【给个编号去读写】,但性质完全不同: 内存写的是位置(地址),用 movl 这类指令;端口写的是编号,用 in/out。 内存范围由地址线决定;端口范围固定是 0 到 65535 共 65536 个。 内存里存的是程序的数据;端口上是设备的状态与控制。 分开的好处是互不干扰——设备的东西不会跑到程序的数据里去。

三、端口还分宽度

按一次要传多少,端口读写分成三档: inb/outb——1 字节,用在状态位、控制位这种零星信息上。 inw/outw——2 字节,用在中等的配置值上。 inl/outl——4 字节,一次要传一段时用。 挑宽度的依据只有一条:一次要传多少。

四、为什么平时见不到它

【📋 实操路径】普通程序不能直接执行 in/out——得在内核或驱动里。 原因和第九章说的一样:这是一条【关口】,不能让谁都能随便操作硬件。 于是应用层看的永远是 API,in/out 被埋在深深的地下室。 驱动程序干的活儿,一大半就是替你在这些端口上来回读写。

五、动手 Demo:负责硬件输入输出的in指令和out指令

CPU 和设备之间靠端口交换数据,用 in 读、out 写。 这里用一个端口空间模拟那套动作。

ports = {}

def outb(port, val):
    ports[port] = val & 0xFF

def inb(port):
    return ports.get(port, 0xFF)

outb(0x60, 0x1E)              # 往键盘端口写一个字节
print(hex(inb(0x60)))         # 再读回来
print(hex(inb(0x61)))         # 读一个没写过的端口

实跑输出:

--------------------------------------------------------------------
负责硬件输入输出的in指令和out指令
--------------------------------------------------------------------

**CPU 和设备之间靠【端口】交换数据**,用两条专门的指令:

    in     从端口读一个值进来
    out    把一个值写到端口去

  端口和内存的区别在这儿:

                 内存               端口
  --------------------------------------------------------
  怎么指位置     写地址(movl)     写端口号(in/out)
  编号范围       地址线决定         0 ~ 65535 共 65536 个
  谁在用         程序的数据         设备的状态与控制

  【📋 实操路径】普通程序不能直接执行 in/out,**得在内核或驱动里。**
  这里用一个端口空间**模拟**那套动作:

        outb(0x60, 0x1E)    ; 往键盘端口写一个字节
        inb(0x60)           ; 再读回来

    写 0x60 之后再读回     0x1E
    读一个没写过的端口     0xFF(约定为全 1,表示【设备没准备好】)

  端口还分宽度,**按要传的数据多少挑**:

  指令          一次传     用在哪
  ------------------------------------------
  inb/outb     1 字节     状态位、控制位
  inw/outw     2 字节     中等配置值
  inl/outl     4 字节     一次要传一大段时

  **端口号记住的是【谁是谁】**——比如键盘一个、串口一个、定时器一个。
  驱动程序干的活儿,一大半就是替你在这些端口上来回读写。

实测:写 0x60 之后再读回 0x1E;没写过的端口读出 0xFF(约定为全 1,表示设备没准备好)。 端口号记住的是【谁是谁】——驱动程序一大半的活儿就是在这儿来回读写。

30 秒实验

  1. 在纸上列一列:键盘、鼠标、串口、定时器各占一个端口号,写成一张小表。
  2. 想一想:为什么设备不能像文件那样【开放给所有程序随便读写】。

常见误区

  1. 以为端口和内存是同一回事。一个写地址、一个写端口号,空间是分开的。
  2. 以为应用层可以直接执行 in/out。它被限制在内核与驱动里。
自测三问(点开对答案) 1. in 和 out 指令各做什么? —— in 从端口读一个值进来,out 把一个值写到端口去。 2. 端口和内存有什么不同? —— 内存按地址访问、存程序数据;端口按编号访问(0 到 65535)、表示设备的状态与控制。 3. 为什么普通程序不能直接执行 in/out? —— 因为它是访问硬件的关口,被限制在内核和驱动里,防止任意程序操作设备。

相关笔记