当前位置: 首页 > news >正文

抄写Linux源码(Day17:你的键盘是什么时候生效的?)

回忆我们需要做的事情:
为了支持 shell 程序的执行,我们需要提供:
1.缺页中断(不理解为什么要这个东西,只是闪客说需要,后边再说)
2.硬盘驱动、文件系统 (shell程序一开始是存放在磁盘里的,所以需要这两个东西)
3.fork,execve, wait 这三个系统调用,也可以说是 进程调度 (否则无法 halt shell 程序并且启动另外的程序)
4.键盘驱动、VGA/console/uart 驱动、中断处理 (支持键盘输入和屏幕显示)
5.内存管理 (shell 启动其它进程时,不能共用内存,而是切换其它进程的页表) — 完成内核内存管理
6.为了写代码方便,我们需要从 MBR 进入到 main 函数,这也是从 汇编 切换到 C 语言 — 已经完成
7.应用程序申请内存的接口

  1. 现在已经进入 main 函数了
  2. 在内核中实现了初步的内核进程管理 (kfree kalloc)

读闪客文章 “你的键盘是什么时候生效的?”

https://mp.weixin.qq.com/s?__biz=Mzk0MjE3NDE0Ng==&mid=2247500119&idx=1&sn=f46331f70677aba168243040a96be1c0&chksm=c2c5bbfaf5b232ec6ed2187d0a8ae3a6001ab5997b9ee838f3b32aa892d9a41157473e90195f&scene=178&cur_album_id=2123743679373688834#rd

那我们今天就来刨根问底一下,到底过了多久之后,按下键盘才有效果呢?

当然首先你得知道,按下键盘后会触发中断,CPU 收到你的键盘中断后,根据中断号,寻找由操作系统写好的键盘中断处理程序。

中断的原理和过程不了解的,可以看我的文章,认认真真的聊聊中断

这个中断处理程序会把你的键盘码放入一个队列中,由相应的用户程序或内核程序读取,并显示在控制台,或者其他用途,这就代表你的键盘生效了。

不过放宽心,我们不展开讲这个中断处理程序以及用户程序读取键盘码后的处理细节,我们把关注点放在,究竟是“什么时候”,按下键盘才会有这个效果。

我们以 Linux 0.11 源码为例,发现进入内核的 main 函数后不久,有这样一行代码。

void main(void) {...trap_init();...
}

看到这个方法的全部代码后,你可能会会心一笑,也可能一脸懵逼。

void trap_init(void) {int i;set_trap_gate(0,&divide_error);set_trap_gate(1,&debug);set_trap_gate(2,&nmi);set_system_gate(3,&int3);   /* int3-5 can be called from all */set_system_gate(4,&overflow);set_system_gate(5,&bounds);set_trap_gate(6,&invalid_op);set_trap_gate(7,&device_not_available);set_trap_gate(8,&double_fault);set_trap_gate(9,&coprocessor_segment_overrun);set_trap_gate(10,&invalid_TSS);set_trap_gate(11,&segment_not_present);set_trap_gate(12,&stack_segment);set_trap_gate(13,&general_protection);set_trap_gate(14,&page_fault);set_trap_gate(15,&reserved);set_trap_gate(16,&coprocessor_error);for (i=17;i<48;i++)set_trap_gate(i,&reserved);set_trap_gate(45,&irq13);set_trap_gate(39,&parallel_interrupt);
}

这啥玩意?这么多 set_xxx_gate。

有密集恐惧症的话,绝对看不下去这个代码,所以我就给他简化一下。

把相同功能的去掉。

void trap_init(void) {int i;// set 了一堆 trap_gateset_trap_gate(0, &divide_error);... // 又 set 了一堆 system_gateset_system_gate(45, &bounds);...// 又又批量 set 了一堆 trap_gatefor (i=17;i<48;i++)set_trap_gate(i, &reserved);...
}

这就简单多了,我们一块一块看。

首先我们看 set_trap_gate 和 set_system_gate 这俩货,发现了这么几个宏定义。

