Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🐧 Linux Kernel Comments
    • Chapter 2 - 微型计算机组成结构

      • Chapter 2 - 微型计算机组成结构
    • Chapter 3 - 内核编程语言和环境

      • Chapter 3 - 内核编程语言和环境
    • Chapter 4 - 80X86 保护模式及其编程

      • Chapter 4.1-4.2 - 80X86 系统寄存器和系统指令 & 保护模式内存管理
      • Chapter 4.3 - 分段机制
      • Chapter 4.4 - 分页机制
      • Chapter 4.5 - 保护
      • Chapter 4.6 - 中断和异常处理
      • Chapter 4.7 - 任务管理
      • Chapter 4.8 - 保护模式编程初始化
      • Chapter 4.9 - 一个简单的多任务内核实例
    • Chapter 5 - Linux 内核体系结构

      • Chapter 5.1-5.2 - Linux 内核模式 & 体系结构
      • Chapter 5.3 - Linux 内核对内存的管理和使用
      • Chapter 5.4-5.6 - 中断机制 & 系统调用 & 系统时间和定时
      • Chapter 5.7-5.9 - Linux 进程控制 & 堆栈使用 & 文件系统
    • Chapter 6 - 引导启动程序 (boot)

      • Chapter 6 - 引导启动程序 (boot)
    • Chapter 7 - 初始化程序 (init)

      • Chapter 7 - 初始化程序 (init)
    • Chapter 8 - 内核代码

      • Chapter 8.1 - 内核代码总体功能
      • Chapter 8.2 - asm.s 程序
      • Chapter 8.3 - traps.c 程序
      • Chapter 8.4 - sys_call.s 程序
      • Chapter 8.5 - mktime.c 程序
      • Chapter 8.6 - sched.c 程序
      • Chapter 8.7 - signal.c 程序
      • Chapter 8.8 - exit.c 程序
      • Chapter 8.9 - fork.c 程序
      • Chapter 8.10 - sys.c 程序
    • Chapter 9 - 块设备驱动程序

      • Chapter 9.1 - 块设备驱动程序 总体功能
      • Chapter 9.2 - blk.h 文件
      • Chapter 9.3 - hd.c 程序
      • Chapter 9.4 - ll_rw_blk.c 程序
      • Chapter 9.5 - ramdisk.c 程序
    • Chapter 10 - 字符设备驱动程序

      • Chapter 10.1 - 字符设备驱动程序 总体功能
      • Chapter 10.2 - keyboard.S 程序
      • Chapter 10.3 - console.c 程序
      • Chapter 10.4 - serial.c 程序
      • Chapter 10.5 - rs_io.s 程序
      • Chapter 10.6 - tty_io.c 程序
      • Chapter 10.7 - tty_ioctl.c 程序
    • Chapter 12 - 文件系统

      • Chapter 12.1 - 文件系统 总体功能
      • Chapter 12.2 - buffer.c 程序
      • Chapter 12.3 - bitmap.c 程序
      • Chapter 12.4 - truncate.c 程序
      • Chapter 12.5 - inode.c 程序
      • Chapter 12.6 - super.c 程序
      • Chapter 12.7 - namei.c 程序
      • Chapter 12.9 - block_dev.c 程序
      • Chapter 12.10 - file_dev.c 程序
      • Chapter 12.11 - pipe.c 程序
      • Chapter 12.12 - char_dev.c 程序
      • Chapter 12.13 - read_write.c 程序
      • Chapter 12.14 - open.c 程序
      • Chapter 12.15 - exec.c 程序
      • Chapter 12.16 - stat.c 程序
      • Chapter 12.17 - fcntl.c 程序
      • Chapter 12.18 - ioctl.c 程序
      • Chapter 12.19 - select.c 程序

Chapter 10.5 - rs_io.s 程序

Created by : Mr Dk.

2019 / 08 / 26 19:15

Ningbo, Zhejiang, China


10.5 rs_io.s 程序

10.5.1 功能描述

实现 RS-232 串行通信的中断处理过程。主要对终端设备的读、写缓冲队列进行操作:

  • 把从串行线路上收到的字符存入串行终端的读缓冲队列
  • 或把写缓冲队列中需要发送的字符通过串行线路发送给远端的串行终端设备

引起串行中断的原因有四种:

  1. modem 状态发生变化
  2. 线路状态发生变化
  3. 接收到字符
  4. 允许发送保持寄存器已空中断,需要发送字符

前两种情况,只需要读取对应的状态寄存器,即可使其复位。对于接收到字符的情况,将字符放入读缓冲队列中,并调用行规则函数处理为字符行放入辅助队列中;对于需要发送字符的情况,程序首先从写缓冲队列中取出字符发送,并判断队列是否为空:

  • 若不为空,则循环发送过程
  • 若为空,则禁止发送保持寄存器为空产生的中断

10.5.2 代码注释

几个常量的定义

size = 1024 # 读写缓冲队列的长度

# 读写缓冲队列结构体 tty_queue 的偏移量
rs_addr = 0 # 串行端口号字段
head = 4 # 缓冲区头指针字段
tail = 8 # 缓冲区尾指针字段
proc_list = 12 # 等待缓冲队列的进程
buf = 16 # 缓冲区

# 写缓冲队列满后,内核会把要往队列中写字符的进程置为等待状态
# 当写缓冲队列剩余最多 256 个字符时
# 中断处理程序可以唤醒等待缓冲队列的进程
startup = 256

