手写简易操作系统(十七)--编写键盘驱动
前情提要
上一节我们实现了锁与信号量,这一节我们就可以实现键盘驱动了,访问键盘输入的数据也属于临界区资源,所以需要锁的存在。
一、键盘简介
之前的 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
就是断码)
按键 | 扫描码 | 按键 | 扫描码 | 按键 | 扫描码 |
---|---|---|---|---|---|
Esc | 0x01 | F1 | 0x3B | F2 | 0x3C |
F3 | 0x3D | F4 | 0x3E | F5 | 0x3F |
F6 | 0x40 | F7 | 0x41 | F8 | 0x42 |
F9 | 0x43 | F10 | 0x44 | F11 | 0x57 |
F12 | 0x58 | PrintSc | 0x37 | ScrollLk | 0x46 |
Pause/Brk | 0x45 | ` | 0x29 | 1 | 0x02 |
2 | 0x03 | 3 | 0x04 | 4 | 0x05 |
5 | 0x06 | 6 | 0x07 | 7 | 0x08 |
8 | 0x09 | 9 | 0x0A | 0 | 0x0B |
- | 0x0C | = | 0x0D | Backspace | 0x0E |
Tab | 0x0F | Q | 0x10 | W | 0x11 |
E | 0x12 | R | 0x13 | T | 0x14 |
Y | 0x15 | U | 0x16 | I | 0x17 |
O | 0x18 | P | 0x19 | [ | 0x1A |
] | 0x1B | | | 0x2B | CapsLock | 0x3A |
A | 0x1E | S | 0x1F | D | 0x20 |
F | 0x21 | G | 0x22 | H | 0x23 |
J | 0x24 | K | 0x25 | L | 0x26 |
; | 0x27 | ’ | 0x28 | Enter | 0x1C |
Shift左 | 0x2A | Z | 0x2C | X | 0x2D |
C | 0x2E | V | 0x2F | B | 0x30 |
N | 0x31 | M | 0x32 | , | 0x33 |
. | 0x34 | / | 0x35 | Shift右 | 0x36 |
Ctrl左 | 0x1D | Win左 | 0xE0 | Alt左 | 0x38 |
Space | 0x39 | Alt右 | 0xE038 | Win右 | 0xE0 |
Menu | 0xE0 | Ctrl右 | 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); // 唤醒消费者}
}
我们看一下后面两个函数,wait
和wakeup
,这两个函数,这两个函数传入的是一个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");
}
键盘驱动就稍显复杂一点,主要是涉及到了shift
,ctrl
,alt
,caplock
这些个控制键,这些键位是否按下所表示的通码断码是不一样的。这里就是处理字符,相信大家看代码就可以看明白。
四、仿真
我们创建一个线程,键盘输入什么,打印什么
结束语
本节我们编写了键盘驱动以及其使用的环形队列数据结构。下一节我们将实现一个用户进程,即特权级为3的进程。
老规矩,代码地址为 https://github.com/lyajpunov/os
相关文章:
手写简易操作系统(十七)--编写键盘驱动
前情提要 上一节我们实现了锁与信号量,这一节我们就可以实现键盘驱动了,访问键盘输入的数据也属于临界区资源,所以需要锁的存在。 一、键盘简介 之前的 ps/2 键盘使用的是中断驱动的,在当时,按下键盘就会触发中断&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,转载请说明出处,保留一切权益。 关于编程器固件 所谓编程器固件是用编程器读取嵌入式设备的FLASH存储数据生成的文件,类似于直接用工具复制整个硬盘 编程器固件与普通固件的差异 编程器固件是用特定的结构(按顺序、大小)将一些文件系…...
Python从原Excel表中抽出数据存入同一文件的新的Sheet(附源码)
python读取excel数据。Python在从原Excel表中抽出数据并存储到同一文件的新的Sheet中的功能,充分展示了其在数据处理和自动化操作方面的强大能力。这一功能不仅简化了数据迁移的过程,还提高了数据处理的效率,为数据分析和管理工作带来了极大的…...
计算机网络实验六:路由信息协议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数据库备份的重要性 (一)数据丢失的原因 (二)数据丢失的后果 二、MySQL备份类型 (一)根据数据库状态 (二)根据数据的完整性 (三)…...
String类相关oj练习
前言: 此处练习对应博客文章:公主,王子,请点击 1.第一次只出现一次的字符 做题首先看清要求和提示: 给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不…...
【Linux】进程实践项目 —— 自主shell编写
送给大家一句话: 不管前方的路有多苦,只要走的方向正确,不管多么崎岖不平,都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》 自主shell命令编写 1 前言2 项目实现2.1 创建命令行2.2 获取命令2.3 分割命令2.4 运行命令 3 源代码…...
基于SpringBoot和Vue的学生笔记共享平台的设计与实现
今天要和大家聊的是一款基于SpringBoot和Vue的学生笔记共享平台的设计与实现 !!! 有需要的小伙伴可以通过文章末尾名片咨询我哦!!! 💕💕作者:李同学 💕&…...
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 镜像使用 未按文档实际搭建,但大致是这样 官方文档:https://higress.io/zh-cn/docs/overview/what-is-higress 了解:利用sealos快速安装kubernetes集群:https://note.youdao.com/s…...
Swagger3探索之游龙入海
引言 后端开发中常用的接口调用工具一般使用Postman、ApiPost工具,但后期需要与前端联调,要补充接口文档花费大量时间,此时Swagger3应运而生,大大提高沟通交流的效率。 引用依赖 <!-- Swagger3 调用方式 http://ip:port/swa…...
javaWeb项目-学生考勤管理系统功能介绍
项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架:ssm、Springboot 前端:Vue、ElementUI 关键技术:springboot、SSM、vue、MYSQL、MAVEN 数据库工具:Navicat、SQLyog 1、JAVA技术 JavaSc…...
云备份项目认识、环境搭建以及所使用的库的介绍
一、云备份认识 将本地计算机一个受监管的文件夹的文件上传到服务器中,有服务器组织,客户端可以通过网页将文件查看并且下载下来,下载过程支持断点续传功能,并且服务器会对上传的文件进行热点管理,长时间没人访问的文…...
汇编语言第四版-王爽第2章 寄存器
二进制左移四位,相当于四进制左移一位。 debug命令实操,win11不能启动,需要配置文件 Windows64位系统进入debug模式_window10系统64位怎么使用debugger-CSDN博客...
MoonBit MeetUp回顾——张正、宗喆:编程语言在云原生与区块链领域的技术探索
宗喆和张正分别给我们带了 KCL 相关的最新进展,由蚂蚁集团开发的 Rust 编写的开源 DSL,目标是优化云原生策略配置和用户体验。它通过引入动态配置管理、配置校验和基础设施抽象等核心概念,解决开发者认知负担、配置膨胀和标准化工具缺乏的问题…...
云原生靶场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环境搭建参考:【3D目标检测】环境搭建(OpenP…...
k8s1.28.8版本安装prometheus并持久化数据
本文参考 [k8s安装prometheus并持久化数据_/prometheus-config-reloader:-CSDN博客](https://blog.csdn.net/vic_qxz/article/details/119598466)前置要求: 已经部署了NFS或者其他存储的K8s集群. 这里注意networkpolicies网络策略问题,可以后面删除这个策略&#x…...
Mybatis-特殊SQL的执行
1. 模糊查询 在MyBatis中进行模糊查询时,有以下三种常见的实现方式: 1.1. 错误示范 先来个准备操作,并做一个错误示例 根据姓名,模糊查询用户,(x小x) 更新数据表 SQLMapper.java package com.sakurapaid.mybatis3…...
金融衍生品市场
金融衍生品市场 衍生金融品的作用衍生金融工具远期合约期货合约期权 衍生金融品的作用 套期保值(Hedging) 组合多头头寸(long position)与空头头寸(short position)例:股票与股指期货 投机 衍生金融工具 远期合约 定义:在将来…...
2、Cocos Creator 下载安装
Cocos Creator 从 v2.3.2 开始接入了全新的 Dashboard 系统,能够同时对多版本引擎和项目进行统一升级和管理!Cocos Dashboard 将做为 Creator 各引擎统一的下载器和启动入口,方便升级和管理多个版本的 Creator。还集成了统一的项目管理及创建…...
Docker版本:18.06.1安装
1、操作系统:CentOS 7.5以上 2、Docker版本: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请求,前端传的参数名字跟后端规定的参数一样。但是通过RequestBody注解接收的参数始终为NULL! //实体类中属性没有用驼峰命名 private String SubscribeID; /*** 标题*/ private String Title;解决方案: 1、字段上使用JsonProperty(valu…...
unity 打包安卓错误汇集
Failed to find target with hash string "android-34’ in: D:Pr 他说找不到sdk34level的我用as打开后卸载又重装,最后解决了 我放到Plugins/Android/下面的Java代码没有被编译 这个不知道为什么。我故意把代码写的有问题,会报错那种ÿ…...
C语言-文件操作
🌈很高兴可以来阅读我的博客!🌟我热衷于分享🖊学习经验,🏫多彩生活,精彩足球赛事⚽🔗我的CSDN: Kevin ’ s blog📂专栏收录:C预言 1. 文件的作用 …...
ADB 操作命令详解及用法大全
ADB 简介 ADB,全称 Android Debug Bridge,是 Google 提供的一款用于 Android 平台设备(包括真机和模拟器)调试、交互和管理的命令行工具。通过 ADB,开发者可以在电脑上对连接的 Android 设备执行一系列高级操作&#…...
指针数组。
指针数组 int c[5]{1,2,3,4,5};int *pc;printf("p:%d",p);return 0;输出:p:-756683712 说明p是地址值,*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.点击生成证书,复制证书并且保存起来(血泪教训,证书只会在创建时显示一次,以后就再也…...
DevSecOps平台架构系列-亚马逊云AWS DevSecOps平台架构
目录 一、概述 二、AWS DevSecOps实施原则 2.1 尽早采用安全测试,加速问题反馈 2.2 优先考虑预防性安全控制 2.3 部署检测性安全控制时,确保有与之互补的响应性安全控制 2.4 安全自动化 2.5 总结 三、AWS DevSecOps关键组件 3.1 关键组件 3.2 关…...
为什么政府的网站总是做的很差/电商平台运营方案思路
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:6729 解决:1981 题目描述:读入一组字符串(待操作的),再读入一个int n记录记下来有几条命令,总共有2中命令&a…...
企业网站开发/网络舆情分析报告模板
摘要: 一、背景介绍近年来,越来越热的云计算被推倒风口浪尖,各大中型企业纷纷把企业服务迁移到云上,众多的创业公司也把云服务器作为数据服务的首选。那么问题来了,有些企业的运维开始担心上云的过程是否能做到简单和平…...
制作动画片的软件/哈尔滨百度关键词优化
仅供参考 1,js: onShareAppMessage(res) {return {title: 我在使用俺搜找客户,10万材料人都在用,就差你了,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 起可用 时间复杂度:O(1) 设置 key 以保存字符串 value。如果 key 已经保存了一个 value,则无论其类型如何,都会覆盖该值。成功的 SET 操作将丢弃与该键任何以前…...
密云石家庄网站建设/爱站工具包的模块有哪些
转载自:http://www.manew.com/3102.html Unity3D中一些脚本的方法只能用在JS中,在C#中是无效的,而C#可以与服务器端通讯,JS本身却不行。而且,如果需要用到js调用c#的问题,js会比c#先编译,所以在…...
天津哪里做网站最好/关键词挖掘工具有哪些
对问题各个击破 —— 高效程序员的 45 个习惯之习惯35 “逐行检查代码库中的代码确实很令人恐惧。但是要调试一个明显的错误,只有去查看整个系统的代码,而且要全部过一遍。毕竟你不知道问题可能发生在什么地方,这样做是找到它的唯一方式…...