#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \"movw %0,%%dx\n\t" \"movl %%eax,%1\n\t" \"movl %%edx,%2" \: \: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \"o" (*((char *) (gate_addr))), \"o" (*(4+(char *) (gate_addr))), \"d" ((char *) (addr)),"a" (0x00080000))#define set_trap_gate(n,addr) \_set_gate(&idt[n],15,0,addr)#define set_system_gate(n,addr) \_set_gate(&idt[n],15,3,addr)

不过这俩都是最终指向了相同的另一个宏定义 _set_gate,说明是有共性的。

啥共性呢?我直接说吧,那段你完全看不懂的代码,是将汇编语言嵌入到 c 语言了,这种内联汇编的格式非常恶心,所以我也不想搞懂它,最终的效果就是在中断描述符表中插入了一个中断描述符。

中断描述符表还记得吧,英文叫 idt。

在这里插入图片描述
这段代码就是往这个 idt 表里一项一项地写东西,其对应的中断号就是第一个参数,中断处理程序就是第二个参数。

产生的效果就是,之后如果来一个中断后,CPU 根据其中断号,就可以到这个中断描述符表 idt 中找到对应的中断处理程序了。

比如这个。

set_trap_gate(0,&divide_error);

就是设置 0 号中断,对应的中断处理程序是 divide_error。

等 CPU 执行了一条除零指令的时候,会从硬件层面发起一个 0 号异常中断,然后执行由我们操作系统定义的 divide_error 也就是除法异常处理程序,执行完之后再返回。

再比如这个。

set_system_gate(5,&overflow);

就是设置 5 号中断,对应的中断处理程序是 overflow,是边界出错中断。

TIPS:这个 trap 与 system 的区别仅仅在于,设置的中断描述符的特权级不同,前者是 0(内核态),后者是 3(用户态),这块展开将会是非常严谨的、绕口的、复杂的特权级相关的知识,不明白的话先不用管,就理解为都是设置一个中断号和中断处理程序的对应关系就好了。

再往后看,批量操作这里。

void trap_init(void) {...for (i=17;i<48;i++)set_trap_gate(i,&reserved);...
}

17 到 48 号中断都批量设置为了 reserved 函数,这是暂时的,后面各个硬件初始化时要重新设置好这些中断,把暂时的这个给覆盖掉,此时你留个印象。

所以整段代码执行下来,内存中那个 idt 的位置会变成如下的样子。

在这里插入图片描述
好了,我们看到了设置中断号与中断处理程序对应的地方,那这行代码过去后,键盘好使了么?

NO

键盘产生的中断的中断号是 0x21,此时这个中断号还仅仅对应着一个临时的中断处理程序 &reserved,我们接着往后看。

在这行代码往后几行,还有这么一行代码。

void main(void) {...trap_init();...tty_init();...
}void tty_init(void) {rs_init();con_init();
}void con_init(void) {...set_trap_gate(0x21,&keyboard_interrupt);...
}

我省略了大量的代码,只保留了我们关心的。

注意到 trap_init 后有个 tty_init,最后根据调用链,会调用到一行添加 0x21 号中断处理程序的代码,就是刚刚熟悉的 set_trap_gate。

而后面的 keyboard_interrupt 根据名字也可以猜出,就是键盘的中断处理程序嘛!

好了,那我们终于找到大案了,就是从这一行代码开始,我们的键盘生效了!

没错,不过还有点小问题,不过不重要,就是我们现在的中断处于禁用状态,不论是键盘中断还是其他中断,通通都不好使。

而 main 方法继续往下读,还有一行这个东西。

void main(void) {...trap_init();...tty_init();...sti();...
}

sti 最终会对应一个同名的汇编指令 sti,表示允许中断。所以这行代码之后,键盘才真正开始生效!

动画酷不酷?好啦,今天的文章就到这里了,中断的原理和细节,就看我之前的文章,认认真真的聊聊中断。