串口中断处理程序

两个串行端口入口点不同,但共用处理逻辑:

.align 2
_rs1_interrupt:
    pushl $_table_list+8 # 串口 1 读写缓冲队列指针地址
    jmp rs_int
.align 2
_rs2_interrupt:
    pushl $_table_list+16 # 串口 2 读写缓冲队列指针地址

rs_int:
    pushl %edx
    pushl %ecx
    pushl %ebx
    pushl %eax
    push %es
    push %ds
    pushl $0x10 # ds 指向内核数据段
    pop %ds
    pushl $0x10 # es 指向内核数据段
    pop %es
    movl 24(%esp), %edx # 取中断入口压入堆栈的读写缓冲队列结构体的地址
    movl (%edx), %edx # 取 tty_queue 结构体的地址
    movl rs_addr(%edx), %edx # 取串口的端口基地址
    addl $2,%edx # 指向串口的中断标识寄存器
rep_int:
    xorl %eax, %eax
    inb %dx, %al # 取中断标识字节,判断中断来源
    testb $1,%al # 首先判断有无待处理中断
    jne end # 若无待处理中断,则跳转至退出处理处
    cmpb $6,%al # ??
    ja end # ?
    movl 24(%esp), %ecx # 缓冲队列指针地址
    pushl %edx # 临时保存中断标识寄存器端口地址
    subl $2, %edx # edx 中恢复串口基地址值
    call jmp_table(, %eax, 2) # 根据中断来源作相应处理
    popl %edx # 恢复中断标识寄存器端口地址
    jmp rep_int
end:
    movb $0x20, %al # 中断退出,向中断控制器发送结束中断指令 EOI
    outb %al, $0x20
    pop %ds
    pop %es
    popl %eax
    popl %ebx
    popl %ecx
    popl %edx
    addl $4, %esp # 丢弃中断入口入栈的队列指针地址
    iret

中断类型处理程序表

jmp_table:
    .long modem_status, write_char, read_char, line_status

对于产生中断的四种类型,作分别的处理

modem 状态变化中断
.align 2
modem_status:
    addl $6, %edx
    inb %dx, %al # 读 modem 状态寄存器,复位
    ret
线路状态中断
.align 2
line_status:
    addl $5, %edx
    inb %dx, %al # 读线路状态寄存器,复位
    ret
读字符中断
.align 2
read_char:
    inb %dx, %al # 读取接收缓冲寄存器中的字符
    movl %ecx, %edx # 当前串口的缓冲队列指针地址
    subl $_table_list, %edx # 缓冲队列指针表首地址
    shrl $3, %edx # 串口号 1/2
    movl (%ecx), %ecx # read_queue
    movl head(%ecx), %ebx # 取读队列的缓冲区头指针
    movb %al, buf(%ecx, %ebx) # 将字符放在头指针所处位置
    incl %ebx # 头指针前移
    andl $size-1, %ebx # 头指针 % 缓冲区长度
    cmpl tail(%ecx), %ebx # 头指针与尾指针比较
    je 1f # 缓冲区满
    movl %ebx, head(%ecx) # 保存修改过的头指针 (寄存器 → 内存)
1:
    addl $63, %edx # 串口号转换为 tty 号
    pushl %edx # tty 号作为参数入栈
    call _do_tty_interrupt
    addl $4, %esp # 丢弃入栈参数
    ret
写字符中断
.align 2
write_char:
    movl 4(%ecx), %ecx # write_queue
    movl head(%ecx), %ebx # 写队列头指针
    subl tail(%ecx), %ebx # 头指针 - 尾指针 == 队列中字符数
    andl $size-1, %ebx
    je write_buffer_empty # 写队列为空的处理
    cmpl $startup, %ebx # 队列中的字符超过 256 个
    ja 1f # 不需要处理等待进程,跳转处理尾指针
    movl proc_list(%ecx), %ebx # 正在等待写队列的进程
    testl %ebx, %ebx # 等待队列是否为空
    je 1f # 等待队列为空,跳转处理尾指针
    movl $0, (%ebx) # 唤醒进程
1:
    movl tail(%ecx), %ebx # 尾指针
    movb buf(%ecx, %ebx), %al # 从尾指针处取字符
    outb %al, %dx # 将字符写到发送保持寄存器中
    incl %ebx # 尾指针前移
    andl $size-1, %ebx # 尾指针 % 缓冲区长度
    movl %ebx, tail(%ecx) # 保存已修改过的尾指针
    cmpl head(%ecx), %ebx # 尾指针和头指针比较
    je write_buffer_empty # 相等,队列已空
    ret

.align 2
write_buffer_empty:
    movl proc_list(%ecx), %ebx # 正在等待写队列的进程
    testl %ebx, %ebx # 队列指针是否为空
    je 1f
    movl $0, (%ebx) # 唤醒进程
1:
    incl %edx # 指向中断允许寄存器
    inb %dx, %al # 读取中断允许寄存器
    jmp 1f # 延时
1:
    jmp 1f # 延时
1:
    andb $0xd, %al # 禁止发送保持寄存器已空中断
    outb %al, %dx # 写入中断允许寄存器
    ret
Edit this page on GitHub
Prev
Chapter 10.4 - serial.c 程序
Next
Chapter 10.6 - tty_io.c 程序