从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动

news2025/3/9 10:40:19

目录

所以,键盘是如何工作的

说一说我们的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试一下:

  1. 你先摁下Ctrl键,毕竟你先按C就会打印出字符C了。

  2. 你保持Ctrl键不松手

  3. 一个手指按下C

  4. 然后随意的松开,比如说可能松开ctrl

  5. 松开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 IRQs
    outb(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>0181<caps lock>3aba
F13bbba1e9e
F23cbcs1f9f
F33dbdd20a0
F43ebef21a1
F53fbfg22a2
F640c0h23a3
F741c1j24a4
F842c2k25a5
F943c3l26a6
F1044c4:;27a7
F1157d7"'28a8
F1258d8<enter>1c9c
29a9<L-Shift>2aaa
!10282z2cac
@20383x2dad
#30484c2eae
$40585v2faf
%50686b30b0
^60787n31b1
&70888m32b2
*80989<,33b3
(90a8a>.34b4
)00b8b?/35b5
_-0c8c<R-shift>36b6
+=0d8d<L-ctrl>1d9d
<backspace>0e8e<L-alt>38b8
<tab>0f8f<space>39b9
q1090<R-alt>e0,38e0,b8
w1191<R-ctrl>e0,1de0,9d
e1212
r1393
t1494
y1595
u1696
i1797
o1898
p1999
{[1a9a
}]1b9b
|\2bab
通码断码通码断码
PrintScreen SysRqe0,2a,e0,37e0,b7,e0,aaNumLock45c5
Scroll Lock46c6/e0,35e0,b5
Pause Breake1,1d,45e1,9d,c5*37b7
Inserte0,52e0,d2-4aca
Homee0,47e0,c77Home47c7
Page Upe0,49e0,c98Up48c8
Deletee0,53e0,d39PgUp49c9
Ende0,4fe0,cf4Left4bcb
Page Downe0,51e0,d154ccc
e0,46e0,c66Right4dcd
e0,4de0,cd1End4fcf
e0,48e0,c82Down50d0
e0,50e0,d03PgDn51d1
0Ins52d2.Del53d3
+4eceEntere0,1ce0,9c

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

01-简单几步!在Windows上用llama.cpp运行DeepSeek-R1模型

1.llama.cpp介绍 Llama.cpp 是一个开源的、轻量级的项目&#xff0c;旨在实现 Meta 推出的开源大语言模型 Llama 的推理&#xff08;inference&#xff09;。Llama 是 Meta 在 2023 年开源的一个 70B 参数的高质量大语言模型&#xff0c;而 llama.cpp 是一个用 C 实现的轻量化…

HarmonyOS Next 属性动画和转场动画

HarmonyOS Next 属性动画和转场动画 在鸿蒙应用开发中&#xff0c;动画是提升用户体验的关键要素。通过巧妙运用动画&#xff0c;我们能让应用界面更加生动、交互更加流畅&#xff0c;从而吸引用户的注意力并增强其使用粘性。鸿蒙系统为开发者提供了丰富且强大的动画开发能力&…

JavaWeb-mysql8版本安装

下载方式 地址&#xff1a;https://www.mysql.com/cn/downloads/ 选择&#xff1a;MySQL Community (GPL) downloads 选择&#xff1a;MySQL Community Server 选择&#xff1a; 选择&#xff1a; 安装mysql &#xff08;8.0.30&#xff09; 1、以管理员身份 打开 命令行…

【实战ES】实战 Elasticsearch:快速上手与深度实践-3.2.3 案例:新闻搜索引擎的相关性优化

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 Elasticsearch新闻搜索引擎相关性优化实战3.2.3 案例&#xff1a;新闻搜索引擎的相关性优化项目背景1. 相关性问题诊断与分析1.1 初始查询DSL示例1.2 问题诊断矩阵1.3 性能基…

HCIA复习拓扑实验

一.拓扑图 二.需求 1.学校内部的HTTP客户端可以正常通过域名www.baidu.com访问到百度网络中HTTP服务器 2.学校网络内部网段基于192.168.1.0/24划分&#xff0c;PC1可以正常访问3.3.3.0/24网段&#xff0c;但是PC2不允许 3.学校内部路由使用静态路由&#xff0c;R1和R2之间两…

企业如何选择研发项目进度管理软件?盘点15款实用工具

这篇文章介绍了以下工具: 1. PingCode&#xff1b; 2. Worktile&#xff1b; 3. 腾讯 TAPD&#xff1b; 4. 华为 DevCloud&#xff1b; 5. 亿方云&#xff1b; 6. 阿里云效&#xff1b; 7. CODING 码云&#xff1b; 8. 明道云&#xff1b; 9. 进度猫&#xff1b; 10. 轻流等。 …

(二 十 二)趣学设计模式 之 备忘录模式!

目录 一、 啥是备忘录模式&#xff1f;二、 为什么要用备忘录模式&#xff1f;三、 备忘录模式的实现方式四、 备忘录模式的优缺点五、 备忘录模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;…

conda 配置新环境时package will be install 和 package will be download 的区别

install 和 download 的区别 package will be downloaded下的包&#xff1a;这一类显示的是需要从 conda 仓库或其他指定的源下载的软件包。这些软件包通常是 .tar.bz2、.tar.xz 或 .conda 格式的压缩包。这些包会被下载到本地缓存目录&#xff08;通常是 ~/.conda 或 C:\Users…

第本章:go 切片

注意&#xff1a; 切片必须要初始化 才能使用 &#xff0c;切片是引用类型 a :[]int{} // 这上叫始化 此时并没有申请内存 // 如果要追加值的话&#xff1a; append ints : append(a, 1, 2, 3)a : make([]int,5) // 声明切片类型var a []string //声明一…

关于AI数据分析可行性的初步评估

一、结论&#xff1a;可在部分环节嵌入&#xff0c;无法直接处理大量数据 1.非本地部署的AI应用处理非机密文件没问题&#xff0c;内部文件要注意数据安全风险。 2.AI&#xff08;指高规格大模型&#xff09;十分适合探索性研究分析&#xff0c;对复杂报告无法全流程执行&…

编程考古-Borland历史:《.EXE Interview》对Anders Hejlsberg关于Delphi的采访内容(中)

为了纪念Delphi在2002年2月14日发布的25周年(2020.2.12),这里有一段由.EXE杂志编辑Will Watts于1995年对Delphi首席架构师Anders Hejlsberg进行的采访记录。在这次采访中,Anders讨论了Delphi的设计与发展,以及即将到来的针对Windows 95的32位版本。 Q. 编译器引擎本身是用…

Manus+Ollama实现本地大模型部署和应用测试

这几天Manus即DeepSeek后又突然火爆&#xff0c;我也进行了跟踪测试&#xff0c;特记录一下分享给大家&#xff0c;目前来看&#xff0c;Manus的确是一个可以进行任务分解的自动化解决方案&#xff0c;将其他AI需要多次繁杂的迭代对话做了较大的改进&#xff0c;相当于用户抛出…

【Python 数据结构 9.树】

我装作漠视一切&#xff0c;其实我在乎的太多&#xff0c;但我知道抓得越紧越容易失去 —— 25.3.6 一、树的基本概念 1.树的定义 树是n个结点的有限集合&#xff0c;n0时为空树。当n大于0的时候&#xff0c;满足如下两个条件&#xff1a; ① 有且仅有一个特定的结点&#xff…

LLM 学习(二 完结 Multi-Head Attention、Encoder、Decoder)

文章目录 LLM 学习&#xff08;二 完结 Multi-Head Attention、Encoder、Decoder&#xff09;Self-Attention &#xff08;自注意力机制&#xff09;结构多头注意力 EncoderAdd & Norm 层Feed Forward 层 EncoderDecoder的第一个Multi-Head AttentionMasked 操作Teacher Fo…

计算机网络软考

1.物理层 1.两个主机之间发送数据的过程 自上而下的封装数据&#xff0c;自下而上的解封装数据&#xff0c;实现数据的传输 2.数据、信号、码元 码元就是数字通信里用来表示信息的基本信号单元。比如在二进制中&#xff0c;用高电平代表 “1”、低电平代表 “0”&#xff0c…

VBA 数据库同一表的当前行与其他行的主键重复判断实现方案

目的&#xff0c;判断是否主键重复&#xff0c;不重复则登录新数据&#xff0c;重复则不登录。 定义类型&#xff1a; DataRecord   tableName 表名   rowNumber 行号   columnName 列名   data 数据 想要实现的代码逻辑如下&#xff1a; 模拟数据库的登录过程。假设…

2025最新群智能优化算法:山羊优化算法(Goat Optimization Algorithm, GOA)求解23个经典函数测试集,MATLAB

一、山羊优化算法 山羊优化算法&#xff08;Goat Optimization Algorithm, GOA&#xff09;是2025年提出的一种新型生物启发式元启发式算法&#xff0c;灵感来源于山羊在恶劣和资源有限环境中的适应性行为。该算法旨在通过模拟山羊的觅食策略、移动模式和躲避寄生虫的能力&…

网络基础(一)【网络发展/认识协议/网络 VS 系统/以太网通信原理/重谈协议/网络中的地址管理】

网络基础&#xff08;一&#xff09; 1. 网络的发展2. 认识协议3. 网络 VS 系统4. 以太网通信原理5. 重谈协议6. 网络中的地址管理 1. 网络的发展 最开始时&#xff0c;计算机之间相互独立。 但是为了协作完成一些任务&#xff0c;就产生了计算机之间相互通讯的需求&#xff0c…

学习threejs,Animation、Core、CustomBlendingEquation、Renderer常量汇总

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️Animation常量汇总1.1.1 循…

常用无功功率算法的C语言实现(二)

0 前言 尽管数字延迟法和积分移相法在不间断采样的无功功率计算中得到了广泛应用,但它们仍存在一些固有缺陷。 对于数字延迟法而言,其需要额外存储至少1/4周期的采样点,在高采样频率的场景下,这对存储资源的需求不可忽视。而积分移相法虽然避免了额外的存储开销,但为了抑制…