Linux 0.12 内核时钟中断实现:从初始化到中断响应

时钟中断是操作系统中最基础且最重要的中断之一,它为系统提供时间基准,支持进程调度、定时器等核心功能。本文将基于 Linux 0.12 内核的 setup 程序框架,详细介绍时钟中断的完整实现,包括 8253 定时器初始化、中断向量绑定及中断处理程序编写,确保代码可直接用于实验验证。

一、时钟中断实现基础

1. 硬件基础

  • 8253 可编程定时器:产生周期性时钟信号,默认频率 18.2Hz(约 55ms 一次中断)
  • 8259A 中断控制器:时钟中断默认映射到 IRQ0,对应中断向量 0x20
  • 中断描述符表(IDT):需在向量 0x20 处注册时钟中断处理程序

2. 开发环境与工具链

沿用 Linux 0.12 开发环境,核心工具包括:

  • as 2.34:汇编器(支持 AT&T 语法)
  • ld 2.34:链接器(生成二进制镜像)
  • qemu-system-i386:模拟器(验证中断响应)

二、完整代码实现

以下是基于 setup 程序扩展的时钟中断实现代码,包含定时器初始化、中断处理程序及 IDT 配置:

asm

/* setup.s —— 扩展时钟中断支持(2048字节) */
.code16
.text
.global _start_setup/* 段地址定义 */
INITSEG  = 0x9000      /* 硬件信息存储段 */
SETUPSEG = 0x9020      /* setup程序段地址 */
IDT_BASE  = 0x0000      /* 中断描述符表基地址 */
IDT_LIMIT = 0x7FFF      /* IDT长度(8192字节) */_start_setup:/* 初始化段寄存器 */movw    %cs, %axmovw    %ax, %dsmovw    %ax, %es/* 收集硬件信息(光标、内存等) */movb    $0x03, %alxor     %bh, %bhint     $0x10movw    %dx, (0)              /* 存储光标位置到INITSEG:0000 */movb    $0x88, %ahint     $0x15movw    %ax, (2)              /* 存储内存大小到INITSEG:0002 *//* 显示启动信息 */movw    $setup_msg, %axmovw    %ax, %bpmovw    $0x1301, %ax          /* BIOS 10h/13h:显示字符串 */movw    $0x000C, %bx          /* 亮红色文字 */movw    $16, %cx              /* 字符串长度 */movb    $3, %dh               /* 行3 */movb    $0, %dl               /* 列0 */int     $0x10/* 初始化8259A中断控制器(允许IRQ0时钟中断) */call    init_8259A/* 初始化8253定时器(产生时钟中断) */call    init_8253/* 准备进入保护模式 */cli                           /* 关闭中断 */movw    $0x0000, %axcld                           /* 清除方向标志 */
do_move:movw    %ax, %esaddw    $0x1000, %axcmpw    $0x9000, %axjz      end_movemovw    %ax, %dsxorw    %di, %dixorw    %si, %simovw    $0x8000, %cx          /* 复制64KB数据 */repmovswjmp     do_move
end_move:/* 加载GDT并切换到保护模式 */movw    $SETUPSEG, %axmovw    %ax, %dslgdt    gdt_48                /* 加载全局描述符表 *//* 加载IDT(包含时钟中断描述符) */call    setup_idt             /* 初始化中断描述符表 */lidt    idt_48                /* 加载IDT寄存器 *//* 切换到保护模式 */movl    %cr0, %eaxorl     $1, %eaxmovl    %eax, %cr0.byte   0x66, 0xea            /* 远跳转到32位代码 */.long   protected_mode.word   0x0008                /* 代码段选择子 *//* 32位保护模式代码 */
.code32
protected_mode:/* 初始化数据段寄存器 */movl    $0x10, %eaxmovw    %ax, %dsmovw    %ax, %esmovw    %ax, %fsmovw    %ax, %gsmovw    %ax, %ssmovl    $0x90000, %esp        /* 设置栈指针 *//* 开启中断 */sti/* 显示时钟中断就绪标志 */movl    $0xb8000 + 2*80, %edi  /* 第2行起始位置 */movb    $'C', %al             /* 'C'表示时钟就绪 */movb    $0x0A, %ah            /* 绿底黑字 */movw    %ax, (%edi)loop:jmp     loop                  /* 等待时钟中断 *//* 初始化8259A中断控制器:允许IRQ0时钟中断 */
init_8259A:/* 主8259A初始化 */movb    $0x11, %al            /* ICW1:边沿触发,多片 */outb    %al, $0x20.word   0x00eb, 0x00eb        /* 短延迟 */movb    $0x20, %al            /* ICW2:IRQ0映射到向量0x20 */outb    %al, $0x21.word   0x00eb, 0x00ebmovb    $0x04, %al            /* ICW3:主片级联 */outb    %al, $0x21.word   0x00eb, 0x00ebmovb    $0x01, %al            /* ICW4:8086模式 */outb    %al, $0x21.word   0x00eb, 0x00ebmovb    $0xFE, %al            /* OCW1:仅允许IRQ0(时钟)中断 */outb    %al, $0x21ret/* 初始化8253定时器:产生18.2Hz时钟信号 */
init_8253:movb    $0x36, %al            /* 控制字:计数器0,模式3,二进制 */outb    %al, $0x43.word   0x00eb, 0x00ebmovb    $0x00, %al            /* 计数器0低8位(初值0xFFFF) */outb    %al, $0x40.word   0x00eb, 0x00ebmovb    $0xFF, %al            /* 计数器0高8位 */outb    %al, $0x40.word   0x00eb, 0x00ebret/* 初始化IDT:注册时钟中断处理程序(向量0x20) */
setup_idt:leal    idt, %edi             /* EDI = IDT基地址 */movl    $256, %ecx            /* 初始化256个中断描述符 */movl    $ignore_int, %edx     /* 默认处理程序地址 */movl    $0x00080000, %eax     /* 高16位=0,低16位=处理程序偏移 */movw    %dx, %ax              /* AX = 处理程序偏移低16位 */movw    $0x8E00, %dx          /* 中断门属性(P=1,DPL=0,32位) */rp_idt:movl    %eax, (%edi)          /* 偏移低32位 */movl    %edx, 4(%edi)         /* 选择子+属性 */addl    $8, %edi              /* 下一个描述符 */decl    %ecxjne     rp_idt/* 单独设置时钟中断描述符(向量0x20) */leal    0x20*8(%edi - 256*8), %edi  /* 定位到向量0x20 */leal    clock_int, %edx       /* 时钟处理程序地址 */movw    %dx, %ax              /* 更新偏移低16位 */movl    %eax, (%edi)movl    $0x8E00 + 0x0008, 4(%edi)  /* 选择子=0x08(内核代码段) */ret/* 时钟中断处理程序 */
clock_int:pushal                       /* 保存所有通用寄存器 *//* 更新屏幕显示(第3行显示中断计数) */movl    $0xb8000 + 3*80*2, %edi  /* 显示位置:第3行第0列 */incl    (%edi)                /* 计数+1(初始值0) */movb    $0x0C, %ah            /* 红底黑字 */movb    (%edi), %al           /* 计数数值 */addb    $'0', %al             /* 转换为ASCII */movw    %ax, (%edi)/* 发送EOI信号给8259A */movb    $0x20, %aloutb    %al, $0x20            /* 主控制器EOI */popal                        /* 恢复寄存器 */iret                         /* 中断返回 *//* 默认中断处理程序 */
ignore_int:pushalmovl    $0xb8000 + 4*80*2, %edi  /* 第4行显示错误 */movb    $'!', %almovb    $0x0F, %ah            /* 白字黑底 */movw    %ax, (%edi)movb    $0x20, %aloutb    %al, $0x20popaliret/* 全局描述符表(GDT) */
gdt:.word   0, 0, 0, 0            /* 空描述符 */.word   0x07ff, 0x0000, 0x9A00, 0x00C0  /* 代码段:0-32MB */.word   0x07ff, 0x0000, 0x9200, 0x00C0  /* 数据段:0-32MB */.word   0xffff, 0x8000, 0x920b, 0x00C0  /* 视频段:0xB8000 */gdt_48:.word   0x800                 /* GDT长度 */.word   512 + gdt, 0x9        /* GDT基地址(0x9xxxx) *//* 中断描述符表(IDT) */
idt:.fill   256, 8, 0             /* 256个中断描述符 */idt_48:.word   IDT_LIMIT             /* IDT长度 */.word   IDT_BASE + idt, 0x0   /* IDT基地址 *//* 字符串与填充 */
setup_msg:.ascii  "setup is running".fill   2048 - (.-_start_setup), 1, 0  /* 填充到2048字节 */

