从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动
目录
所以,键盘是如何工作的
说一说我们的8042
输出缓冲区寄存器
状态寄存器
控制寄存器
动手!
注册中断
简单整个键盘驱动
Reference
ScanCode Table
我们下一步就是准备进一步完善我们系统的交互性。基于这个,我们想到的第一个可以用来进行输入的设备,就是键盘!这个不假。我们这个章节的核心内容,就是构建一个基于键盘的输入子系统。给我们之后的系统更多功能添砖加瓦!
所以,键盘是如何工作的
虽然现在的键盘可以说是日新月异,但是基本的工作原理不会发生很大的改变。我们的键盘是一个独立的设备,需要介入总线跟我们的主板系统沟通。不管怎么说,我们的主板上,存在一个Intel 8048 或兼容芯片,它的作用是:每当键盘上发生按键操作,它就向键盘控制器报告哪个键被按下,按键是否弹起。
上图的8048 是键盘上的芯片,其主要责任就是监控哪个键被按下。当键盘上发生按键操作时,8048 当然知道是哪个键被按下。但光它自己知道还不 行,它毕竟要将按键信息传给8042,必须得让8042 知道到底是按下了哪个键,为此8048 必然要和8042达成一个协议,这个协议规定了键盘上的每个物理键对应的唯一数值,说白了就是对键盘上所有的按键进行编码,为每个按键分配唯一的数字,这样双方都知道了每个数值代表哪个键。当某个键被按下时,8048 把这个键对应的数值发送给8042,8042根据这个数值便知道是哪个键被按下了。
现在,笔者就在使用键盘敲击一些字符,我松开手,按键被弹起来了,输入显然完成了,8048就会通知在主板上的8042何时按键被弹起,也就是击键操作何时结束,这样8042才知道用户在一次持续按键操作中到底输入了多少个相同的字符。因此,键盘扫描码中不仅仅要记录按键被按下时对应的编码,还要记录按键被松开(弹起)时的编码。这下事情变得显然了,我们不得不请出两个码——按键被按下时的编码叫通码,也就是表示按键上的触点接通了内部电路,使硬件产生了一个码,故通码也称为 makecode。按键在被按住不松手时会持续产生相同的码,直到按键被松开时才终止,因此按键被松开弹起时产生的编码叫断码,也就是电路被断开了,不再持续产生码了,故断码也称为breakcode。一个键的扫描码是由通码和断码组的。
郑刚老师在《操作系统真相还原》中介绍了三种扫描码,这里只介绍第二种,因为余下的已经几乎没人使用了,我们(程序员)不是不知道键盘用的是哪种扫描码吗,那好,只要 8042 知道就行。为了兼容第一套 键盘扫描码对应的中断处理程序,不管键盘用的是何种键盘扫描码,当键盘将扫描码发送到 8042 后,都 由 8042 转换成第一套扫描码,这就是我们上一节中所说的 8042 的“处理”。 因此,我们在键盘的中断处理程序中只处理第一套键盘扫描码就可以了。关于整张表,参考笔者的附录即可。
看完这个表,你仔细观察一下,大多数情况下第一套扫描码中的通码和断码都是 1 字节大小。而且不难发现:断码 = 0x80 + 通码。
所以回过头来,我们在分析一下:完整的击键操作包括两个过程,先是被按下,也许是被按下一瞬间,也许是持续保持被按下,然后是被松开,总之,按下的动作是先于松开发生的,因此每次按键时会先产生通码,再产生断码。比如我们按下字符 a 时,按照第一套键盘扫描码来说,先是产生通码 0x1e,后是产生断码 0x9e。
另一些我们注意到:一些按键的通码和断码都以0xe0 开头,它们占2 字节,甚至Pause 键以0xe1 开头, 占6字节。原因是这样的,并不是一种键盘就要用一套键盘扫描码,最初第一套键盘扫描码是由XT 键盘所使用的,它后来也被一些更新的键盘所使用。XT 键盘上的键很少,比如右边回车键附近就没有alt 和ctrl 键,这是在后来的键盘中才加进去的,因此表示扩展 extend,所以在扫描码前面加了 0xe0 作为前缀。比如在 XT 键盘上,左边有alt 键,其通码为0x38,断码为0xb8。右边的alt 键是后来在新的键盘上加进去的,因此,一方面为了表示都是同样功能的alt 键,另一方面表示不是左边那个alt,而是右边的alt,于是这个扩展的alt 键的扫描码便为“0xe0 和原来左边alt 的扫描码”。因此,右边alt 键的通码便为“0xe0,0x38”,断码为“0xe0,0xb8”。
那组合键呢?比如说我们嗯下Ctrl + C键,想要复制郑刚老师的教授内容,自己偷个懒的时候,这个是如何处理的呢?
现在你自己慢慢做一次Ctrl + C试一下:
-
你先摁下Ctrl键,毕竟你先按C就会打印出字符C了。
-
你保持Ctrl键不松手
-
一个手指按下C
-
然后随意的松开,比如说可能松开ctrl
-
松开C,然后感觉自己像是一个笨蛋一样(笑)
不开玩笑了,当我们做步骤一的时候,8048向8042发送了<L-ctrl>
键的通码0x14,当然,这显然是第二套扫描码8042收到0x14后将其转换为第一套键盘扫描码0x1d,并将其保存到自己的输出缓冲区寄存器中。接着,8042向中断代理发送中断信号,处理器随后执行键盘中断处理程序。键盘中断处理程序从8042的输出缓冲区寄存器中获取扫描码0x1d,并判断这次按下的是<L-ctrl>
键(实际上无论是<L-ctrl>
还是<R-ctrl>
,通常都被视为Ctrl键,因为它们只是位置不同,功能相同)。
我们的键盘处理程序在某个全局变量中记录Ctrl键已被按下。这个,跟大部分的键盘处理程序是一样的。
第二步的时候,我们的<L-ctrl>
键持续按住不松手因此8048会持续向8042发送0x14。8042每次都会将其转换为第一套键盘扫描码0x1d,并向中断代理发送中断信号。每次键盘中断处理程序都会从8042中获取到0x1d。与步骤一相同,键盘处理程序判断这是<L-ctrl>
的通码,并在全局变量中记录Ctrl键被按下。尽管Ctrl键已经被按下,键盘处理程序可能只记录最后一次按下的键,而不关心之前按下了多少次相同的键。(我们好像没必要记着,对吧)
第三步,我们终于准备发送c的第二套键盘扫描码0x21,8042将其转换为第一套键盘扫描码0x2e,并保存到输出缓冲区寄存器中,随后向中断代理发送中断信号。
键盘中断处理程序开始执行,从8042的输出缓冲区寄存器中获取0x2e。键盘处理程序判断这次按下的是c键,并检查之前Ctrl键已经被按下(全局变量中有记录),因此判断用户按下的是“Ctrl+c”组合键。Ctrl、Alt、Shift等控制键通常与后续按下的键组合使用,这是基于微软的操作习惯,即控制键先按下,普通键后按下。由于这次按下的不是控制键,键盘处理程序将记录Ctrl键是否按下的全局变量清空,并将“Ctrl+C”这一消息上报给上层模块。我们的上层接受到后,就会做对应的Hook操作。
我们在步骤四种假设你是,,<L-ctrl>
键被松开,8048向8042发送它的第二套键盘扫描码0xf0和0x14(断码)。第二套键盘扫描码的断码通常由固定的前缀0xf0和其通码组成。8042将这两个字节转换为第一套键盘扫描码0x9d(断码),随后发送中断信号。键盘中断处理程序发现最高位为1,表示这是断码,意味着键被松开。无论松开的是什么键,键盘处理程序都会忽略,不做任何处理。
在步骤五中,a键被松开,8048向8042发送它的第二套键盘扫描码0xf0和0x21(断码)。8042将其转换为0xae并保存到输出缓冲区寄存器中,随后发送中断信号。键盘中断处理程序读取该扫描码,发现是键被弹起,因此忽略该事件。
说一说我们的8042
Intel 8042 芯片或兼容芯片被集成在主板上的南桥芯片中,它是键盘控制器,也就是键盘的 IO 接口, 因此它是8048的代理,也是前面所得到的处理器和键盘的“中间层”。8048通过PS/2、USB 等接口与8042通信,处理器通过端口与8042通信(IO 接就是外部硬件的代理,它和处理器都位于主机内部,因此处理器与 IO 接口可以通过端口直接通信)。
我们来看看IO口:
寄存器 | 端口 | 读/写 |
---|---|---|
Output Buffer(输出缓冲区) | 0x60 | 读 |
Input Buffer(输入缓冲区) | 0x60 | 写 |
Status Register(状态寄存器) | 0x64 | 读 |
Control Register(控制寄存器) | 0x64 | 写 |
8042 是连接 8048 和处理器的桥梁,8042 存在的目的是:为了处理器可以通过它控制 8048 的工作方式,然后让8048 的工作成果通过8042 回传给处理器。此时8042 就相当于数据的缓冲区、中转站,根据数据被发送的方向,8042 的作用分别是输入和输出。
处理器把对 8048 的控制命令临时放在 8042 的寄存器中,让 8042 把控制命令发送给 8048,此时 8042 充当了 8048 的参数输入缓冲区。 8048 把工作成果临时提交到8042 的寄存器中,好让处理器能从 8042 的寄存器中获取它(8048)的工作成果,此时 8042 充当了 8048 的结果输出缓冲区。
当需要把数据从处理器发到8042 时(数据传送尚未发生时),0x60 端口的作用是输入缓冲区,此时应该用out 指令写入0x60 端口。
当数据已从 8048 发到 8042 时,0x60 端口的作用是输出缓冲 区,此时应该用in指令从8042 的0x60 端口(输出缓冲区寄存器)读 取8048 的输出结果。
最后,出于编程目的,还差寄存器说明:
寄存器 | 宽度 | 读写属性 | 描述 |
---|---|---|---|
输入缓冲区寄存器 | 8 位 | 只写 | 键盘驱动程序通过 out 指令向此寄存器写入对 8048 的控制命令、参数等。对于 8042 本身的控制命令也是写入此寄存器。 |
状态寄存器 | 8 位 | 只读 | 反映 8048 和 8042 的内部工作状态。各位意义详见描述。 |
控制寄存器 | 8 位 | 只写 | 用于写入命令控制字。每个位都可以设置一种工作方式,意义详见描述。 |
输出缓冲区寄存器
8042的输出缓冲区寄存器是一个8位宽度的寄存器,只读,键盘驱动程序从此寄存器中通过in指令读取来自8048 的扫描码、来自8048 的命令应答以及对8042 本身设置时,8042 自身的应答也从该寄存器中获取。
注意,输出缓冲区寄存器中的扫描码是给处理器准备的,在处理器未读取之前,8042 不会再往此寄 存器中存入新的扫描码。
8042 是怎样知道输出缓冲区寄存器中的值是否被读取了呢?这个简单,8042 也有个 智能芯片,它为处理器提供服务,当处理器通过端口跟它要数据的时候它当然知道了,因此,每当有 in 指令来读取此寄存器时,8042 就将状态寄存器中的第0位置成0,这就表示寄存器中的扫描码数据已经被取走,可以继续处理下一个扫描码了。当再次往输出缓冲寄存器存入新的扫描码时,8042 就将状态寄存器中的第 0 位置为 1,这表示输出缓冲寄存器已满,可以读取了。
状态寄存器
位 | 描述 |
---|---|
位 0 | 置 1 时表示输出缓冲区寄存器已满,处理器通过 in 指令读取后该位自动置 0。 |
位 1 | 置 1 时表示输入缓冲区寄存器已满,8042 将值读取后该位自动置 0。 |
位 2 | 系统标志位,最初加电时为 0,自检通过后置为 1。 |
位 3 | 置 1 时,表示输入缓冲区中的内容是命令,置 0 时,输入缓冲区中的内容是普通数据。 |
位 4 | 置 1 时表示键盘启用,置 0 时表示键盘禁用。 |
位 5 | 置 1 时表示发送超时。 |
位 6 | 置 1 时表示接收超时。 |
位 7 | 来自 8048 的数据在奇偶校验时出错。 |
控制寄存器
位 | 描述 |
---|---|
位 0 | 置 1 时启用键盘中断。 |
位 1 | 置 1 时启用鼠标中断。 |
位 2 | 设置状态寄存器的位 2。 |
位 3 | 置 1 时,状态寄存器的位 4 无效。 |
位 4 | 置 1 时禁止键盘。 |
位 5 | 置 1 时禁止鼠标。 |
位 6 | 将第二套键盘扫描码转换为第一套键盘扫描码。 |
位 7 | 保留位,默认为 0。 |
动手!
注册中断
这里我们先把中断一次性注册了,省事
; -------------------------------------------------------------------------
; Part 2 Table Page for the interrupt for kernel
; -------------------------------------------------------------------------
INTR_VECTOR 0x20, PUSH_ZERO ; Entry for the timer interrupt.
INTR_VECTOR 0x21, PUSH_ZERO ; Entry for the keyboard interrupt.
INTR_VECTOR 0x22, PUSH_ZERO ; Cascade interrupt.
INTR_VECTOR 0x23, PUSH_ZERO ; Entry for serial port 2.
INTR_VECTOR 0x24, PUSH_ZERO ; Entry for serial port 1.
INTR_VECTOR 0x25, PUSH_ZERO ; Entry for parallel port 2.
INTR_VECTOR 0x26, PUSH_ZERO ; Entry for the floppy disk.
INTR_VECTOR 0x27, PUSH_ZERO ; Entry for parallel port 1.
INTR_VECTOR 0x28, PUSH_ZERO ; Entry for the real-time clock.
INTR_VECTOR 0x29, PUSH_ZERO ; Redirect.
INTR_VECTOR 0x2a, PUSH_ZERO ; Reserved.
INTR_VECTOR 0x2b, PUSH_ZERO ; Reserved.
INTR_VECTOR 0x2c, PUSH_ZERO ; PS/2 mouse.
INTR_VECTOR 0x2d, PUSH_ZERO ; FPU floating-point unit exception.
INTR_VECTOR 0x2e, PUSH_ZERO ; Hard disk.
INTR_VECTOR 0x2f, PUSH_ZERO ; Reserved.
记得修改一下支持的中断数
#define IDT_DESC_CNT (0x30) // The number of interrupt descriptors in the IDT
然后在pci.c种,记得只打开键盘的中断
// Mask interrupts to disable all IRQsoutb(PCI_MASTER_DATA_PORT, 0xfd); // Mask all IRQs on the master PIC (set bit 0)outb(PCI_SLAVE_DATA_PORT, 0xff); // Mask all IRQs on the slave PIC (set all bits)
简单整个键盘驱动
实际上就是直接读缓存端口就好了哈哈
#include "include/device/keyboard.h"
#include "include/library/ccos_print.h"
#include "include/kernel/interrupt.h"
#include "include/device/configs/keyboard_ascii.h"
#include "include/device/configs/keyboard_mappings.h"
#include "include/io/io.h"
static void keyboard_intr_handler(void)
{// hey don't use puts here, gs is not switched, else we will visit// wrong place__ccos_putchar('C');// uint8_t scancode = inb(KEYBOARD_BUF_PORT);// __ccos_display_int(scancode);return;
}
// register the interrupt here
void init_basic_input_subsystem(void)
{ccos_puts("initing subsystem of input: from keyboard!\n");register_intr_handler(KEYBOARD_INTERRUPT_N, keyboard_intr_handler);ccos_puts("init subsystem of input: from keyboard done!\n");
}
这里呢,#include "include/device/configs/keyboard_ascii.h" #include "include/device/configs/keyboard_mappings.h"
两个文件就具体到笔者的代码种看先。
嘿!我们上电试试看,不用担心线程的事情,我们把时钟中断关闭了。
可以看到我们摁下摁键的时候,这些字符就会蹦出来了!注意,嗯下一次,弹起一次。这就说明了通码和断码的存在了。
小小的修改一下代码哈:
static void keyboard_intr_handler(void)
{// hey don't use puts here, gs is not switched, else we will visit// wrong place// __ccos_putchar('C');uint8_t scancode = inb(KEYBOARD_BUF_PORT);__ccos_display_int(scancode);__ccos_putchar(' ');return;
}
看看!现在外面收到了scancode了!
代码
CCOperateSystem/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code at main · Charliechen114514/CCOperateSystemhttps://github.com/Charliechen114514/CCOperateSystem/tree/main/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code
下一篇
nullhttps://blog.csdn.net/charlie114514191/article/details/146105521
Reference
ScanCode Table
键 | 通码 | 断码 | 键 | 通码 | 断码 |
---|---|---|---|---|---|
<esc> | 01 | 81 | <caps lock> | 3a | ba |
F1 | 3b | bb | a | 1e | 9e |
F2 | 3c | bc | s | 1f | 9f |
F3 | 3d | bd | d | 20 | a0 |
F4 | 3e | be | f | 21 | a1 |
F5 | 3f | bf | g | 22 | a2 |
F6 | 40 | c0 | h | 23 | a3 |
F7 | 41 | c1 | j | 24 | a4 |
F8 | 42 | c2 | k | 25 | a5 |
F9 | 43 | c3 | l | 26 | a6 |
F10 | 44 | c4 | :; | 27 | a7 |
F11 | 57 | d7 | "' | 28 | a8 |
F12 | 58 | d8 | <enter> | 1c | 9c |
~· | 29 | a9 | <L-Shift> | 2a | aa |
!1 | 02 | 82 | z | 2c | ac |
@2 | 03 | 83 | x | 2d | ad |
#3 | 04 | 84 | c | 2e | ae |
$4 | 05 | 85 | v | 2f | af |
%5 | 06 | 86 | b | 30 | b0 |
^6 | 07 | 87 | n | 31 | b1 |
&7 | 08 | 88 | m | 32 | b2 |
*8 | 09 | 89 | <, | 33 | b3 |
(9 | 0a | 8a | >. | 34 | b4 |
)0 | 0b | 8b | ?/ | 35 | b5 |
_- | 0c | 8c | <R-shift> | 36 | b6 |
+= | 0d | 8d | <L-ctrl> | 1d | 9d |
<backspace> | 0e | 8e | <L-alt> | 38 | b8 |
<tab> | 0f | 8f | <space> | 39 | b9 |
q | 10 | 90 | <R-alt> | e0,38 | e0,b8 |
w | 11 | 91 | <R-ctrl> | e0,1d | e0,9d |
e | 12 | 12 | |||
r | 13 | 93 | |||
t | 14 | 94 | |||
y | 15 | 95 | |||
u | 16 | 96 | |||
i | 17 | 97 | |||
o | 18 | 98 | |||
p | 19 | 99 | |||
{[ | 1a | 9a | |||
}] | 1b | 9b | |||
|\ | 2b | ab |
键 | 通码 | 断码 | 键 | 通码 | 断码 |
---|---|---|---|---|---|
PrintScreen SysRq | e0,2a,e0,37 | e0,b7,e0,aa | NumLock | 45 | c5 |
Scroll Lock | 46 | c6 | / | e0,35 | e0,b5 |
Pause Break | e1,1d,45 | e1,9d,c5 | * | 37 | b7 |
Insert | e0,52 | e0,d2 | - | 4a | ca |
Home | e0,47 | e0,c7 | 7Home | 47 | c7 |
Page Up | e0,49 | e0,c9 | 8Up | 48 | c8 |
Delete | e0,53 | e0,d3 | 9PgUp | 49 | c9 |
End | e0,4f | e0,cf | 4Left | 4b | cb |
Page Down | e0,51 | e0,d1 | 5 | 4c | cc |
← | e0,46 | e0,c6 | 6Right | 4d | cd |
→ | e0,4d | e0,cd | 1End | 4f | cf |
↑ | e0,48 | e0,c8 | 2Down | 50 | d0 |
↓ | e0,50 | e0,d0 | 3PgDn | 51 | d1 |
0Ins | 52 | d2 | .Del | 53 | d3 |
+ | 4e | ce | Enter | e0,1c | e0,9c |
相关文章:
从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动
目录 所以,键盘是如何工作的 说一说我们的8042 输出缓冲区寄存器 状态寄存器 控制寄存器 动手! 注册中断 简单整个键盘驱动 Reference ScanCode Table 我们下一步就是准备进一步完善我们系统的交互性。基于这个,我们想到的第一个可以…...
Azure云生态系统详解:核心服务、混合架构与云原生概念
核心服务:深入掌握Azure SQL Database、Azure Database for PostgreSQL、Azure Database for MySQL的架构、备份恢复、高可用性配置(如Geo-Replication、自动故障转移组、异地冗余备份)。混合架构:熟悉Azure Arc(管理混…...
Unity Dots
文章目录 什么是DotsDOTS的优势ECS(实体组件系统)Job System作业系统Burst编译器最后 什么是Dots DOTS(Data-Oriented Technology Stack)是Unity推出的一种用于开发高性能游戏和应用的数据导向技术栈,包含三大核心组件…...
SAP DOI EXCEL宏的使用
OAOR里上传EXCEL模版 屏幕初始化PBO创建DOI EXCEL对象,并填充EXCEL内容 *&---------------------------------------------------------------------* *& Module INIT_DOI_DISPLAY_9100 OUTPUT *&--------------------------------------------…...
VSTO(C#)Excel开发3:Range对象 处理列宽和行高
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...
单链表基本操作的实现与解析(补充)
目录 一、引言 二、代码实现 遍历考虑情况 三、操作解析 查找操作(sltfind函数) 前插操作(sltinsert函数) 后插操作(sltinsertafter函数) 前删操作(slterase函数) 后删操作&…...
电子学会—2024年月6青少年软件编程(图形化)四级等级考试真题——魔法门
魔法门 1.准备工作 (1)保留默认角色小猫和白色背景; (2)添加角色Home Button,复制9个造型,在每个造型上分别加上数字1到9,如下图所示; 2.功能实现 (1)程序开始,依次克隆出五个Home Button,克隆体之间的间距为90; …...
《加快应急机器人发展的指导意见》中智能化升级的思考——传统应急设备智能化升级路径与落地实践
感谢阅读本次内容分享,下面我将解读分析《加快应急机器人发展的指导意见》,喜欢的点赞支持一下呗~(日更真的很辛苦~),欢迎评论区留言讨论,你们的发言我都会看到~ 《加快应急机器人发展的指导意见》中智能化升级的思考——传统应急…...
Git系列之git tag和ReleaseMilestone
以下是关于 Git Tag、Release 和 Milestone 的深度融合内容,并补充了关于 Git Tag 的所有命令、详细解释和指令实例,条理清晰,结合实际使用场景和案例。 1. Git Tag 1.1 定义 • Tag 是 Git 中用于标记特定提交(commit…...
【每日学点HarmonyOS Next知识】Web上传文件、监听上下左右区域连续点击、折叠悬停、字符串相关、播放沙盒视频
1、HarmonyOS APP内h5原生webview input[typefile]无法唤醒手机上传? 文件上传要使用对应的picker https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/web-file-upload-V5 Web组件支持前端页面选择文件上传功能,应用开发者可以使用on…...
解决电脑问题(3)——显示器问题
当电脑显示器出现问题时,可以根据不同的故障现象采取相应的解决方法,以下是一些常见的情况及解决措施: 屏幕无显示 检查连接:首先检查显示器与电脑主机之间的视频连接线是否插好,确保两端的接口都牢固连接,…...
AArch64架构及其编译器
—1.关于AArch64架构 AArch64是ARMv8-A架构的64位执行状态,支持高性能计算和大内存地址空间。它广泛应用于现代处理器,如苹果的A系列芯片、高通的Snapdragon系列,以及服务器和嵌入式设备。 • 编译器:可以使用GCC、Clang等编译器编…...
免费送源码:Java+springboot+MySQL 房屋租赁系统小程序的设计与实现 计算机毕业设计原创定制
目 录 摘要 1 1 绪论 1 1.1选题意义 1 1.2开发现状 1 1.3springboot框架介绍 1 1.4论文结构与章节安排 1 2 房屋租赁系统小程序系统分析 3 2.1 可行性分析 3 2.1.1 技术可行性分析 3 2.1.2 经济可行性分析 3 2.1.3 法律可行性分析 3 2.2 系统功能分析 3 2.2.1 功…...
前端数据模拟 Mock.js 学习笔记
mock.js介绍 Mock.js是一款前端开发中拦截Ajax请求再生成随机数据响应的工具,可以用来模拟服务器响应 优点是:非常方便简单,无侵入性,基本覆盖常用的接口数据类型支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜…...
【Linux内核系列】:深入解析输出以及输入重定向
🔥 本文专栏:Linux 🌸作者主页:努力努力再努力wz ★★★ 本文前置知识: 文件系统以及文件系统调用接口 用c语言简单实现一个shell外壳程序 内容回顾 那么在此前的学习中,我们对于Linux的文件系统已经有了…...
Adam 优化器与动量法:二阶矩与 ODE 的联系
Adam 优化器与动量法:二阶矩与 ODE 的联系 作为深度学习研究者,你一定对 Adam(Adaptive Moment Estimation)优化器非常熟悉。它因自适应学习率和高效率而成为训练神经网络的标配算法。Adam 使用了一阶动量(梯度的指数…...
嵌入式学习第二十三天--网络及TCP
进程通信的方式: 同一主机 传统 system V 不同主机 网络 --- 解决不同主机间 的进程间通信 网络 (通信) //1.物理层面 --- 联通(通路) //卫星 2G 3G 4G 5G 星链 (千帆) //2.逻辑层面 --- 通路(软件) MAC os LINUX …...
前端表单提交与后端处理全解析:从HTML到Axios与SpringBoot实战
前端表单提交与后端处理全解析:从HTML到Axios与SpringBoot实战 一、GET与POST请求的两种面孔 1. HTML表单基础实现 <!-- GET请求示例:搜索表单 --> <form action="/api/search" method="GET"><input type="text" name="…...
python django orm websocket html 实现deepseek持续聊天对话页面
最终效果: 技术栈: python django orm websocket html 项目结构: 这里只展示关键代码: File: consumers.py # -*- coding:utf-8 -*- # Author: 喵酱 # time: 2025 - 03 -02 # File: consumers.py # desc: import json from asg…...
大白话html语义化标签优势与应用场景
大白话html语义化标签优势与应用场景 大白话解释 语义化标签就是那些名字能让人一看就大概知道它是用来做什么的标签。以前我们经常用<div>来做各种布局,但是<div>本身没有什么实际的含义,就像一个没有名字的盒子。而语义化标签就像是有名…...
考研英语语法全攻略:从基础到长难句剖析
引言 在考研英语的备考之旅中,语法犹如一座灯塔,为我们在浩瀚的英语知识海洋中指引方向。无论是阅读理解中复杂长难句的解读,还是写作时准确流畅表达的需求,扎实的语法基础都起着至关重要的作用。本文将结合有道考研语法基础入门课的相关内容,为大家全面梳理考研英语语法…...
Vue3 生命周期
回顾Vue2的生命周期 创建(创建前,创建完毕)beforeCreate、created挂载(挂载前,挂载完毕)beforeMount、mounted更新(更新前,更新完毕)beforeUpdate、updated销毁…...
hbase-05 namespace、数据的确界TTL
要点 掌握HBase的命名空间namespace概念 掌握HBase数据版本确界 掌握HBase数据TTL 1. HBase的namespace 1.1 namespace基本介绍 在HBase中,namespace命名空间指对一组表的逻辑分组,类似RDBMS中的database,方便对表在业务上划分。Apache…...
线程的常见使用方法
Java中的线程并不是真正意义的线程,我们使用的是Thread类来表示线程,而这个类是 JVM 用来管理线程的一个类,也就是说,每个线程都有一个唯一的 Thread对象 与之关联 每一个执行流都需要有一个对象来进行描述,那么一个Thread对象就是用来表述一个线程执行流的,JVM会将这些对象统…...
架构师面试(十一):消息收发
问题 IM 是互联网中非常典型的独立的系统,麻雀虽小但五脏俱全,非常值得深入研究和探讨,继上次IM相关题目之后,我们继续讨论IM相关话题。 关于IM系统【消息收发模型】的相关描述,下面说法错误的有哪几项? …...
MoonSharp 文档一
目录 1.Getting Started 步骤1:在 IDE 中引入 MoonSharp 步骤2:引入命名空间 步骤3:调用脚本 步骤4:运行代码 2.Keeping a Script around 步骤1:复现前教程所有操作 步骤2:改为创建Script对象 步骤…...
【linux网络编程】端口
一、端口(Port)概述 在计算机网络中,端口(Port) 是用来标识不同进程或服务的逻辑通信端点。它类似于一座大楼的房间号,帮助操作系统和网络协议区分不同的应用程序,以便正确地传输数据。 1. 端口…...
Vulnhub-Node
目录标题 一、靶机搭建二、信息收集靶机信息扫ip扫开放端口和版本服务信息指纹探测目录扫描 三、Web渗透信息收集zip爆破ssh连接 四、提权内核版本提权 利用信息收集拿到路径得到账户密码,下载备份文件,base64解密后,利用fcrackzip爆破zip压缩…...
RK3568平台(camera篇)camera3_profiles_rk3588.xml解析
camera3_profiles_rk3588.xml 是一个与 Android 相机 HAL(硬件抽象层)相关的配置文件,通常用于定义 Rockchip RK3588 平台上的相机设备及其功能。该文件基于 Android 的 Camera3 HAL 框架,用于描述相机的配置、流配置、分辨率、帧率、格式等信息。 以下是对 camera3_profi…...
高阶哈希算法
SHA-256简介 SHA-256 是 **SHA-2(Secure Hash Algorithm 2)**家族中的一种哈希算法,由美国国家安全局设计,并于 2001 年发布。它能够将任意长度的数据映射为一个固定长度256 位,即 32 字节的哈希值,通常以…...
Spark数据倾斜深度解析与实战解决方案
Spark数据倾斜深度解析与实战解决方案 一、数据倾斜的本质与影响 数据倾斜是分布式计算中因数据分布不均导致的性能瓶颈现象。当某些Key对应的数据量远超其他Key时,这些"热点Key"所在的Task会消耗80%以上的计算时间,成为整个作业的木桶短板。具体表现为: Task执…...
Kubernetes滚动更新实践
前言 在我之前的项目中,对微服务升级采用的做法是删除整个namespace, 再重新应用所有yaml。 这种方式简单粗暴,但是不可避免的导致服务中断,影响了用户体验 为了解决更新服务导致的服务中断问题, Kubernetes提供了一种…...
Broken pipe
比较常见的一个问题。 但是并不是每个人都能说清楚。 首先注意下写法: Broken pipe # B大写 p小写 主要是grep的时候别写错了 常见的原因 1、客户端关闭连接。 在服务器端处理请求的过程中,客户端突然关闭了连接,例如浏览器关闭、网络断开…...
doris:ClickHouse
Doris JDBC Catalog 支持通过标准 JDBC 接口连接 ClickHouse 数据库。本文档介绍如何配置 ClickHouse 数据库连接。 使用须知 要连接到 ClickHouse 数据库,您需要 ClickHouse 23.x 或更高版本 (低于此版本未经充分测试)。 ClickHouse 数据库的 JDBC 驱动程序&a…...
前K个高频单词
692. 前K个高频单词 - 力扣(LeetCode) 题目描述: 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序…...
恢复IDEA的Load Maven Changes按钮
写代码的时候不知道点到什么东西了,pom文件上的这个弹窗就是不出来了,重启IDEA,reset windos都没用,网上搜也没收到解决方案 然后开打开其他项目窗口时,看到那个的功能名叫 Hide This Notification 于是跑到Setting里…...
【五.LangChain技术与应用】【31.LangChain ReAct Agent:反应式智能代理的实现】
一、ReAct Agent是啥?为什么说它比「普通AI」聪明? 想象一下,你让ChatGPT查快递物流,它可能直接编个假单号糊弄你。但换成ReAct Agent,它会先推理(Reasoning)需要调用哪个接口,再行动(Action)查询真实数据——这就是ReAct的核心:让AI学会「动脑子」再动手。 举个真…...
Leetcode 62: 不同路径
Leetcode 62: 不同路径 问题描述: 一个机器人位于一个 (m \times n) 网格的左上角(起始点位于 ((0, 0)))。 机器人每次只能向下或向右移动一步。网格的右下角为终点(位于 ((m-1, n-1)))。 计算机器人从左上角到右下角…...
计算机毕业设计SpringBoot+Vue.js火锅店管理系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Docker Desktop 4.38 安装与配置全流程指南(Windows平台)
一、软件定位与特性 Docker Desktop 是容器化应用开发与部署的一体化工具,支持在本地环境创建、管理和运行Docker容器。4.38版本新增GPU加速支持、WSL 2性能优化和Kubernetes 1.28集群管理功能,适用于微服务开发、CI/CD流水线搭建等场景。 二、安装环境…...
算法系列之广度优先搜索解决妖怪和尚过河问题
在算法学习中,广度优先搜索(BFS)是一种常用的图搜索算法,适用于解决最短路径问题、状态转换问题等。本文将介绍如何利用广度优先搜索解决经典的“妖怪和尚过河问题”。 问题描述 有三个妖怪和三个和尚需要过河。他们只有一条小船…...
【技术白皮书】内功心法 | 第一部分 | IP协议的目的与工作原理(IP地址)
目录 IP协议的介绍IP协议的目的与工作原理IP协议处理过程与信件传递的相似IP协议处理过程与信件传递的区别IP协议中的概念IP数据包IP地址IP地址组成IP地址分类和组成A、B、C三类地址的格式设计特殊类型的IP地址与传统通信地址进行类比IP地址的表示五类IP地址的地址范围IP地址的…...
【Linux】外接硬盘管理
查看外接硬盘信息 连接外接硬盘后,使用以下命令识别设备: lsblk:列出块设备及其挂载点 lsblk示例输出可能显示设备名称如 /dev/sdb。 通过 lsblk -f 可同时显示文件系统类型和 UUID。 fdisk -l:列出所有磁盘的分区信息ÿ…...
【Hadoop】详解Zookeeper选主流程
1. ZooKeeper 的工作原理 Zookeeper 的核心是Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。 为了保证事务的顺序一致性,Zookeeper采用了递增的事务id号(zxid)来标识事务。所有…...
C语言-语法
数据类型 字符串 C中字符串拼接不用+号,直接使用空格。 char* str = "hello" "world"; 换行链接,加上\就不会报错 char* longStr = "00000000000000000000000000000\ 00000000000000000000000000000"; typedef C 语言提供了 typedef …...
Flink Forward Asia 2024 大会 内容整理
一、Flink 2.0:十年磨一剑,开启流计算新纪元 作为 Flink 诞生十周年的重磅更新,Flink 2.0 被定位为“面向未来十年的流计算引擎”,核心目标是解决云原生、AI 融合、存算效率等新时代挑战。以下是它的几大杀手锏: 1. …...
golang进阶知识专项-理解值传递
在 Go 语言中,所有函数的参数传递都是值传递(Pass by Value)。当你将一个变量作为参数传递给函数时,实际上传递的是该变量的副本,而不是变量本身。理解这一点对于避免常见的编程错误至关重要。根据不同的类型ÿ…...
2020年联考《申论》第三题(河北县级卷)
材料: 总面积不过八平方米的店铺,摆满货品之后,两人在店内走动都有些困难,但这家小店在当地却是小有名气的老字号。69岁的店主老林是资深木雕艺人,更被称为“最后的手工酸枝筷子传人”。手工木筷取材自缅甸坤甸木、老挝…...
Kali WebDAV 客户端工具——Cadaver 与 Davtest
1. 工具简介 在 WebDAV 服务器管理和安全测试过程中,Cadaver 和 Davtest 是两款常用的命令行工具。 Cadaver 是一个 Unix/Linux 命令行 WebDAV 客户端,主要用于远程文件管理,支持文件上传、下载、移动、复制、删除等操作。Davtest 则是一款…...
八点八数字科技:开启数字人应用的无限可能
在数字科技飞速发展的时代,八点八数字科技凭借卓越实力,成为行业的领军者。自 2014 年成立,公司汇聚近百位顶尖人才,手握 30 新型发明专利与 80 软件著作权,完成数千万融资,实力备受认可。其自主研发的全…...