键盘处理的具体流程,可以跟着我今天的代码深入进去看看哟,Linux 0.11 里还是很简单的。

TODO: 闪客省略了键盘处理的具体流程,以后有空我们也可以看一看

读完闪客文章 “你的键盘是什么时候生效的?”

相关文章:

抄写Linux源码(Day17:你的键盘是什么时候生效的?)

回忆我们需要做的事情&#xff1a; 为了支持 shell 程序的执行&#xff0c;我们需要提供&#xff1a; 1.缺页中断(不理解为什么要这个东西&#xff0c;只是闪客说需要&#xff0c;后边再说) 2.硬盘驱动、文件系统 (shell程序一开始是存放在磁盘里的&#xff0c;所以需要这两个东…...

在原生html中使用less

引入less <link rel"stylesheet/less" href"./lessDemo.less" /><script src"./js/less.min.js"></script> less.min.js文件下载地址:https://github.com/less/less.js 注意&#xff1a;less文件在前&#xff0c;js文件在后…...

【Qt】顶层窗口和普通窗口区别以及用法

区别 在Qt项目开发中&#xff0c;经常会用到窗体控件用于显示及数据操作和其他交互等。 但&#xff0c;窗体分为顶层窗口&#xff08;Top-level Window&#xff09;和普通窗口&#xff08;Regular Window&#xff09;。 他们之间是有区别的&#xff0c;包括在项目实际中的用法…...

qt开发从入门到实战2

以下是本人学习笔记 原视频&#xff1a;最新QT从入门到实战完整版|传智教育 qt开发从入门到实战1 练习示例 设计一个按钮&#xff0c;点击时弹出新窗口&#xff0c;再次点击时新窗口关闭 // exerciseQWidget* second_window new QWidget();QPushButton* btn3 new QPushBu…...

Android---字节码层面分析Class类文件

Java 提供了一种可以在所有平台上都能使用的一种中间代码---字节码文件(.class文件)。有了字节码&#xff0c;无论是那个平台只要安装了虚拟机都可以直接运行字节码文件。有了虚拟机&#xff0c;解除了 java 虚拟机与 java 代码之间的耦合。 Java 虚拟机当初被设计出来时就不单…...

【2023研电赛】东北赛区一等奖作品:基于FPGA的小型水下无线光通信端机设计

本文为2023年第十八届中国研究生电子设计竞赛东北赛区一等奖作品分享&#xff0c;参加极术社区的【有奖活动】分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&#xff01;&#xff0c;分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&a…...

JWT授权为啥要在 Authorization标头里加个Bearer 呢

这是因为 W3C 的 HTTP 1.0 规范&#xff0c;Authorization 的格式是&#xff1a; Authorization: <type> <authorization-parameters> w3c规定&#xff0c;请求头Authorization用于验证用户身份。这就是告诉我们&#xff0c;token应该写在请求头Authorization中 …...

一篇理解TCP协议

一、TCP协议概念。 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的传输层协议。它主要用于在计算机网络中&#xff0c;通过建立可靠的通信连接来进行数据传输。 TCP协议的特点如下&#xff1a; 可靠性&#xf…...

rk平台android12系统设置里面互联网选项中的以太网选项点击不了问题

rk平台android12系统中,系统设置中的互联网选项,当连接以太网以后,会显示以太网的选项,但是点击没作用,现在需要点击能够进入到以太网的设置界面,需要添加相关的点击事件。 首先,在packages/apps/Settings/AndroidManifest.xml中的以太网设置配置添加一个action,用于打…...

ctrl+d和ctrl+c的区别

CtrlD和CtrlC都是常用的键盘快捷键&#xff0c;但它们的功能不同。 CtrlD 在不同的操作系统和应用程序中可以有不同的功能。在Unix/Linux系统的命令行终端中&#xff0c;CtrlD的作用是发送EOF&#xff08;End of File&#xff09;信号&#xff0c;表示输入结束。在Windows系统中…...

ChatGLM2-6B微调实践