三、编译与实验验证

1. 编译命令

bash

# 汇编生成目标文件
as -32 -o setup.o setup.s# 链接生成2048字节二进制
ld -m elf_i386 -Ttext 0x0 -s --oformat binary -e _start_setup -o setup setup.o# 验证文件大小
ls -l setup | awk '{print $5 " 字节(预期2048字节)"}'

2. 制作镜像与运行

bash

# 拼接引导扇区和setup程序(假设引导扇区为bootsect)
cat bootsect setup > linux.img# 使用QEMU运行
qemu-system-i386 -fda linux.img -boot a -vga std -no-reboot

3. 预期实验现象

  • QEMU 窗口第 2 行显示 C(时钟就绪标志)
  • 第 3 行字符随时间递增(每 55ms+1),表明时钟中断正常响应
  • 无其他错误字符(如第 4 行无 !),说明中断向量配置正确

四、关键代码解析

1. 8253 定时器初始化

asm

movb $0x36, %al    ; 控制字:计数器0,模式3(方波)
outb %al, $0x43
movb $0x00, %al    ; 初值低8位(0xFFFF)
outb %al, $0x40
movb $0xFF, %al    ; 初值高8位
outb %al, $0x40

  • 定时器 0 工作在模式 3(方波输出),初值 0xFFFF,产生约 18.2Hz 的周期性中断

