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

手写简易操作系统(十七)--编写键盘驱动

前情提要

上一节我们实现了锁与信号量,这一节我们就可以实现键盘驱动了,访问键盘输入的数据也属于临界区资源,所以需要锁的存在。

一、键盘简介

之前的 ps/2 键盘使用的是中断驱动的,在当时,按下键盘就会触发中断,引导操作系统去处理这个按键行文。但是当今的usb键盘,使用的是轮询机制,cpu会定时访问键盘看有没有按下键盘。

我个人认为这是cpu技术的进步导致的,在之前,cpu的频率比较低,使用轮询可能会导致漏掉用户按键的行为。但是在今天,cpu的主频已经非常高了,处理一个按键行为就触发中断,这个开销太大了,而且轮询的频率也上来了,现在每秒访问几千次对电脑一点影响都没有,所以现在大多采用了轮询机制。

不过据说中断驱动的还是比较快,现在一些电竞主板还是支持ps/2的接口,这个未经论证。

1.1、键盘的通码与断码

键盘的状态要么是按下,要么是弹起,因此一个键便有两个编码,按键按下时的编码叫做通码,键盘上的触电接通了电路,使硬件产生了一个编码,故此通码叫makecode。按键在被按住不松手时会持续产生相同的码,直到按键被松开时才终止,因此按键被松开弹起时产生的编码叫断码,也就是电路被断开了,不再持续产生码了,故断码也称为breakcode。

无论是按下键,或是松开键,当键的状态改变后,键盘中的8048芯片把按键对应的扫描码(通码或断码)发送到主板上的8042芯片,由8042处理后保存在自己的寄存器中,然后向8259A发送中断信号,这样处理器便去执行键盘中断处理程序,将8042处理过的扫描码从它的寄存器中读取出来,继续进行下一步处理。

1.2、键盘扫描码

键的扫描码是由键盘中的键盘编码器决定的,不同的编码方案便是不同的键盘扫描码,也就是说,相同的键在不同的编码方案下产生的通码和断码也是不同的。

根据不同的编码方案,键盘扫描码有三套,分别称为scan code set 1、scan code set 2、scan code set 3。

其中scan code set 1是XT键盘用的扫描码,这个历史就比较久远了。scan code set 2是AT键盘的扫描码,这个键盘和我们当今的键盘也不是很一样,但是已经比较接近了。scan code set 3是IBM PS/2系列高端计算机所用的键盘上,IBM蓝色巨人现在都凉了,这个键盘也就很少看到了。

第二套键盘扫描码几乎是目前所使用的键盘的标准,因此大多数键盘向8042发送的扫描码都是第二套扫描码。但是难免有别的键盘,所以才会出现8042这个芯片,这个芯片做一个中间层,为了兼容第一套键盘扫描码对应的中断处理程序,不管键盘用的是何种键盘扫描码,当键盘将扫描码发送到8042后,都由8042转换成第一套扫描码,我们再从8042中读取扫描码。

这里我们给出常用键位的扫描码(这里的扫描码就是通码,加0x80就是断码)