ChatGLM2-6B微调实践 环境准备安装部署1、安装 Anaconda2、安装CUDA3、安装PyTorch4、安装 ChatGLM2-6B 微调实践1、准备数据集2、安装python依赖3、微调并训练新模型4、微调后模型的推理与评估5、验证与使用微调后的模型 微调过程中遇到的问题 环境准备 申请阿里云GPU服务器&…...

YOLOv7独家改进FPN系列:结合新颖的 GhostSlimPAN 范式网络结构,进一步提升检测器性能

💡本篇内容:YOLOv7改进FPN系列:结合新颖的 GhostSlimFPN 范式网络结构,进一步提升检测器性能 重点:🔥🔥🔥YOLOv7 使用这个 核心创新点 在数据集改进做实验:即插即用: 当 Slim 遇到 YOLO 系列 💡🚀🚀🚀本博客 YOLO系列 + 改进源代码改进 适用于 YOLOv7…...

12. Java异常及异常处理处理

Java —— 异常及处理 1. 异常2. 异常体系3. 常见Exception4. 异常处理4.1 try finally catch关键字4.2 throws和throw 自定义异常4.3 finally&#xff0c;final&#xff0c;finalize三者的区别 1. 异常 异常&#xff1a;在程序执行过程中发生的意外状况&#xff0c;可能导致程…...

自定义hooks函数

体会1 1、js文件中定义useXX函数 export function usetestY() {const count ref(10);const doubleCount computed(() > count.value * 2);return {count,doubleCount,}; } 2、在vue文件中使用useXX函数 import { usetestY } from ./data;const { count, doubleCount } …...

Linux系统及Docker安装RabbitMq

目录 一、linux系统安装 1、上传文件 2、在线安装依赖环境 3、安装Erlang 4、安装RabbitMQ 5、开启管理界面及配置 6、启动 7、删除mq 二、docker安装 1、上传mq.tar包或使用命令拉取镜像 2、启动并运行 3、访问mq 一、linux系统安装 1、上传文件 2、在线安装依赖环…...

山东省赛二阶段第一部分解题思路

提交攻击者的IP地址 192.168.1.7 这个直接awk过滤一下ip次数&#xff0c;这个ip多得离谱&#xff0c;在日志里面也发现了它的恶意行为&#xff0c;后门&#xff0c;反弹shell 识别攻击者使用的操作系统 Linux 找出攻击者资产收集所使用的平台 shodan 提交攻击者目…...

WebGoat 靶场 JWT tokens 四 五 七关通关教程

文章目录 webGoat靶场第 四 关 修改投票数第五关第七关 你购买书&#xff0c;让Tom用户付钱 webGoat靶场 越权漏洞 将webgoat-server-8.1.0.jar复制到kali虚拟机中 sudo java -jar webgoat-server-8.1.0.jar --server.port8888解释&#xff1a; java&#xff1a;这是用于执行…...

【单元测试】如何使用 JUnit5 框架?

JUnit5 单元测试框架使用教程 一、Junit5 是什么&#xff1f; Junit5是一个用于在Java平台上进行单元测试的框架。JUnit 5 框架主要由三部分组成&#xff1a;JUnit Platform、JUnit Jupiter 和 JUnit Vintage。 JUnit Platform&#xff1a;定义了测试引擎的 API&#xff0c;是…...

C#封装、继承和多态的用法详解

大家好&#xff0c;今天我们将来详细探讨一下C#中封装、继承和多态的用法。作为C#的三大面向对象的特性&#xff0c;这些概念对于程序员来说非常重要&#xff0c;因此我们将对每个特性进行详细的说明&#xff0c;并提供相应的示例代码。 目录 1. 封装&#xff08;Encapsulati…...

数据结构与算法(持续更新)

线性表 单链表 单链表的定义 由于顺序表的插入删除操作需要移动大量的元素&#xff0c;影响了运行效率&#xff0c;因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素&#xff0c;不需要使用地址连续的存储单元&#xff0c;因此它…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...