2. 时钟中断向量绑定

asm

leal 0x20*8(%edi), %edi  ; 定位到IDT的0x20号向量
leal clock_int, %edx     ; 绑定时钟处理程序
movw %dx, %ax            ; 存储处理程序偏移
movl %eax, (%edi)
movl $0x8E00 + 0x08, 4(%edi)  ; 内核代码段选择子(0x08)

  • 中断门属性 0x8E00 表示 32 位中断门,特权级 0
  • 选择子 0x08 对应 GDT 中的内核代码段

3. 中断处理程序

asm

clock_int:pushal                  ; 保存寄存器incl    (%edi)          ; 更新计数movb    $0x20, %aloutb    %al, $0x20      ; 发送EOIpopaliret                    ; 中断返回

  • 必须发送 EOI 信号(0x20),否则 8259A 会屏蔽后续中断
  • iret 指令自动恢复 CS、EIP、EFLAGS 寄存器

五、常见问题解决

  1. 时钟中断无响应

    • 检查 8259A 初始化:movb $0xFE, %al 确保仅开启 IRQ0
    • 验证 IDT 加载:lidt idt_48 指令是否正确执行
    • 确认 GDT 代码段选择子:中断门选择子必须为内核代码段(0x08)
  2. 中断后系统崩溃

    • 检查堆栈设置:保护模式下 %esp 需指向有效内存(如 0x90000)
    • 确保 pushal 与 popal 配对,避免寄存器状态混乱
  3. QEMU 显示异常

    • 验证 VGA 内存地址:文本模式内存基地址为 0xB8000
    • 检查字符 ASCII 转换:计数需加 '0' 才能正确显示数字

总结

本文实现了 Linux 0.12 内核时钟中断的完整流程,从 8253 定时器初始化到 IDT 向量绑定,再到中断处理程序编写,所有代码严格遵循 AT&T 语法及 as 汇编器规范。通过 QEMU 运行可观察到周期性的中断计数更新,直观验证时钟中断的响应机制。这一实现为后续进程调度、时间管理等内核功能奠定了基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/919089.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/919089.shtml
英文地址,请注明出处:http://en.pswp.cn/news/919089.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Effective C++ 条款45:运用成员函数模板接受所有兼容类型

Effective C 条款45:运用成员函数模板接受所有兼容类型核心思想:使用成员函数模板(member function templates)生成可接受兼容类型的函数,特别是泛型拷贝构造函数和赋值操作符,同时避免抑制编译器生成的默认…

华测科技(北京)的3D GPR数据分析

很高兴得到了张总的支持,获得了他们雷达的数据,并写了雷达数据读取和转换文件。1 背景搜索后发现 华测科技(北京)有限公司 的实力很强,因为他们的检测可达100km/h的时速。以前我只知道行业内 青岛中电众益 的3D GPR产…

X86、ARM与C86架构全面对比分析:性能、功耗、成本与生态系统

目录标题X86、ARM与C86架构全面对比分析:性能、功耗、成本与生态系统一、架构概述与发展背景1.1 X86架构:PC与服务器市场的传统霸主1.2 ARM架构:移动领域的王者与新兴服务器力量1.3 C86架构:国产x86兼容的创新尝试二、性能表现对比…

w嵌入式分享合集66

自己的原文哦~ https://blog.51cto.com/whaosoft/14132240 一、STM32的NRST管脚异常复位问题 这个问题是客户对开发的平台做EMS 浪涌测试的时候发生的,平台上使用了一个STM32G474 RCT6 MCU 。在某个等级的EMS 测试中, 客户发现MCU有时候会异常…

ZKmall开源商城的数据校验之道:用规范守护业务基石

在电商系统里,数据就像流淌的血液 —— 用户填的手机号、下单的商品数量、支付的金额,每一个数字、每一段文字都得靠谱。要是数据出了错,轻则订单下不了,重则钱货两空。ZKmall 开源商城作为一个分布式电商系统,每天要处…

QML实现数据可视化

界面样式 项目开发流程 1.通过QtCreator创建一个Qt Quick插件,插件命名为CarPanMod; 2.通过QtCreator创建一个Qt Quick Application,命名为QmlPro; 3.在插件CarPanMod中实现条形图,折线图和饼状图的绘制; 4.在应用程序QmlPro中,添加插件的导入路径; 5.在应用程序中,通过i…

