从0开始的操作系统手搓教程24——完成我们的键盘驱动子系统
目录
所以,我们现来说说转义字符
我们需要如何处理扫描码
当键入的是双字符键时
当键入的是字母键时
下一篇
我们下面来看看我们的键盘驱动子系统是一个怎么个事情。
驱动程序,你可以认为是对硬件的一层封装。我们按照手册规格的规定姿势,封装好一套一套的流程,我们上层的软件想要使用这个硬件,就直接找这个驱动接口,一个调用完事。这就是一个驱动。
所以,我们现来说说转义字符
我们下面呢,就是要准备讨论一个重要的东西:叫转义字符。因为我们编写键盘驱动程序,就必须要理解键盘如何处理一部分不可见字符的。
我的意思是——我们的字符集中的字符,分为两个大类:可见的字符和不可见的字符。可见的字符太好说了,abcdefg....,不可见的字符,则是一些输入控制字符。我们如何输入控制字符呢?啊哈,转义字符嘛!
在 C 语言中有三种转义字符。
-
一般转义字符,'+单个字母'的形式。
-
八进制转义字符,'\0+三位八进制数字表示的 ASCII 码'的形式。
-
十六进制转义字符,'\x+两位十六进制数字表示的 ASCII 码'的形式。
我们实际上,就是将我们的字符的通码,转换成可见的ASCII字符,发送到上面需要的子程序。这样就完事了。
我们需要如何处理扫描码
当按下的键是可见字符时,屏幕上都会将其显示出来,比如按下了 a 键,屏幕上应该输出字符'a',给用户一个反馈,这样用户才觉得自己没按错,这是为打造好的用户体验最起码的素质。 当按下的键是控制字符时,我们应该做出相应的控制行为,并在屏幕上展现出这种行为,比如按下了backspace 键,咱们也应该在屏幕上让用户觉得光标所在处前面的字符被删掉了。
如果是一些用于操作方面的控制键,简称操作控制键,如<shift>、<ctrl>、<caps lock>,它通常是组合键,需要与其他键一起考虑,然后做出具体的行为展现,在键盘驱动中完成处理。 如果是一些用于字符方面的键,无论是可见字符,或是字符方面的控制键(简称字符控制键),如<backspace>,统统交给字符处理程序完成,比如咱们的 __ccos_putchar。还记得吗?__ccos_putchar能够处理 <backspace>,也就是'\b'。
所以,我们需要做的就是将操作控制键,转化成正确的含义传递给字符处理程序。也就是正确的ASCII码给我们的 __ccos_putchar。
我们首先定义一下keymap,不然的话
#ifndef KEYBOARD_MAPPINGS_H
#define KEYBOARD_MAPPINGS_H
#include "include/device/configs/keyboard_ascii.h"
// Key mappings table
static char keymap[[maybe_unused]][][2] = {/* ---------------------------------- *//* 0x00 */ {0, 0}, /* 0x01 */ {ESC, ESC}, /* 0x02 */ {'1', '!'}, /* 0x03 */ {'2', '@'}, /* 0x04 */ {'3', '#'}, /* 0x05 */ {'4', '$'}, /* 0x06 */ {'5', '%'}, /* 0x07 */ {'6', '^'}, /* 0x08 */ {'7', '&'}, /* 0x09 */ {'8', '*'}, /* 0x0A */ {'9', '('}, /* 0x0B */ {'0', ')'}, /* 0x0C */ {'-', '_'}, /* 0x0D */ {'=', '+'}, /* 0x0E */ {BACKSPACE, BACKSPACE}, /* 0x0F */ {TAB, TAB}, /* 0x10 */ {'q', 'Q'}, /* 0x11 */ {'w', 'W'}, /* 0x12 */ {'e', 'E'}, /* 0x13 */ {'r', 'R'}, /* 0x14 */ {'t', 'T'}, /* 0x15 */ {'y', 'Y'}, /* 0x16 */ {'u', 'U'}, /* 0x17 */ {'i', 'I'}, /* 0x18 */ {'o', 'O'}, /* 0x19 */ {'p', 'P'}, /* 0x1A */ {'[', '{'}, /* 0x1B */ {']', '}'}, /* 0x1C */ {ENTER, ENTER},/* 0x1D */ {CTRL_L_CHAR, CTRL_L_CHAR},/* 0x1E */ {'a', 'A'}, /* 0x1F */ {'s', 'S'}, /* 0x20 */ {'d', 'D'}, /* 0x21 */ {'f', 'F'}, /* 0x22 */ {'g', 'G'}, /* 0x23 */ {'h', 'H'}, /* 0x24 */ {'j', 'J'}, /* 0x25 */ {'k', 'K'}, /* 0x26 */ {'l', 'L'}, /* 0x27 */ {';', ':'}, /* 0x28 */ {'\'', '"'}, /* 0x29 */ {'`', '~'}, /* 0x2A */ {SHIFT_L_CHAR, SHIFT_L_CHAR}, /* 0x2B */ {'\\', '|'}, /* 0x2C */ {'z', 'Z'}, /* 0x2D */ {'x', 'X'}, /* 0x2E */ {'c', 'C'}, /* 0x2F */ {'v', 'V'}, /* 0x30 */ {'b', 'B'}, /* 0x31 */ {'n', 'N'}, /* 0x32 */ {'m', 'M'}, /* 0x33 */ {',', '<'}, /* 0x34 */ {'.', '>'}, /* 0x35 */ {'/', '?'},/* 0x36 */ {SHIFT_R_CHAR, SHIFT_R_CHAR}, /* 0x37 */ {'*', '*'}, /* 0x38 */ {ALT_L_CHAR, ALT_L_CHAR},/* 0x39 */ {' ', ' '}, /* 0x3A */ {CAPS_LOCK_CHAR, CAPS_LOCK_CHAR}
};
#endif
这个keymap,更多定义的是是否又跟shift进行组合的Keymap表,我的意思是——当我们只是嗯下A这个键的时候,正常出来的就是keymap[0x1e][0],然后,就是嗯下Shift的时候,我们正确的索引就是keymap[0x1e][1],这个没啥毛病,出来的是A。
在keyboard_ascii.h的中(当时名字取得不太好),存放的是我们的键盘控制相关的定义,请看下文
#ifndef KEYBOARD_ASCII_H
#define KEYBOARD_ASCII_H
/* Port and interrupt number for keyboard input */
#define KEYBOARD_BUF_PORT (0x60) // I/O port for keyboard buffer
#define KEYBOARD_INTERRUPT_N (0x21) // Keyboard interrupt number
/* ASCII control characters */
#define ESC ('\x1b') // Escape key
#define BACKSPACE ('\b') // Backspace key
#define TAB ('\t') // Tab key
#define ENTER ('\r') // Enter (carriage return)
#define DELETE ('\x7f') // Delete key
/* Invisible control characters (do not produce a visible output) */
#define INVISIBLE (0) // Represents an invisible keypress
#define CTRL_L_CHAR INVISIBLE // Left Control key
#define CTRL_R_CHAR INVISIBLE // Right Control key
#define SHIFT_L_CHAR INVISIBLE // Left Shift key
#define SHIFT_R_CHAR INVISIBLE // Right Shift key
#define ALT_L_CHAR INVISIBLE // Left Alt key
#define ALT_R_CHAR INVISIBLE // Right Alt key
#define CAPS_LOCK_CHAR INVISIBLE // Caps Lock key
/* Make codes for modifier keys (sent when key is pressed) */
#define SHIFT_L_MAKE (0x2A) // Left Shift key make code
#define SHIFT_R_MAKE (0x36) // Right Shift key make code
#define ALT_L_MAKE (0x38) // Left Alt key make code
#define ALT_R_MAKE (0xE038) // Right Alt key make code (extended)
#define CTRL_L_MAKE (0x1D) // Left Control key make code
#define CTRL_R_MAKE (0xE01D) // Right Control key make code (extended)
#define CAPS_LOCK_MAKE (0x3A) // Caps Lock key make code
/* Break codes for modifier keys (sent when key is released) */
#define ALT_R_BREAK (0xE0B8) // Right Alt key break code (extended)
#define CTRL_R_BREAK (0xE09D) // Right Control key break code (extended)
#endif
为了薄记我们的控制摁键的状态,笔者抽象了一个这样的结构体:
/* Defines a structure to record the status of special keys */
static struct {bool ctrl_status; // Indicates whether the Ctrl key is pressedbool shift_status; // Indicates whether the Shift key is pressedbool alt_status; // Indicates whether the Alt key is pressedbool caps_lock_status; // Indicates whether Caps Lock is activebool ext_scancode; // Indicates if the scancode starts with 0xe0
} key_state;
上面的结构体就是用来处理我们的键盘的状态用的。上面的注释上我说的很清楚了。
下面,让我们仔细瞧瞧看!我们改造后的键盘中断处理程序是如何的。
static void keyboard_intr_handler(void)
{// fetch the recordingsbool prev_shift_down = key_state.shift_status;bool prev_caps_lock = key_state.caps_lock_status;
bool break_code;uint16_t scancode = inb(KEYBOARD_BUF_PORT);
if (scancode == SCANCODE_EXT) {key_state.ext_scancode = true;return;}
我们首先读取8042上的缓存的键盘扫描码,我们第一件事,就是看看我们的这一次是不是e0,是e0说明我们的键盘产生了多个扫描码,马上结束中断,准备好薄记。退出中断处理子程序
if(key_state.ext_scancode){scancode = ((0xe000) | scancode);key_state.ext_scancode = false;}
我们判断是不是发生过0xe0的收到,收到的话,我们就要合并上一次的0xe0:scancode = ((0xe000) | scancode);,然后关闭标记,方便我们接受下一次的0xe0.
我们下一步就是判断,这一次是断码还是通码。我们检查一下字节的最高位:
// get break code
break_code = ((scancode & 0x0080) != 0);
如果,我们收到的是一个断码,就要准备处理这个字符了(用户结束了输入,我们准备好清理一部分状态)
if (break_code) {// Extract the make code by masking the break code bituint16_t make_code = (scancode &= 0xff7f);
// Update the status of control keys if they are releasedif (make_code == CTRL_L_MAKE || make_code == CTRL_R_MAKE) {key_state.ctrl_status = false;} else if (make_code == SHIFT_L_MAKE || make_code == SHIFT_R_MAKE) {key_state.shift_status = false;} else if (make_code == ALT_L_MAKE || make_code == ALT_R_MAKE) {key_state.alt_status = false;}
// Caps Lock does not toggle on release, so no update is needed herereturn;}
我们把最高位的1一清走,就得到了通码,为什么看通码呢?上一节想一想你松开Ctrl + A的Ctrl的,显然你就要结束全选全文了。那么,我们自然需要薄记——用户松开了Ctrl,不需要全选了。我们就要清理我们用来薄记的key_state的结构体的状态
我们的键盘上支支持到0x3a上,为了防止越界(其实更好的办法是取出来这个结构体的数组的大小做限制,但是笔者这里偷懒了,你可以自行尝试封装一个更好的判断)
走到下面,我们先要判断这个字符是不是非法的——我们看看,首先,我们要求这个扫描码必须在外面的处理范围内(还可以包含右ALT和右Ctrl键)
// Handle make codes for defined keysif ((scancode > 0x00 && scancode < 0x3b) || (scancode == ALT_R_MAKE) || (scancode == CTRL_R_MAKE) ){// handling inside}else{ccos_puts("unknown key\n"); // Handle undefined keys}
那些不是的,我们直接给一个提示就好了,你可以啥也不做,这取决于你。
下面准备处理了:主键盘区主要就是数字键和字母键,因此现在要考虑的是之前是否按下了<shift>键和<capslock>键,大伙儿知道,主键盘区中部分键有两个意义,当与 shift 配合使用时,表示键中上面的字符,为方便讨论,暂 称它们为双字符键。(因为不是shift的时候,表达的是其他的含义——举个例子,可能是1,2,3等等,这个你仔细看看你的键盘,凡是一个键盘上面挤着两个字符的都是双字字符)<shift>键和<capslock>键对双字符键和字母键的影响是不一样的.
当键入的是双字符键时
如果同时按下了<shift>键,则应该转换为数字键上面的那个符号,比如当前按下的是数字2,之前若按下 shift 未松手的话,现在应该将其转换为字符'@'。 <capslock>键是否开启,对双字符键无影响。
当键入的是字母键时
如果之前开启了<caps lock>键,则应该转换为大写字母。 如果之前同时按下了<shift>键不松手,但没有按下<capslock>键,则也应该转换为大写字母。 若之前同时按下了<capslock>键和<shift>键,<shift>键将<capslock>键的功能抵消,因此键入的是字母键应该转换为小写字母。
嗯,那这样说来,我们最终可以统一到shift键的处理上(别太麻烦,将大写锁定的作用也归结到shift变量上,这样事情简单一些)。所以请出一个变量叫shift。
-
若 shift 为 false,则表示 shift 为 0,这表示一维数组中第 0 个元素,即
keymap[通码][0]。 -
若 shift 为 true,则表示 shift 为 1,这表示一维数组中第 1 个元素,即
keymap[通码][1]。
bool shift =false; // Used to index characters from keymap based on Shift status
之后,我们处理这些双字字符:
-
数字 0~9、字符'-'、字符'='的通码是< 0x0e。
-
字符'`'的通码是 0x29。
-
字符'['的通码是 0x1a。
-
字符']'的通码是 0x1b。
-
字符'\'的通码是 0x2b。
-
字符';'的通码是 0x27。
-
字符'''的通码是 0x28。
-
字符','的通码是 0x33。
-
字符'.'的通码是 0x34。
-
字符'/'的通码是 0x35
// Special key ranges that require Shift to access additional charactersif ((scancode < 0x0e) || (scancode == 0x29) || (scancode == 0x1a) ||(scancode == 0x1b) || (scancode == 0x2b) || (scancode == 0x27) ||(scancode == 0x28) || (scancode == 0x33) || (scancode == 0x34) ||(scancode == 0x35)){if (prev_shift_down) {shift = true;} }else{// handling special keys}
如果是这些双字符键,并且按下了<shift>,那说明就是准备触发了第二列的索引,我们后面直接keymap[index][shift]的时候shift就是1嘛!
让我们看看else下的handling special keys的部分,实际上就是这个代码
// For alphabetic keys, Shift and Caps Lock together cancel each// otherif (prev_shift_down && prev_caps_lock) {shift = false;} else if (prev_shift_down || prev_caps_lock) {shift = true;} else {shift = false;}
我们如果if (shift_down_last && caps_lock_last),说明我们同时嗯下了shift和caps lock,那就抵消了。
如果其中任何一个被嗯下,那事情就很简单了: shift = true;,余下的情况就是啥也没嗯下,
准备爬表!我们获取表的offset,办法就是直接提取低8位。剩下的位都已经在上面的判断处理干净了
scancode &= 0x00ff;
char cur_char = keymap[scancode][shift]; // Retrieve the character from the keymap
如果我们是可见的字符(参考keyboard.ascii.h,笔者对一切不可见字符直接塞上了0,这个时候,如果我们键入了一个不可见的字符,cur_char索引的结果就是0)
// Add the character to the buffer if it is not nullif (cur_char) {__ccos_putchar(cur_char);return;}
直接输出这个字符!
当然,如果是控制不可见字符,我们就是准备薄记状态了,请看:
// Update the status of control keys for the next inputif (scancode == CTRL_L_MAKE || scancode == CTRL_R_MAKE) {key_state.ctrl_status = true;} else if (scancode == SHIFT_L_MAKE || scancode == SHIFT_R_MAKE) {key_state.shift_status = true;} else if (scancode == ALT_L_MAKE || scancode == ALT_R_MAKE) {key_state.alt_status = true;} else if (scancode == CAPS_LOCK_MAKE) {// Toggle Caps Lock status on each presskey_state.caps_lock_status = !key_state.caps_lock_status;}
完事!直接上电看看情况。

非常好!
代码
CCOperateSystem/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code at main · Charliechen114514/CCOperateSystem
https://github.com/Charliechen114514/CCOperateSystem/tree/main/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code
下一篇
从0开始的操作系统手搓教程25:使用环状缓冲区来让我们的键盘驱动真正的有作用起来-CSDN博客然而,上面的驱动程序,我相信不少朋友发现毛病了——那就是他是处理完一个字符之后,就迅速的扔掉,没法找到了。换而言之,我们需要一个缓冲区,让我们可以记住用户之前输入了什么。这不,环状缓冲区就来了。缓冲区就是为了解决生产者和消费者问题而存在的,想象一下。https://blog.csdn.net/charlie114514191/article/details/146105601
相关文章:
从0开始的操作系统手搓教程24——完成我们的键盘驱动子系统
目录 所以,我们现来说说转义字符 我们需要如何处理扫描码 当键入的是双字符键时 当键入的是字母键时 下一篇 我们下面来看看我们的键盘驱动子系统是一个怎么个事情。 驱动程序,你可以认为是对硬件的一层封装。我们按照手册规格的规定姿势࿰…...
git大文件传输报错
简述 git传输大于25M的文件时会报错,需要使用 Git LFS进行文件传输。 Git LFS(Large File Storage)是 GitHub 推荐的方式,可以管理大文件而不会影响 Git 性能。 操作流程 # 安装 Git LFS git lfs install# 将 PDF 文件添加到 G…...
基础玩转物联网-4G模块如何快速实现与MQTT服务器通信
目录 1 前言 2 环境搭建 2.1 硬件准备 2.2 软件准备 2.3 硬件连接 2.4 检查驱动 3 连接MQTT服务器 3.1 创建MQTT监听Topic 3.2 打开配置工具读取基本信息 3.3 设置连接参数进行数据交互 4 总结 1 前言 MQTT(Message Queuing Telemetry Transport)是一种轻…...
使用Beanshell前置处理器对Jmeter的请求body进行加密
这里我们用HmacSHA256来进行加密举例: 步骤: 1.先获取请求参数并对请求参数进行处理(处理成String类型) //处理请求参数的两种方法: //方法一: //获取请求 Arguments args sampler.getArguments(); //转…...
Python入门3:类与面对对象
目录 类 一、类的概念 二、类的定义和使用 2.1 类的定义 2.2 实例化对象 三、类的属性和方法 3.1 属性 属性的类型: 补充--私有属性 属性的操作: 3.2 方法 方法的类型: 补充--私有方法 方法的操作 四、面对过程和面对对象 …...
mac本地部署Qwq-32b记录
导语 昨天看到阿里开源了Qwq-32b,号称性能可以媲美Deepseek-R1。今天晚上有空就在Mac上折腾了一下,使用ollma进行了部署,效果感觉还不错,特此记录。 环境 硬件 型号:Macbook M1 Pro 14寸内存:512G 环境…...
【病毒分析】熊猫烧香病毒分析及其查杀修复
目录 前言 一、样本概况 1.1 样本信息 1.2 测试环境及工具 1.3 分析目标 二、具体行为分析 2.1 主要行为 2.1.1 恶意程序对用户造成的危害 2.2 恶意代码分析 2.2.1 加固后的恶意代码树结构图(是否有加固) 2.2.2 恶意程序的代码分析片段 三、解决方案(或总结) 3.1 …...
【语料数据爬虫】Python实现将Json语料数据转换成Word文档
前言 本文是该专栏的第1篇,后面会持续分享Python爬虫采集各种语料数据的的干货知识,值得关注。 本专栏为笔者精心推出的“语料数据”爬虫专栏,特别适合需要写作素材的同学,该专栏文章以采集最新的“语料数据”为主,最终篇幅将涵盖【百万级语料数据】库。 值得一提的是,…...
警惕AI神话破灭:深度解析大模型缺陷与禁用场景指南
摘要 当前AI大模型虽展现强大能力,但其本质缺陷可能引发系统性风险。本文从认知鸿沟、数据困境、伦理雷区、技术瓶颈四大维度剖析大模型局限性,揭示医疗诊断、法律决策等8类禁用场景,提出可信AI建设框架与用户防护策略。通过理论分析与实操案…...
做到哪一步才算精通SQL
做到哪一步才算精通SQL-Structured Query Language 数据定义语言 DDL for StructCREATE:用来创建数据库、表、索引等对象ALTER:用来修改已存在的数据库对象DROP:用来删除整个数据库或者数据库中的表TRUNCATE:用来删除表中所有的行…...
leetcode454 四数相加
四数相加Ⅱ的解法可以将四数分为两组,即“分组 哈希”: 初始化哈希表。 分组:nums1 和 nums2 一组,nums3 和 nums4 一组。 分别对 nums1 和 nums2 进行遍历,将所有 nums1 和 nums2 的值的和作为哈希表的 key&#x…...
RoboVQA:机器人多模态长范围推理
23 年 11 月来自 Google Deepmind 的论文“RoboVQA: Multimodal Long-Horizon Reasoning for Robotics”。 本文提出一种可扩展、自下而上且本质多样化的数据收集方案,该方案可用于长期和中期的高级推理,与传统的狭窄自上而下的逐步收集相比,…...
C 语言数据结构(二):顺序表和链表
目录 1. 线性表 2. 顺序表 2.1 概念及结构 2.1.1 静态顺序表(不常用) 2.1.2 动态顺序表(常用) 编辑 2.2 练习 2.2.1 移除元素 2.2.2 删除有序数组中的重复项 2.2.3 合并两个有序数组 2.3 顺序表存在的问题 3. 链表 …...
无公网IP也能远程控制Windows:Linux rdesktop内网穿透实战
文章目录 前言1. Windows 开启远程桌面2. Linux安装rdesktop工具3. Win安装Cpolar工具4. 配置远程桌面地址5. 远程桌面连接测试6. 设置固定远程地址7. 固定地址连接测试 前言 如今远程办公已经从一种选择变成了许多企业和个人的必修课,而如何在Linux系统上高效地访…...
uniapp+Vue3 开发小程序的下载文件功能
小程序下载文件,可以先预览文件内容,然后在手机上打开文件的工具中选择保存。 简单示例:(复制到HBuilder直接食用即可) <template><view class"container-detail"><view class"example…...
blazemeter工具使用--用于自动生成jmeter脚本并进行性能测试
1、安装blazemeter(网上有很多详情的教程) 2、开始录制:设置号你的文件名称后开始录制 3、录制完成后保存为jmeter(jmx)文件 4、在jmeter中打开文件 5、添加一个后置处理器:查看结果树,后运行看看能否成功…...
【实战ES】实战 Elasticsearch:快速上手与深度实践-7.2.2自动扩缩容策略(基于HPA)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 7.2.2 基于HPA的Elasticsearch自动扩缩容深度实践1. 云原生时代弹性伸缩的核心价值1.1 业务负载特征分析1.2 传统扩缩容方式的局限性 2. HPA核心机制深度解析2.1 HPA工作原理…...
通义万相2.1:开启视频生成新时代
文章摘要:通义万相 2.1 是一款在人工智能视频生成领域具有里程碑意义的工具,它通过核心技术的升级和创新,为创作者提供了更强大、更智能的创作能力。本文详细介绍了通义万相 2.1 的背景、核心技术、功能特性、性能评测、用户反馈以及应用场景…...
如何用HTML5 Canvas实现电子签名功能✍️
🤖 作者简介:水煮白菜王,一位资深前端劝退师 👻 👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。 感谢支持💕💕&a…...
区块链中的数字签名:安全性与可信度的核心
数字签名是区块链技术的信任基石,它像区块链世界的身份证和防伪标签,确保每一笔交易的真实性、完整性和不可抵赖性。本文会用通俗的语言,带你彻底搞懂区块链中的数字签名! 文章目录 1. 数字签名是什么?从现实世界到区块…...
RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)
RabbitMQ高级特性 RabbitMQ 高级特性解析:RabbitMQ 消息可靠性保障 (上)-CSDN博客 RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)-CSDN博客 引言 RabbitMQ 作为一款强大的消息队列中间件ÿ…...
表格columns拼接两个后端返回的字段(以umi框架为例)
在用组件对前端项目进行开发时,我们会遇到以下情况:项目原型中有取值范围这个表字段,需要存放最小取值到最大取值。 而后端返回给我们的数据是返回了一个最小值和一个最大值, 在columns中我们需要对这两个字段进行拼接࿰…...
sparkTTS window 安装
SparkTTS 的简介 Spark-TTS是一种基于SpardAudio团队提出的 BiCodec 构建的新系统,BiCodec 是一种单流语音编解码器,可将语音策略性地分解为两种互补的标记类型:用于语言内容的低比特率语义标记和用于说话者特定属性的固定长度全局标记。这种…...
【K8S系列】深入探究Kubernetes中查看日志的方法
在Kubernetes(简称K8s)的世界里,日志是诊断和排查问题的关键线索。无论是应用程序的运行状态、错误信息,还是系统的健康状况,都能从日志中找到蛛丝马迹。本文将详细介绍在K8s中查看日志的各种方法,从基础的…...
How to install nodejs with nvm on Linux mint 22.1
nvm是nodejs官方用于管理nodejs多版本环境的一个工具 ,今天,我带领大家基于nvm完成nodejs在Linux mint 22.1上的安装。 考虑到Linux mint 22.1是基于ubuntu 24.04.1 LTS的,所以,这里的安装也完全适用于nodejs在nodejs上的安装。 …...
JmeterHttp请求头管理出现Unsupported Media Type问题解决
JmeterHttp请求头管理出现Unsupported Media Type问题解决 大多数的app与pc端压测的时候都会出现这种情况 当我们在jemter测试当中当中遇见Unsupported Media Type,有一种可能就是我们请求的网页的content-Type的类型与我们测试的时候的类型不一致 解决方法 可以添…...
十大数据科学Python库
十大数据科学Python库 1、NumPy:脊髓2、Pandas:数据操纵专家3、Matplotlib:艺术之魂4、Scikit-Learn:瑞士军刀5、TensorFlow:聪明的家伙6、PyTorch:叛逆者7、Selenium:操纵大师8、NLTKÿ…...
LabVIEW伺服阀高频振动测试
在伺服阀高频振动测试中,闭环控制系统的实时性与稳定性至关重要。针对用户提出的1kHz控制频率需求及Windows平台兼容性问题,本文重点分析NI PCIe-7842R实时扩展卡的功能与局限性,并提供其他替代方案的综合对比,以帮助用户选择适合…...
解决asp.net mvc发布到iis下安全问题
解决asp.net mvc发布到iis下安全问题 环境信息1.The web/application server is leaking version information via the "Server" HTTP response2.确保您的Web服务器、应用程序服务器、负载均衡器等已配置为强制执行Strict-Transport-Security。3.在HTML提交表单中找不…...
CSS-基础选择器,字体属性,文本属性介绍
一、CSS 简介 CSS 是层叠样式表 ( Cascading Style Sheets ) 的简称. 有时我们也会称之为 CSS 样式表或级联样式表。 CSS 是也是一种标记语言 CSS 主要用于设置 HTML 页面中的文本内容(字体、大小、对齐方式等)、图片的外形(宽高&a…...