按键扫描码按键扫描码按键扫描码
Esc0x01F10x3BF20x3C
F30x3DF40x3EF50x3F
F60x40F70x41F80x42
F90x43F100x44F110x57
F120x58PrintSc0x37ScrollLk0x46
Pause/Brk0x45`0x2910x02
20x0330x0440x05
50x0660x0770x08
80x0990x0A00x0B
-0x0C=0x0DBackspace0x0E
Tab0x0FQ0x10W0x11
E0x12R0x13T0x14
Y0x15U0x16I0x17
O0x18P0x19[0x1A
]0x1B|0x2BCapsLock0x3A
A0x1ES0x1FD0x20
F0x21G0x22H0x23
J0x24K0x25L0x26
;0x270x28Enter0x1C
Shift左0x2AZ0x2CX0x2D
C0x2EV0x2FB0x30
N0x31M0x32,0x33
.0x34/0x35Shift右0x36
Ctrl左0x1DWin左0xE0Alt左0x38
Space0x39Alt右0xE038Win右0xE0
Menu0xE0Ctrl右0xE01D

问:为什么会有通码和断码,通码不就够了嘛

**答:**如果按一个组合键的话,比如ctrl+a,是先按下ctrl,再按a,再松开ctrl,再松开a。如果没有断码,我们无法判断ctrl是否松开。

1.3、键盘的芯片

和键盘相关的芯片只有8042和8048,它们都是独立的处理器,都有自己的寄存器和内存。Intel 8048芯片或兼容芯片位于键盘中,它是键盘编码器,Intel 8042芯片或兼容芯片被集成在主板上的南桥芯片中,它是键盘控制器,也就是键盘的IO接口,因此它是8048的代理,也是前面所得到的处理器和键盘的“中间层”。我们只需要学习8042就够了

他的端口如下

寄存器端口读写
Output Buffer(输出缓冲区)0x60
Input Buffer(输入缓冲区)0x60
Status Register(状态寄存器)0x64
Control Register(控制寄存器)0x64

状态寄存器8位宽度的寄存器,只读,反映8048和8042的内部工作状态。各位意义如下。

(1)位0:置1时表示输出缓冲区寄存器已满,处理器通过in指令读取后该位自动置0。
(2)位1:置1时表示输入缓冲区寄存器已满,8042将值读取后该位自动置0。
(3)位2:系统标志位,最初加电时为0,自检通过后置为1。
(4)位3:置1时,表示输入缓冲区中的内容是命令,置0时,输入缓冲区中的内容是普通数据。
(5)位4:置1时表示键盘启用,置0时表示键盘禁用。
(6)位5:置1时表示发送超时。
(7)位6:置1时表示接收超时。
(8)位7:来自8048的数据在奇偶校验时出错。

8位宽度的寄存器,只写,用于写入命令控制字。每个位都可以设置一种工作方式,意义如下。

(1)位0:置1时启用键盘中断。
(2)位1:置1时启用鼠标中断。
(3)位2:设置状态寄存器的位2。
(4)位3:置1时,状态寄存器的位4无效。
(5)位4:置1时禁止键盘。
(6)位5:置1时禁止鼠标。
(7)位6:将第二套键盘扫描码转换为第一套键盘扫描码。
(8)位7:保留位,默认为0。

二、环形队列

键盘中断的数据是放在队列中的,等待其他线程的读取。如果我们之前做过关于软件相关的工作,很容易理解这个概念,就是buffer,缓冲区。因为我们是一直在输入的,所以这里设计成了环形队列。

我们看一下环形队列的数据结构

#define bufsize 256/* 环形队列 */
struct ioqueue {// 生产者消费者问题struct lock lock;// 生产者,缓冲区不满时就继续往里面放数据struct task_struct* producer;// 消费者,缓冲区不空时就继续从往里面拿数据struct task_struct* consumer;char buf[bufsize];			// 缓冲区大小int32_t head;			    // 队首,数据往队首处写入int32_t tail;			    // 队尾,数据从队尾处读出
};

这个就很明朗了。一个生产者一个消费者,生产者向buf中添加数据,消费者从buf中取出数据,为了防止buf中的数据出错,生产者和消费者同时只能有一个可以访问到buf。如果buf中数据满了,生产者就不能放了,此时阻塞生产者,如果buf中数据为空,消费者就不能拿了,此时阻塞消费者。

我们看一下具体的实现

/* 初始化io队列ioq */
void ioqueue_init(struct ioqueue* ioq) {lock_init(&ioq->lock);                 // 初始化io队列的锁ioq->producer = ioq->consumer = NULL;  // 生产者和消费者置空ioq->head = ioq->tail = 0;             // 队列的首尾指针指向缓冲区数组第0个位置
}/* 返回pos在缓冲区中的下一个位置值 */
static inline int32_t next_pos(int32_t pos) {return (pos + 1) % bufsize;
}/* 判断队列是否已满 */
bool ioq_full(struct ioqueue* ioq) {return next_pos(ioq->head) == ioq->tail;
}/* 判断队列是否已空 */
bool ioq_empty(struct ioqueue* ioq) {return ioq->head == ioq->tail;
}/* 使当前生产者或消费者在此缓冲区上等待 */
static void ioq_wait(struct task_struct** waiter) {// 二级指针不为空,指向的pcb指针地址为空ASSERT(*waiter == NULL && waiter != NULL);*waiter = running_thread();thread_block(TASK_BLOCKED);
}/* 唤醒waiter */
static void wakeup(struct task_struct** waiter) {// 二级指针指向不为空ASSERT(*waiter != NULL);thread_unblock(*waiter);*waiter = NULL;
}/* 消费者从ioq队列中获取一个字符 */
char ioq_getchar(struct ioqueue* ioq) {// 若缓冲区(队列)为空,把消费者ioq->consumer记为当前线程自己,等待生产者唤醒while (ioq_empty(ioq)) {lock_acquire(&ioq->lock);ioq_wait(&ioq->consumer);lock_release(&ioq->lock);}char byte = ioq->buf[ioq->tail];	  // 从缓冲区中取出ioq->tail = next_pos(ioq->tail);	  // 把读游标移到下一位置if (ioq->producer != NULL) {wakeup(&ioq->producer);		      // 唤醒生产者}return byte;
}/* 生产者往ioq队列中写入一个字符byte */
void ioq_putchar(struct ioqueue* ioq, char byte) {// 若缓冲区(队列)已经满了,把生产者ioq->producer记为自己,等待消费者线程唤醒自己while (ioq_full(ioq)) {lock_acquire(&ioq->lock);ioq_wait(&ioq->producer);lock_release(&ioq->lock);}ioq->buf[ioq->head] = byte;      // 把字节放入缓冲区中ioq->head = next_pos(ioq->head); // 把写游标移到下一位置if (ioq->consumer != NULL) {wakeup(&ioq->consumer);      // 唤醒消费者}
}

我们看一下后面两个函数,waitwakeup,这两个函数,这两个函数传入的是一个pcb指针的地址,所以这里是一个二级指针。所以无论是阻塞还是解除阻塞都是取这个二级指针的地址,也就得到了pcb指针。这里对于不熟悉指针的人来说可能会有点扰。

三、键盘驱动

#define KBD_BUF_PORT 0x60	 // 键盘buffer寄存器端口号为0x60/* 用转义字符定义部分控制字符 */
#define esc		    '\033'	 // 八进制表示字符,也可以用十六进制'\x1b'
#define backspace	'\b'
#define tab		    '\t'
#define enter		'\r'
#define delete		'\177'	 // 八进制表示字符,十六进制为'\x7f'/* 以上不可见字符一律定义为0 */
#define char_invisible	0
#define ctrl_l_char	    char_invisible
#define ctrl_r_char	    char_invisible
#define shift_l_char    char_invisible
#define shift_r_char	char_invisible
#define alt_l_char	    char_invisible
#define alt_r_char	    char_invisible
#define caps_lock_char	char_invisible/* 定义控制字符的通码和断码 */
#define shift_l_make	0x2a
#define shift_r_make 	0x36 
#define alt_l_make   	0x38
#define alt_r_make   	0xe038
#define alt_r_break   	0xe0b8
#define ctrl_l_make  	0x1d
#define ctrl_r_make  	0xe01d
#define ctrl_r_break 	0xe09d
#define caps_lock_make 	0x3astruct ioqueue kbd_buf;	   // 定义键盘缓冲区/* 定义以下变量记录相应键是否按下的状态,* ext_scancode用于记录makecode是否以0xe0开头 */
static bool ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;/* 以通码make_code为索引的二维数组 */
static char keymap[][2] = {/* 扫描码   未与shift组合  与shift组合*//* ---------------------------------- *//* 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}/*其它按键暂不处理*/
};/* 键盘中断处理程序 */
static void intr_keyboard_handler(void) {/* 这次中断发生前的上一次中断,以下任意三个键是否有按下 */bool ctrl_down_last = ctrl_status;bool shift_down_last = shift_status;bool caps_lock_last = caps_lock_status;uint16_t scancode = inb(KBD_BUF_PORT);// 若扫描码是e0开头的, 结束此次中断处理函数,等待下一个扫描码进来if (scancode == 0xe0) {ext_scancode = true;    // 打开e0标记return;}// 如果上次是以0xe0开头,将扫描码合并if (ext_scancode) {scancode = ((0xe000) | scancode);ext_scancode = false;   // 关闭e0标记}// 若是断码(按键弹起时产生的扫描码)if ((scancode & 0x0080) != 0) {// 获得相应的通码uint16_t make_code = (scancode &= 0xff7f);// 若是任意以下三个键弹起了,将状态置为falseif (make_code == ctrl_l_make || make_code == ctrl_r_make) {ctrl_status = false;}else if (make_code == shift_l_make || make_code == shift_r_make) {shift_status = false;}else if (make_code == alt_l_make || make_code == alt_r_make) {alt_status = false;}// 若是其他非控制键位,不需要处理,那些键位我们只需要知道通码return;}// 若是通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_codeelse if ((scancode > 0x00 && scancode < 0x3b) || (scancode == alt_r_make) || (scancode == ctrl_r_make)) {// keymap的二维索引bool shift = false;// 按下的键不是字母if ((scancode < 0x0e) || (scancode == 0x29) || \(scancode == 0x1a) || (scancode == 0x1b) || \(scancode == 0x2b) || (scancode == 0x27) || \(scancode == 0x28) || (scancode == 0x33) || \(scancode == 0x34) || (scancode == 0x35)) {  if (shift_down_last) {shift = true;}}// 如果按下的键是字母,需要和CapsLock配合else {if (shift_down_last && caps_lock_last) {      // 如果shift和capslock同时按下shift = false;}else if (shift_down_last || caps_lock_last) { // 如果shift和capslock任意被按下shift = true;}else {shift = false;}}// 将扫描码的高字节置0,主要是针对高字节是e0的扫描码.uint8_t index = (scancode &= 0x00ff);// 在数组中找到对应的字符char cur_char = keymap[index][shift];// 如果cur_char不为0,也就是ascii码为除'\0'外的字符就加入键盘缓冲区中if (cur_char) {// 如果ctrl按下,且输入的字符为‘l’或者‘u’,那就保存为 cur_char-‘a’,主要是‘a’前面26位没啥用if ((ctrl_down_last && cur_char == 'l') || (ctrl_down_last && cur_char == 'u')) {cur_char -= 'a';}// 如果缓冲区未满,就将其加入缓冲区if (!ioq_full(&kbd_buf)) {ioq_putchar(&kbd_buf, cur_char);}return;}// 记录本次是否按下了下面几类控制键之一,供下次键入时判断组合键if (scancode == ctrl_l_make || scancode == ctrl_r_make) {ctrl_status = true;}else if (scancode == shift_l_make || scancode == shift_r_make) {shift_status = true;}else if (scancode == alt_l_make || scancode == alt_r_make) {alt_status = true;}// 这里注意,大写的锁定键是取反else if (scancode == caps_lock_make) {caps_lock_status = !caps_lock_status;}}else {put_str("unknown key\n");}
}/* 键盘初始化 */
void keyboard_init() {put_str("keyboard init start\n");ioqueue_init(&kbd_buf);register_handler(0x21, intr_keyboard_handler);put_str("keyboard init done\n");
}

键盘驱动就稍显复杂一点,主要是涉及到了shiftctrlaltcaplock这些个控制键,这些键位是否按下所表示的通码断码是不一样的。这里就是处理字符,相信大家看代码就可以看明白。

四、仿真

我们创建一个线程,键盘输入什么,打印什么

image-20240325171327961

结束语

本节我们编写了键盘驱动以及其使用的环形队列数据结构。下一节我们将实现一个用户进程,即特权级为3的进程。

老规矩,代码地址为 https://github.com/lyajpunov/os

相关文章:

手写简易操作系统(十七)--编写键盘驱动

前情提要 上一节我们实现了锁与信号量&#xff0c;这一节我们就可以实现键盘驱动了&#xff0c;访问键盘输入的数据也属于临界区资源&#xff0c;所以需要锁的存在。 一、键盘简介 之前的 ps/2 键盘使用的是中断驱动的&#xff0c;在当时&#xff0c;按下键盘就会触发中断&a…...

springboot中基于RestTemplate 类 实现调用第三方API接口【POST版本】

https://blog.csdn.net/Drug_/article/details/135111675 这一篇的升级版 还是先配置文件 package com.init.config;import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.clie…...

编程器固件修改教程

首发csdn&#xff0c;转载请说明出处&#xff0c;保留一切权益。 关于编程器固件 所谓编程器固件是用编程器读取嵌入式设备的FLASH存储数据生成的文件&#xff0c;类似于直接用工具复制整个硬盘 编程器固件与普通固件的差异 编程器固件是用特定的结构(按顺序、大小)将一些文件系…...

Python从原Excel表中抽出数据存入同一文件的新的Sheet(附源码)

python读取excel数据。Python在从原Excel表中抽出数据并存储到同一文件的新的Sheet中的功能&#xff0c;充分展示了其在数据处理和自动化操作方面的强大能力。这一功能不仅简化了数据迁移的过程&#xff0c;还提高了数据处理的效率&#xff0c;为数据分析和管理工作带来了极大的…...

计算机网络实验六:路由信息协议RIP

目录 6 实验六:路由信息协议RIP 6.1 实验目的 6.2 实验步骤 6.2.1 构建网络拓扑、配置各网络设备 6.2.2 网络功能验证测试 6.3 实验总结 6 实验六:路由信息协议RIP 6.1 实验目的 (1)学习RIP协议的工作原理和特点 (2)学习如何选择最短路径路由。 (3)进一步掌握…...

MySQL数据库备份策略与实践详解

目录 引言 一、MySQL数据库备份的重要性 &#xff08;一&#xff09;数据丢失的原因 &#xff08;二&#xff09;数据丢失的后果 二、MySQL备份类型 &#xff08;一&#xff09;根据数据库状态 &#xff08;二&#xff09;根据数据的完整性 &#xff08;三&#xff09;…...

String类相关oj练习

前言&#xff1a; 此处练习对应博客文章&#xff1a;公主&#xff0c;王子&#xff0c;请点击​​​​​​​ 1.第一次只出现一次的字符 做题首先看清要求和提示&#xff1a; 给定一个字符串 s &#xff0c;找到 它的第一个不重复的字符&#xff0c;并返回它的索引 。如果不…...

【Linux】进程实践项目 —— 自主shell编写

送给大家一句话&#xff1a; 不管前方的路有多苦&#xff0c;只要走的方向正确&#xff0c;不管多么崎岖不平&#xff0c;都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》 自主shell命令编写 1 前言2 项目实现2.1 创建命令行2.2 获取命令2.3 分割命令2.4 运行命令 3 源代码…...

基于SpringBoot和Vue的学生笔记共享平台的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的学生笔记共享平台的设计与实现 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&…...

C++心决之命名空间、重载函数和引用

目录 1. C关键字(C98) 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C输入&输出 4. 缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 5. 函数重载 5.1 函数重载概念 5.2 C支持函数重载的原理--名字修饰(name Mangling) 6. 引用 6.1 引用概念 6.2 引用特性…...

higress使用了解

higress使用了解 了解下 http-echo、httpbin 镜像使用 未按文档实际搭建&#xff0c;但大致是这样 官方文档&#xff1a;https://higress.io/zh-cn/docs/overview/what-is-higress 了解&#xff1a;利用sealos快速安装kubernetes集群&#xff1a;https://note.youdao.com/s…...

Swagger3探索之游龙入海

引言 后端开发中常用的接口调用工具一般使用Postman、ApiPost工具&#xff0c;但后期需要与前端联调&#xff0c;要补充接口文档花费大量时间&#xff0c;此时Swagger3应运而生&#xff0c;大大提高沟通交流的效率。 引用依赖 <!-- Swagger3 调用方式 http://ip:port/swa…...

javaWeb项目-学生考勤管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JAVA技术 JavaSc…...

云备份项目认识、环境搭建以及所使用的库的介绍

一、云备份认识 将本地计算机一个受监管的文件夹的文件上传到服务器中&#xff0c;有服务器组织&#xff0c;客户端可以通过网页将文件查看并且下载下来&#xff0c;下载过程支持断点续传功能&#xff0c;并且服务器会对上传的文件进行热点管理&#xff0c;长时间没人访问的文…...

汇编语言第四版-王爽第2章 寄存器

二进制左移四位&#xff0c;相当于四进制左移一位。 debug命令实操&#xff0c;win11不能启动&#xff0c;需要配置文件 Windows64位系统进入debug模式_window10系统64位怎么使用debugger-CSDN博客...

MoonBit MeetUp回顾——张正、宗喆:编程语言在云原生与区块链领域的技术探索

宗喆和张正分别给我们带了 KCL 相关的最新进展&#xff0c;由蚂蚁集团开发的 Rust 编写的开源 DSL&#xff0c;目标是优化云原生策略配置和用户体验。它通过引入动态配置管理、配置校验和基础设施抽象等核心概念&#xff0c;解决开发者认知负担、配置膨胀和标准化工具缺乏的问题…...

云原生靶场kebernetesGoat、Metarget

靶场 文章目录 靶场kebernetesGoat靶场安装Docker in DockerSSRF漏洞容器逃逸到主系统Docker CIS 基线分析Kubernetes CIS 安全基线分析分析被部署挖矿软件的容器镜像获取环境信息Hidden in layersRBAC最低权限配置错误使用 Sysdig Falco 进行运行时安全监控和检测 Metarget ke…...

【3D目标检测】Det3d—SE-SSD模型训练(前篇):KITTI数据集训练

SE-SSD模型训练 1 基于Det3d搭建SE-SSD环境2 自定义数据准备2.1 自定义数据集标注2.2 训练数据生成2.3 数据集分割 3 训练KITTI数据集3.1 数据准备3.2 配置修改3.3 模型训练 1 基于Det3d搭建SE-SSD环境 Det3D环境搭建参考&#xff1a;【3D目标检测】环境搭建&#xff08;OpenP…...

k8s1.28.8版本安装prometheus并持久化数据

本文参考 [k8s安装prometheus并持久化数据_/prometheus-config-reloader:-CSDN博客](https://blog.csdn.net/vic_qxz/article/details/119598466)前置要求: 已经部署了NFS或者其他存储的K8s集群. 这里注意networkpolicies网络策略问题&#xff0c;可以后面删除这个策略&#x…...

Mybatis-特殊SQL的执行

1. 模糊查询 在MyBatis中进行模糊查询时&#xff0c;有以下三种常见的实现方式&#xff1a; 1.1. 错误示范 先来个准备操作&#xff0c;并做一个错误示例 根据姓名&#xff0c;模糊查询用户&#xff0c;(x小x) 更新数据表 SQLMapper.java package com.sakurapaid.mybatis3…...

金融衍生品市场

金融衍生品市场 衍生金融品的作用衍生金融工具远期合约期货合约期权 衍生金融品的作用 套期保值&#xff08;Hedging&#xff09; 组合多头头寸(long position)与空头头寸(short position)例&#xff1a;股票与股指期货 投机 衍生金融工具 远期合约 定义&#xff1a;在将来…...

2、Cocos Creator 下载安装

Cocos Creator 从 v2.3.2 开始接入了全新的 Dashboard 系统&#xff0c;能够同时对多版本引擎和项目进行统一升级和管理&#xff01;Cocos Dashboard 将做为 Creator 各引擎统一的下载器和启动入口&#xff0c;方便升级和管理多个版本的 Creator。还集成了统一的项目管理及创建…...

Docker版本:18.06.1安装

1、操作系统&#xff1a;CentOS 7.5以上 2、Docker版本&#xff1a;18.06.1 1、解压 tar -xvf docker-18.06.1-ce.tgz2、将解压出来的docker文件内容移动到 /usr/bin/ 目录下 cp docker/* /usr/bin/3、将docker注册为service vim /etc/systemd/system/docker.service将下列…...

记 SpringBoot 使用@RequestBody 接收不到参数

POST请求&#xff0c;前端传的参数名字跟后端规定的参数一样。但是通过RequestBody注解接收的参数始终为NULL&#xff01; //实体类中属性没有用驼峰命名 private String SubscribeID; /*** 标题*/ private String Title;解决方案&#xff1a; 1、字段上使用JsonProperty(valu…...

unity 打包安卓错误汇集

Failed to find target with hash string "android-34’ in: D:Pr 他说找不到sdk34level的我用as打开后卸载又重装&#xff0c;最后解决了 我放到Plugins/Android/下面的Java代码没有被编译 这个不知道为什么。我故意把代码写的有问题&#xff0c;会报错那种&#xff…...

C语言-文件操作

&#x1f308;很高兴可以来阅读我的博客&#xff01;&#x1f31f;我热衷于分享&#x1f58a;学习经验&#xff0c;&#x1f3eb;多彩生活&#xff0c;精彩足球赛事⚽&#x1f517;我的CSDN&#xff1a; Kevin ’ s blog&#x1f4c2;专栏收录&#xff1a;C预言 1. 文件的作用 …...

ADB 操作命令详解及用法大全

ADB 简介 ADB&#xff0c;全称 Android Debug Bridge&#xff0c;是 Google 提供的一款用于 Android 平台设备&#xff08;包括真机和模拟器&#xff09;调试、交互和管理的命令行工具。通过 ADB&#xff0c;开发者可以在电脑上对连接的 Android 设备执行一系列高级操作&#…...

指针数组。

指针数组 int c[5]{1,2,3,4,5};int *pc;printf("p:%d",p);return 0;输出&#xff1a;p:-756683712 说明p是地址值&#xff0c;*p就是取这个地址上的元素的值。所以printf(“*p:%d”,*p); 打印出来的是 *p:1 *pc,c是c[5]数组的首地址元素。 #include <iostream>…...

GitHub开源项目权限管理-使用账号和个人令牌访问

1.打开后台账号设置 2.找到左下角的Developer settings 3.找到Personal access tokens 的 Tokens(classic) 4.选择创建新证书 5.填写证书信息 6.点击生成证书&#xff0c;复制证书并且保存起来&#xff08;血泪教训&#xff0c;证书只会在创建时显示一次&#xff0c;以后就再也…...

DevSecOps平台架构系列-亚马逊云AWS DevSecOps平台架构

目录 一、概述 二、AWS DevSecOps实施原则 2.1 尽早采用安全测试&#xff0c;加速问题反馈 2.2 优先考虑预防性安全控制 2.3 部署检测性安全控制时&#xff0c;确保有与之互补的响应性安全控制 2.4 安全自动化 2.5 总结 三、AWS DevSecOps关键组件 3.1 关键组件 3.2 关…...

为什么政府的网站总是做的很差/电商平台运营方案思路

时间限制&#xff1a;1 秒 内存限制&#xff1a;32 兆 特殊判题&#xff1a;否 提交&#xff1a;6729 解决&#xff1a;1981 题目描述&#xff1a;读入一组字符串&#xff08;待操作的&#xff09;&#xff0c;再读入一个int n记录记下来有几条命令&#xff0c;总共有2中命令&a…...

企业网站开发/网络舆情分析报告模板

摘要&#xff1a; 一、背景介绍近年来&#xff0c;越来越热的云计算被推倒风口浪尖&#xff0c;各大中型企业纷纷把企业服务迁移到云上&#xff0c;众多的创业公司也把云服务器作为数据服务的首选。那么问题来了&#xff0c;有些企业的运维开始担心上云的过程是否能做到简单和平…...

制作动画片的软件/哈尔滨百度关键词优化

仅供参考 1&#xff0c;js&#xff1a; onShareAppMessage(res) {return {title: 我在使用俺搜找客户&#xff0c;10万材料人都在用&#xff0c;就差你了,path: /pages/index,success: function(res) {// 转发成功console.log(res)},fail: function(res) {// 转发失败console.l…...

做政府网站哪家公司好/seo优化网站的注意事项

Redis SET 命令手册1. 可选项2. 返回值3. 历史变化4. 案例5. 模式从Redis 1.0.0 起可用 时间复杂度&#xff1a;O(1) 设置 key 以保存字符串 value。如果 key 已经保存了一个 value&#xff0c;则无论其类型如何&#xff0c;都会覆盖该值。成功的 SET 操作将丢弃与该键任何以前…...

密云石家庄网站建设/爱站工具包的模块有哪些

转载自&#xff1a;http://www.manew.com/3102.html Unity3D中一些脚本的方法只能用在JS中&#xff0c;在C#中是无效的&#xff0c;而C#可以与服务器端通讯&#xff0c;JS本身却不行。而且&#xff0c;如果需要用到js调用c#的问题&#xff0c;js会比c#先编译&#xff0c;所以在…...

天津哪里做网站最好/关键词挖掘工具有哪些

对问题各个击破 —— 高效程序员的 45 个习惯之习惯35 “逐行检查代码库中的代码确实很令人恐惧。但是要调试一个明显的错误&#xff0c;只有去查看整个系统的代码&#xff0c;而且要全部过一遍。毕竟你不知道问题可能发生在什么地方&#xff0c;这样做是找到它的唯一方式…...