实时计算 记录

《大数据架构师》海量实时广告流平台架构设计与实践 《架构师必备技能之集群资源评估.pdf》 参考: 大型广告系统架构与实现 架构图

gitee_流水线搭配 Dockerfile 部署vue项目

使用 gitee流水线搭配docker,编写Dockerfile文件进行自动部署Vue项目 gitee流水线 基本配置跟另外一篇文章中类似 gitee_配置自动部署vue项目-CSDN博客 需要修改的只是脚本执行 # 构建阶段脚本echo 清理旧文件 rm -rf dist echo 配置 Git 参数 git config --global http.pos…

Win10快速安装.NET3.5

按Windows键输入CONTROL打开“控制面板”点击“程序”点击“启用或关闭Windows功能”勾选“.NET Framework3.5(包括.NET2.0和3.0)”点击确定随后选择从更新下载(具体提示忘记了),之后windows会自动安装

Docker Compose 入门教程

一、Docker Compose 简介 Docker Compose 是 Docker 官方提供的多容器编排工具,通过 YAML 文件(docker-compose.yml)定义应用程序的服务、网络和卷,实现一键式容器管理。其核心优势包括: 简化多容器管理:通…

Tomcat架构深度解析:从Server到Servlet的全流程揭秘

第一章:Tomcat架构概述1.1 Tomcat的角色与定位:Web服务器 vs Servlet容器Tomcat 是什么?它既是一种轻量级 Web 服务器,也是一种符合 Java EE 规范的 Servlet 容器。Web服务器:类似 Nginx、Apache HTTP Server&#xff…

【Java web】HTTP 协议详解

一、什么是 HTTP?—— 互联网的 "快递员"你有没有想过,当你在浏览器输入www.baidu.com并按下回车时,背后发生了什么?为什么几秒钟后就能看到百度首页?这一切的背后,都离不开一个叫HTTP的 "快…

流式数据服务端怎么传给前端,前端怎么接收?

01 引言 大模型时代,尤其会话模型为了提高用户的使用体验,它不会将所有的数据加载完成一次响应给客户端,而是通过数据流,一点点的将数据慢慢呈现出来。 正是这种有趣的交互方式一次次将SSE(Server Sent Event&#x…

ML307C 4G通信板:工业级DTU固件,多协议支持,智能配置管理

产品概述 ML307C 4G通信板是一款基于中移物联网ML307C模组的工业级DTU(数据传输单元)产品,专为工业物联网应用设计。我们的固件支持多种工业协议,具备远程配置、FOTA升级、数据加密等企业级功能,为您的工业设备提供稳定…

Sublime配置verilog开发环境-具备语法高亮、代码补全、自定义代码段及语法检查等功能,提升FPGA开发效率!

对于在学习FPGA开发之前使用过其他集成开发工具如VS、pycharm、keil或编辑工具如Sublime、VScode、Notepad的朋友,在使用Vivado时可能会像博主一样感觉自带编辑器用起来不太舒服,比如不支持语法高亮显示,不支持代码自动补全等功能。因次&…

18_基于深度学习的烟雾检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)

目录 项目介绍🎯 功能展示🌟 一、环境安装🎆 环境配置说明📘 安装指南说明🎥 环境安装教学视频 🌟 二、数据集介绍🌟 三、系统环境(框架/依赖库)说明🧱 系统环…

【计算机网络架构】混合型架构简介

引言在当今数字化浪潮席卷全球的背景下,网络技术正以前所未有的速度迅猛发展,各种网络架构如雨后春笋般涌现。从早期简单的总线型、星型架构,到后来的环型、树型架构,再到如今复杂的网状型、云计算架构等,每一种架构都…

Hexo 双分支部署指南:从原理到 Netlify 实战

Hexo 双分支部署指南:从原理到 Netlify 实战 在 Hexo 博客部署中,很多人会困惑于hexo d自动部署与 GitHub 手动提交的区别,以及如何通过双分支结构优雅地部署到 Netlify。本文将清晰拆解两种部署方式的核心差异,并手把手教你用双分…

【数据结构】深入理解单链表与通讯录项目实现

文章目录一、单链表的概念及结构1.1 什么是单链表?1.2 节点的组成1.3 单链表的特点二、单链表的实现2.1 类型定义2.2 基础工具函数1. 链表打印函数2. 节点创建函数2.3 单链表的核心操作(1)插入操作1. 尾插(SLTPushBack&#xff09…

《Python学习之字典(一):基础操作与核心用法》

坚持用 清晰易懂的图解 代码语言,让每个知识点变得简单! 🚀呆头个人主页详情 🌱 呆头个人Gitee代码仓库 📌 呆头详细专栏系列 座右铭: “不患无位,患所以立。” Python学习之字典(…