第十章-输入输出系统
Ⅰ.锁
本质是互斥操作
原因:针对公共资源访问时,临界区若不加以互斥限制,可能导致执行过程中突然的中断导致出现异常。
1.互斥过程
设定互斥量M为二值信号量,0/1,P-,V+,现有两个进程A、B共同访问公共资源R,则有
1.线程A访问R,P-,M=0
2.线程B访问R,P-,M<0,阻塞线程B,P+
3.线程A访问R结束,V+,唤醒线程B
4.线程B开始执行
阻塞功能 :阻塞是线程主动的行为 。己阻塞的线程是由别人来唤醒的,通常是锁的持有者。不让线程在就绪队列中出现就行了,这样线程便没有机会运行。
线程自主阻塞之后,保存当前的寄存器,栈信息,当唤醒后,并不会从头开始执行,而是从线程阻塞的部分开始重新执行,并且时间片依旧是阻塞前的数值。
(1)实现线程阻塞和唤醒
/* 阻塞过程:
* 1.关中断
* 2.更改当前线程状态为阻塞态
* 3.调用schedule(),从就绪队列中取出下一个就绪线程列执行
* 4.开中断
*/
void thread_blocked(enum task_status state){// 原子操作,必须关中断enum intr_status old_status = intr_distable();// 只有是传入下面三种状态才可以被阻塞ASSERT((state == TASK_BLOCKED) || (state == TASK_HANGING) || (state == TASK_WAITING));struct task_struct * cur_thread = running_thread();cur_thread->status = state;schedule();// list_pop(&thread_ready_list, &cur_thread->general_tag);intr_set_status(old_status);
}
/* 唤醒线程
* 1.关中断
* 2.判断当前线程是否在就绪队列中
* 3.不在,则添加;在,则PANIC
* 4.当前线程加入就绪队列
* 5.修改状态为TASK_READY
* 6.开中断
*/
// 被阻塞的进程由于没有执行,因此不可以调度schedule()函数
void thread_unblocked(struct task_struct* pthread){enum intr_status old_status = intr_disable();ASSERT((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_HANGING) || (pthread->status == TASK_WAITING));if(pthread->status != TASK_READY){ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag));if(elem_find(&thread_ready_list, &pthread->general_tag)){PANIC("thread_unblock:blocked thread in ready_list");}list_push(&thread_ready_list, &pthread->general_tag);pthread->status = TASK_RUNNING;}intr_set_status(old_status);
}
(2)实现PV操作
/* P操作,存在线程阻塞
* 1.关中断
* 2.判断当前semophore==0
* 3.为0,循环等待,并将当前线程加入信号量等待队列,并阻塞当前线程
* 4.不为0,semophore-1
* 5.开中断
*/
void sema_down(struct semaphore* psema) {emum intr_status old_status = intr_disable();while(psema->value == 0){ASSERT(!elem_find(&psema->waiters, &running_thread()->general_tag));if(elem_find(&psema->waiters, &running_thread()->general_tag)){PANIC("sema_down:P op failed"); }list_append(&psema->waiters, &running_thread()->general_tag);thread_blocked(TASK_BLOCKED);}psema->value--;ASSERT(psema->value == 0);intr_set_status(old_status);
}
/* V操作,存在线程唤醒
* 1.关中断
* 2.判断当前信号量等待队列不为空,根据elem2entry找到队头线程的PCB地址,并唤醒
* 3.为空,semophore++
* 4.开中断
*/
void sema_up(struct semaphore* psema) {emum intr_status old_status = intr_disable();ASSERT(psema->value == 0);if(!list_empty(&psema->waiters)){struct task_struct* wait_thread = elem2entry(struct task_struct, general_tag, list_pop(&psema->waiters));thread_unblocked(wait_thread);}psema->value++;ASSERT(psema->value == 1);intr_set_status(old_status);
}
(3)实现获得锁、释放锁操作
/* 获得锁
* 1.先判断自己是不是锁的持有者
* 2.不是,P操作,持有锁,持有次数+1
* 3.反之,持有次数+1
*/
void lock_acquire (struct lock* plock) {/* 排除曾经自己已经持有锁但还未将其释放的情况 */if(plock->holder != running_thread()){sema_down(&plock->semaphore);plock->holder = running_thread();ASSERT(plock->holder_repeat_nr == 0);plock->holder_repeat_nr = 1;}else{plock->holder_repeat_nr++;}
}
/* 释放锁
* 1.ASSERT()判断是否拥有锁
* 2.判断持有锁的次数,防止进程需要多次访问资源。
* 3.清空锁
* 3.V操作
*/
void lock_release (struct lock* plock) {ASSERT(plock->holder == running_thread());if(plock->holder_repeat_nr > 1){plock->holder_repeat_nr--;return;}ASSERT(plock->holder_repeat_nr == 1);plock->holder = NULL;plock->holder_repeat_nr = 0;sema_up(&plock->semaphore);
}
Ⅱ.从键盘获取输入
键的扫描码:一个键的状态要么是按下,要么是弹起,因此一个键便有两个编码,按键被按下时的编码叫通码,也就是表示按键上的触点接通了内部电路,使硬件产生了一个码,故通码也称为 makecode。按键在被按住不松手时会持续产生相同的码,直到按键被松开时才终止,因此按键被松开弹起时产生的编码叫断码,也就是电路被断开了,不再持续产生码了,故断码也称为 breakcode。一个键的扫描码是由通码和断码组成的。
键盘编码器:键盘是个独立的设备,在它内部有个叫作键盘编码器的芯片,通常是 Intel 8048 或兼容芯片,它的作用是:每当键盘上发生按键操作,它就向键盘控制器报告哪个键被按下,以及键的扫描码。
键盘控制器:在主机内部的主板上,通常是 Intel 8042 或兼容芯片,接收来自键盘编码器的按键信息,将其解码后保存,然后向中断代理发中断,之后处理器执行相应的中断处理程序读入 8042 处理保存过的按键信息。
1.从键盘读取输入的过程
• 扫描码有 3 套,现在一般键盘中的 8048 芯片支持的是第二套扫描码 。 因此每当有击键发生时, 8048发给 8042 的都是第二套键盘扫描码。
• 8042 为了兼容性,将接收到的第二套键盘扫描码转换成第一套扫描码。 8042 是按字节来处理的,每处理一个字节的扫描码后,将其存储到自己的输出缓冲区寄存器 。
• 然后向中断代理 8259A 发中断信号,这样我们的键盘中断处理程序通过读取 8042 的输出缓冲区寄存器,会获得第一套键盘扫描码。
2.键盘扫描码
根据键盘的更新迭代,出现了3种常用的键盘扫描码,为了兼容第一套键盘扫描码对应的中断处理程序,不管键盘用的是何种键盘扫描码,当键盘将扫描码发送到 8042 后,都由 8042 转换成第一套扫描码。
8042介于8048与处理器中间,担任中间人的职责,完成处理器对8048的设置,以及8048的扫描码输入给处理器。因此8042作为输入输出缓存区,包括输入、输出缓冲寄存器,以及状态、控制寄存器。
Ⅲ.键盘驱动程序
对于键盘驱动程序而言,每次都需要读取输出寄存器,取出键盘的扫描码并完成转为ASCII码的工作。
键盘驱动程序,初始化(注册键盘中断处理程序)
#define KEY_BUF_PORT 0x60 // 键盘 buffer 寄存器端口号为 0x60
/* 键盘中断处理程序 */
static void intr_keyboard_handler(void) {
/* 必须要读取输出缓冲区寄存器,否则8042不再继续响应键盘中断 */uint8_t scancode = inb(KBD_BUF_PORT);put_int(scancode);return;
}/* 键盘初始化,注册键盘中断处理程序 */
void keyboard_init() {put_str("keyboard init start\n");register_handler(0x21, intr_keyboard_handler);put_str("keyboard init done\n");
}
1.C语言中有3中转义字符
(1) 一般转义字符,‘\+单个字母’ 的形式。
(2)八进制转义字符,‘\0+三位八进制数字表示的 ASCII 码’ 的形式 。
(3)十六进制转义字符,‘\x+两位十六进制数字表示的 ASCII 码’ 的形式 。
2.处理扫描码
键盘驱动程序需要完成 键盘扫描码->按键的ASCII码 的映射过程
(1)设计思路:
- 如果是一些用于操作方面的控制键,简称操作控制键,如<shift>、<ctrl>、<caps lock>,它通常是组合键,需要与其他键一起考虑,然后做出具体的行为展现,在键盘驱动中完成处理。
- 如果是一些用于字符方面的键,无论是可见字符,或是字符方面的控制键(简称字符控制键),如<backspace>,统统交给字符处理程序完成。
对于第一阶段,它与字符无直接的关系,因此咱们就在键盘驱动中处理。
对于第二阶段,咱们得知道用户按下的是什么宇符,不能把操作控制键当成字符传给字符处理程序,比如把 shift键的扫描码传给 put_char,这不就乱了吗?因此,咱们得把按键的扫描码转换成对应的字符,也就是将通码转换为字符的 ASCII 码,这就是前面所说的源到目标的映射关系。
**简言之:需要区分什么按键是需要显示的,什么按键是负责控制的。**这就需要建立扫描码和ASCII码的映射表,对键盘输入加以判定。
(2)建立键盘字符与通码映射关系
keymap[][0]表示未与shift组合的按键值,keymap[][1]表示与shift组合的按键值
/* 以通码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}
/*其它按键暂不处理*/
};
(3)更新键盘驱动程序
(3.1)读取扫描码
static void intr_keyboard_handler(void) {……bool break_code;uint16_t scancode = inb(KBD_BUF_PORT);/* 若扫描码是e0开头的,表示此键的按下将产生多字节的扫描码,如shift、alt、ctrl、caps_lock等扫描码为2字节* 所以马上结束此次中断处理函数,等待下一个扫描码进来*/ if (scancode == 0xe0) { ext_scancode = true; // 打开e0标记return;}/* 如果上次是以0xe0开头,将扫描码合并 */if (ext_scancode) {scancode = ((0xe000) | scancode);ext_scancode = false; // 关闭e0标记} break_code = ((scancode & 0x0080) != 0); // 获取break_code……
}
(3.2)需要判断当前键盘是处于断码/通码
通码的扫描码scancode第8位为0,断码为1。
若为断码,需要将操作方面的控制键状态改为false,如shift、alt。将他们的断码的第8位改为0,作为通码访问keyb_map获取具体是哪个控制键,然后更改状态即可。
/*---------------续上----------------*/if (break_code) { // 若是断码break_code(按键弹起时产生的扫描码)/* 由于ctrl_r 和alt_r的make_code和break_code都是两字节,所以可用下面的方法取make_code,多字节的扫描码暂不处理 */uint16_t make_code = (scancode &= 0xff7f); // 得到其make_code(按键按下时产生的扫描码)/* 若是任意以下三个键弹起了,将状态置为false */if (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;} /* 由于caps_lock不是弹起后关闭,所以需要单独处理 */return; // 直接返回结束此次中断处理程序}
若为通码,分为操作键和字符键两类,先要读取操作键,如shift、ctrl、alt、caps_lock,然后修改操作键的状态,根据操作键的状态确定映射的字符。
/*---------------续上----------------*//* 若为通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_code */else if ((scancode > 0x00 && scancode < 0x3b) ||(scancode == alt_r_make) ||(scancode == ctrl_r_make)){bool shift = false; // 判断是否与shift组合,用来在一维数组中索引对应的字符if ((scancode < 0x0e) || (scancode == 0x29) ||(scancode == 0x1a) || (scancode == 0x1b) ||(scancode == 0x2b) || (scancode == 0x27) ||(scancode == 0x28) || (scancode == 0x33) ||(scancode == 0x34) || (scancode == 0x35)){/****** 代表两个字母的键 ********0x0e 数字'0'~'9',字符'-',字符'='0x29 字符'`'0x1a 字符'['0x1b 字符']'0x2b 字符'\\'0x27 字符';'0x28 字符'\''0x33 字符','0x34 字符'.'0x35 字符'/'*******************************/if (shift_down_last){ // 如果同时按下了shift键shift = true;}}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;}}uint8_t index = (scancode &= 0x00ff); // 将扫描码的高字节置0,主要是针对高字节是e0的扫描码.char cur_char = keymap[index][shift]; // 在数组中找到对应的字符/* 只处理ascii码不为0的键 */if (cur_char){put_char(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键,当再次按下时则状态取反,* 即:已经开启时,再按下同样的键是关闭。关闭时按下表示开启。*/caps_lock_status = !caps_lock_status;}}else{put_str("unknown key\n");}
Ⅳ.环形输入缓冲区
构建缓冲区保存键盘扫描码转换的字符,每次从缓冲区取出字符,完成打印。
利用生产者消费者模式构建环形队列,实现字符的取出和保存。主要完成缓冲区存储状态、添加1字节、删除1字节等操作。
1.环形队列结构体
/* 环形队列 */
struct ioqueue {struct lock lock;struct task_struct *producer;struct task_struct *consumer;// 缓冲区大小char buf[bufsize];// 队首,数据往队首处写入int32_t head;// 队尾,数据从队尾处读出int32_t tail;
};
2.缓冲区满
/* 返回 pos 在缓冲区中的下一个位置值 */
static int32_t next_pos (int32_t pos) {return (pos+1)%bufsize;
}
/* 采用头插法加入元素,判断队列是否已满 */
bool ioq_full(struct ioqueue* ioq) {ASSERT(intr_get_status() == INTR_OFF);return next_pos(ioq->head) == ioq->tail;
}
3.缓冲区为空
/* 采用头插法加入元素,判断队列是否为空 */
bool ioq_empty(struct ioqueue* ioq) {ASSERT(intr_get_status() == INTR_OFF);return ioq->head == ioq->tail;
}
4.缓冲区为空时,等待
/* 当前生产者或消费者在此缓冲区上等待 */
static void ioq_wait(struct task_struct ** waiter) {ASSERT((waiter != NULL) && (&waiter == NULL));*waiter = running_thread();thread_blocked(TASK_BLOCKED);
}
5.缓冲区不为空,唤醒waiterr
/* 唤醒 waiter */
static void wakeup(struct task_struct** waiter) {ASSERT(&waiter != NULL);thread_unblocked(*waiterr);*waiterr = NULL;
}
6.缓冲区添加一字节
/* 生产者往 ioq 队列中写入一个字符 byte */
void ioq_putchar(struct ioqueue* ioq, char byte) {ASSERT(intr_get_intr() == INTR_OFF);while(ioq_full(ioq)){lock_acquire(&ioq->lock);ioq_wait(&ioq);lock_release(&ioq->lock);}ioq->buf[ioq->head] = byte;ioq->head = next_pos(ioq->head);if(ioq->consumer != NULL){wakeup(&ioq->consumer);}
}
- 判断缓冲区是否满了
- 满了则请求锁,并wait,阻塞当前线程,释放锁
- 反之,头插法,在队列头插入新元素
- 唤醒消费者
7.缓冲区取出一字节
/* 消费者从 ioq 队列中获取一个字符 */
char ioq_getchar(struct ioqueue* ioq) {ASSERT(intr_get_status() == INTR_OFF);while(ioq_empty(ioq)){lock_acquire(&ioq->lock);ioq_wait(&ioq);lock_release(&ioq->lock);}char byte = ioq->buf[ioq->tail];ioq->tail = next_pos(ioq->tail);if(ioq->producer != NULL){wakeup(&ioq->consumer);}return byte;
}
- 判断缓冲区是否为空
- 为空则请求锁,并wait,阻塞当前线程,释放锁
- 反之,从队尾取出新元素
- 唤醒生产者
over
相关文章:

第十章-输入输出系统
Ⅰ.锁 本质是互斥操作 原因:针对公共资源访问时,临界区若不加以互斥限制,可能导致执行过程中突然的中断导致出现异常。 1.互斥过程 设定互斥量M为二值信号量,0/1,P-,V,现有两个进程A、B共同…...

TensorFlow学习:使用官方模型进行图像分类、使用自己的数据对模型进行微调
前言 上一篇文章 TensorFlow案例学习:对服装图像进行分类 中我们跟随官方文档学习了如何进行预处理数据、构建模型、训练模型等。但是对于像我这样的业余玩家来说训练一个模型是非常困难的。所以为什么我们不站在巨人的肩膀上,使用已经训练好了的成熟模…...

Matlab地理信息绘图—研究区域绘制
文章目录 m_map工具箱Matlab绘制研究区域结果显示 m_map工具箱 m_map是 MATLAB 中用于制作地图和地理数据可视化的工具包。这个工具包提供了一组函数和工具,使得用户能够在 MATLAB 中轻松创建地图,并在地图上显示各种地理和气象数据。以下是 m_map 工具包…...

[CSAWQual 2019]Web_Unagi - 文件上传+XXE注入(XML编码绕过)
[CSAWQual 2019]Web_Unagi 1 解题流程1.1 分析1.2 解题 2 思考总结 1 解题流程 这篇博客讲了xml进行编码转换绕过的原理:https://www.shawroot.cc/156.html 1.1 分析 页面可以上传,上传一句话php失败,点击示例发现是xml格式,那…...

ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的问题解决
winR打开窗口输入 services.msc 停止mysql 找到data文件,清空其中全部文件。没有data文件,手动创建 输入 mysqld --remove mysql 移除服务; 注册服务,mysqld -install; 并开始初始化,mysqld --initi…...

什么是函数库和动态链接库?
函数库和动态链接库(也称为共享库)是在软件开发中常见的两种代码重用技术,它们有助于组织、共享和管理代码。在本文中,我们将详细解释函数库和动态链接库的概念、用途以及它们的工作原理。 ## 什么是函数库? 函数库是…...

POM配置
dependencies 所有声明在dependencies里的依赖都会自动引入,并默认被所有的子项目继承 dependencyManagement 只是声明依赖,并不会自动引入,因此子项目需要显示声明依赖。在子项目中声明了依赖项,且没有指定具体版本&#x…...

微电网单台并网逆变器PQ控制matlab仿真模型
微❤关注“电气仔推送”获得资料(专享优惠) 微电网运行在并网模式下且公共电网供应正常时,因为公共电网给定了电 压和频率的参考值,所有的逆变器可以使用PQ控制方式。 当系统频率为额定频率f0时,系统稳定在A点&#x…...

计算机毕业设计选什么题目好?springboot 旅游网站
✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…...

Android Fragment中使用Arouter跳转到Activity后返回Fragment不回调onActivityResult
Fragment中通过路由跳转到Activity 跳转传递参数 通过Arouter跳转 Postcard postcard ARouter.getInstance().build(RouterConstant.ACTION_TRANSMANAGERACTIVITY1);Bundle bundle new Bundle();bundle.putInt("code", 404);postcard.with(bundle); //设置bundlef…...

hive add columns 后查询不到新字段数据的问题
分区表add columns 查询不到新增字段数据的问题; 5.1元数据管理 (1)基本架构 Hive的2个重要组件:hiveService2 和metastore,一个负责转成MR进行执行,一个负责元数据服务管理 beeline-->hiveService2/spar…...

【linux】权限相关问题
【linux】权限相关问题 一.用户的分类sudo 二.文件执行的权限i. 文件的分类ii.人的分类三.修改创建文件的权限chmod更改文件创造的默认权限(umask) 三.删除(粘滞位) 一.用户的分类 在我们使用linux的时候,有用户类型的区分,不同用…...

“.NET视频总结:认识框架的结构和组件,掌握开发工具的奥妙“一
目录 第一单元:二十一世纪程序执行 背景: 总结: 第二单元:对象导向与类别设计 背景: 总结: 第三单元:使用类别与基底类别库 总结: 第四单元:Windows开发程序 背景: 总结: 第五单元:防护式程序设计 背景: 总结: 第六…...

02-RocketMQ开发模型
目录汇总:RocketMQ从入门到精通汇总 上一篇:01-RocketMQ整体理解与快速实战 上一部分,我们可以搭建RocketMQ集群,然后也可以用命令行往RocketMQ写入消息并进行消费了。这一部分我们就来看怎么在项目中用上RocketMQ。 一、RocketMQ…...

第83步 时间序列建模实战:Catboost回归建模
基于WIN10的64位系统演示 一、写在前面 这一期,我们介绍Catboost回归。 同样,这里使用这个数据: 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal Syndr…...

开源任务调度框架
本文主要介绍一下任务调度框架Flowjob的整体结构,以及整体的心路历程。 功能介绍 flowjob主要用于搭建统一的任务调度平台,方便各个业务方进行接入使用。 项目在设计的时候,考虑了扩展性、稳定性、伸缩性等相关问题,可以作为公司…...

Android Native 开发 要点记录
Android Studio 中写 C 代码 android studio创建C项目_android studio native c-CSDN博客 项目配置参考 【CMake】CMakeLists.txt的超傻瓜手把手教程(附实例源码)_【cmake】cmakelists.txt的超傻瓜手把手教程(附实例源码)-CSDN博客 CMakeLists.txt 讲解…...

数据库中查询所有表信息,查询所有字段信息
MYSQL中 所有表信息 information_schema.tables表 SELECT * FROM information_schema.tables -- TABLE_NAME 表名 -- TABLE_COMMENT 表中文名所有字段信息 information_schema.COLUMNS表 SELECT * FROM information_schema.tables -- TABLE_SCHEMA 数据库名 -- COLUMN…...

改进智能优化算法常用指标一键导出为EXCEL,最优值,平均值,标准差,最差值,中位数,秩和检验,箱线图...
声明:对于作者的原创代码,禁止转售倒卖,违者必究! 为了突出改进智能优化算法的效果,常常会将改进的智能算法与其他算法进行对比。 在一些期刊论文中,经常会看到一个超级大的表格,统计着每个算法…...

在asp.net中,实现类似安卓界面toast的方法(附更多弹窗样式)
目录 一、背景 二、操作方法 2.1修改前 2.2修改后 三、总结 附:参考文章: 一、背景 最近在以前的asp.net网页中,每次点击确定都弹窗,然后还要弹窗点击确认,太麻烦了,这次想升级一下,实现…...

一站式解决方案:Qt 跨平台开发灵活可靠
一站式解决方案:Qt 跨平台开发灵活可靠 Qt 是一种跨平台开发工具,为开发者提供了一站式解决方案。无论您的项目目标是 Windows、Linux、macOS、嵌入式系统还是移动平台,Qt 都能胜任。这种跨平台的特性不仅节省开支,还推动了战略的…...

将cpu版本的pytorch换成gpu版本
1.首先激活虚拟环境 winRcmd 打开dos命令窗口 查看虚拟环境列表 conda env list 激活虚拟环境 2.将原来的pytorch_cpu版本换成gpu版本 注意:安装gpu版本的pytorch时并不需要先卸载原来的cpu版本pytorch,安装时会自己替换的 打开pytorch官网查看以前版本 Previo…...

Ubuntu安装QQ
原文网址:2023在Ubuntu安装最新版QQ Linux v3.1.0 - 哔哩哔哩 作者:sprlightning https://www.bilibili.com/read/cv22100663/ 出处:bilibili 2022年末QQ推出了QQ Linux v3.0系列,目前最新版是今年2月24日推出的v3.1.0版本。注意…...

【Python】实现excel文档中指定工作表数据的更新操作
在做数值计算时,个人比较习惯利用excel文档的公式做数值计算进行对比,检查异常,虽然计算量大后,excel计算会比较缓慢,但设计简单,易排错 但一般测试过程中使用到的数据都不是最终数值,会不停根据…...

力扣(LeetCode)2731. 移动机器人(C++)
脑经急转弯排序 碰撞只改变运动方向,速度始终如"1",且机器人视为无差别的,所以碰撞等于擦肩而过!"机器人碰撞,到底撞没撞,如撞。"因此只考虑每个机器人单方向移动,d秒后停…...

vite和webpack
vite和webpack 文章目录 vite和webpackvite介绍什么是vite为什么使用vitevite优缺点热更新的实现原理 webpack介绍什么是webpackwebpack 优缺点 Vite 为什么比 Webpack 快vite和webpack的区别面试问题Vite为什么比webpack快? vite介绍 什么是vite Vite 是新型前端…...

MinIO图片正常上传不可查看,MinIO通过页面无法设置桶为public
项目场景:国产中标麒麟操作系统部署MinIO正常启动后发现图片能正常上传,但是匿名浏览该图片的时候无法查看。通过网络查询解决方案,得出的结论是:需要把当前上传文件的桶设置为public,由于创建桶默认是private且不可通过浏览器进行…...

Linux 指令心法(七)`cat` 查看、合并和创建文本文件
文章目录 命令的概述和用途命令的用法命令行选项和参数的详细说明命令的示例命令的注意事项或提示 命令的概述和用途 cat 是 “concatenate” 的缩写,它是一个 Linux 和 Unix 系统中的命令,用于查看、合并和创建文本文件。cat 主要用于以下几个方面&…...

解决docker开启MySQL的binlog无法成功。docker内部报错:mysql: [ERROR] unknown variable
1. 报错信息 2. 操作流程 整个流程是这样的: 我愉快的输入docker ps,查看MySQL的docker 容器id 执行指令docker exec -it 8a \bin\bash进入容器内部执行vim /etc/my.cnf,打开配置文件按照网上说的,添加如下配置信息退出docker容…...

c,python ,java,c++ c#在控制台打印彩色文本
在C语言、Java和C#中,你可以通过使用特定的控制字符或库来设置文本颜色。下面分别演示如何在这三种编程语言中实现文本颜色的设置: 在C语言中实现文本颜色设置: C语言中的颜色设置通常依赖于特定的终端或操作系统。以下是一种使用C语言的方…...