Day8 鼠标控制与32位模式切换
文章目录
- 1. 例程harib05a(鼠标解读1)
- 2. 例程harib05b(代码整理)
- 3. 例程harib05c(鼠标解读2)
- 4. 例程harib05d(移动鼠标指针)
- 5. 通往32位模式之路
1. 例程harib05a(鼠标解读1)
上一章可以获取到鼠标的数据了,本章主要解读数据。首先修改一下HariMain函数的读数据处理:
for (;;) {io_cli();if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {io_stihlt();} else {if (fifo8_status(&keyfifo) != 0) {i = fifo8_get(&keyfifo);io_sti();sprintf(s, "%02X", i);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);} else if (fifo8_status(&mousefifo) != 0) {i = fifo8_get(&mousefifo);io_sti();if (mouse_phase == 0) {/* 鼠标回复的0xfa,丢弃 */if (i == 0xfa) {mouse_phase = 1;}} else if (mouse_phase == 1) {/* 鼠标数据第一个字节 */mouse_dbuf[0] = i;mouse_phase = 2;} else if (mouse_phase == 2) {/* 鼠标数据第二个字节 */mouse_dbuf[1] = i;mouse_phase = 3;} else if (mouse_phase == 3) {/* 鼠标数据第三个字节 */mouse_dbuf[2] = i;mouse_phase = 1;/* 鼠标三个字节的数据集齐后显示出来 */sprintf(s, "%02X %02X %02X", mouse_dbuf[0], mouse_dbuf[1], mouse_dbuf[2]);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);}}}
}
每次从鼠标过来的数据都应该是三字节一组的,每当数据累积到三字节就显示在屏幕上。
2. 例程harib05b(代码整理)
struct MOUSE_DEC {unsigned char buf[3], phase;
};void enable_mouse(struct MOUSE_DEC *mdec)
{wait_KBC_sendready();io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);wait_KBC_sendready();io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);/* 顺利的话,0xfa会被送来 */mdec->phase = 0; /* 等待0xfa */return;
}int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{if (mdec->phase == 0) {/* 丢弃0xfa */if (dat == 0xfa) {mdec->phase = 1;}return 0;}if (mdec->phase == 1) {/* 鼠标第一个字节 */mdec->buf[0] = dat;mdec->phase = 2;return 0;}if (mdec->phase == 2) {/* 鼠标第二个字节 */mdec->buf[1] = dat;mdec->phase = 3;return 0;}if (mdec->phase == 3) {/* 鼠标第三个字节 */mdec->buf[2] = dat;mdec->phase = 1;return 1;}return -1; // 理论上不可能到这里
}/* HariMain函数 */
struct MOUSE_DEC mdec;
enable_mouse(&mdec);for (;;) {io_cli();if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {io_stihlt();} else {if (fifo8_status(&keyfifo) != 0) {i = fifo8_get(&keyfifo);io_sti();sprintf(s, "%02X", i);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);} else if (fifo8_status(&mousefifo) != 0) {i = fifo8_get(&mousefifo);io_sti();if (mouse_decode(&mdec, i) >= 0) {/* 三字节集齐 */sprintf(s, "%02X %02X %02X", mdec.buf[0], mdec.buf[1], mdec.buf[2]);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);}}}
}
显示效果为:
3. 例程harib05c(鼠标解读2)
struct MOUSE_DEC {unsigned char buf[3], phase;int x, y, btn;
};int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{if (mdec->phase == 0) {/* 舍弃0xfa */if (dat == 0xfa) {mdec->phase = 1;}return 0;}if (mdec->phase == 1) {/* 等待鼠标的第一个字节 */if ((dat & 0xc8) == 0x08) {/* 如果第一个字节正确 */mdec->buf[0] = dat;mdec->phase = 2;}return 0;}if (mdec->phase == 2) {/* 等待鼠标的第二个字节 */mdec->buf[1] = dat;mdec->phase = 3;return 0;}if (mdec->phase == 3) {/* 等待鼠标的第三个字节 */mdec->buf[2] = dat;mdec->phase = 1;mdec->btn = mdec->buf[0] & 0x07;mdec->x = mdec->buf[1];mdec->y = mdec->buf[2];if ((mdec->buf[0] & 0x10) != 0) {mdec->x |= 0xffffff00;}if ((mdec->buf[0] & 0x20) != 0) {mdec->y |= 0xffffff00;}mdec->y = -(mdec->y); /* 鼠标的y方向与画面符号相反 */return 1;}return -1; /* 理论上走不到这里 */
}
MOUSE_DEC 结构体中新增了x,y,btn字段用于维护移动信息和鼠标按键状态。
在等待鼠标的第一个字节阶段(if (mdec->phase == 1)
)中,判断了第一字节对移动有反应的部分(高4bits)是否在0 ~ 3范围内,以及判断第一字节对点击有反应的部分(低4bits)是否在8 ~ F范围内。如果不在这个范围,就被舍弃。这是一种容错机制,只是为了提高鲁棒性。
这里使用dat & 0xc8,当高4bits的值<4且低4bits的值>8时,最终运算结果才能是0x08。
在等待鼠标的第三个字节阶段(if (mdec->phase == 3)
)。鼠标的按键状态btn放置在buf[0]的低3bits,使用mdec->buf[0] & 0x07
取出低3bits;x和y基本上可以直接使用buf[1]和buf[2],但是需要参考buf[0]中对鼠标动作有反应的几个bit位。将x和y除了低8bits外,全部都设置为1,另外鼠标与屏幕的y方向正好相反,为了配合画面方向,就对y符号进行取反操作。
/* HariMain节选 */
char s[40];} else if (fifo8_status(&mousefifo) != 0) {i = fifo8_get(&mousefifo);io_sti();if (mouse_decode(&mdec, i) != 0) {/* 显示鼠标数据的三个字节 */sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);if ((mdec.btn & 0x01) != 0) {s[1] = 'L';}if ((mdec.btn & 0x02) != 0) {s[3] = 'R';}if ((mdec.btn & 0x04) != 0) {s[2] = 'C';}boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);}
}
s是一个字符数组,根据mdec.btn的值,将s中的“lcr”转换为大写。最终实现效果是,分别按下左键,中键和右键会使对应L,C,R字符变为大写。
4. 例程harib05d(移动鼠标指针)
同样是在HariMain函数中做修改:
} else if (fifo8_status(&mousefifo) != 0) {i = fifo8_get(&mousefifo);io_sti();if (mouse_decode(&mdec, i) != 0) {/* 显示鼠标的三个字节的数据 */sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);if ((mdec.btn & 0x01) != 0) {s[1] = 'L';}if ((mdec.btn & 0x02) != 0) {s[3] = 'R';}if ((mdec.btn & 0x04) != 0) {s[2] = 'C';}boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);/* 鼠标指针图案移动 */boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15); /* 隐藏鼠标 */mx += mdec.x;my += mdec.y;if (mx < 0) {mx = 0;}if (my < 0) {my = 0;}if (mx > binfo->scrnx - 16) {mx = binfo->scrnx - 16;}if (my > binfo->scrny - 16) {my = binfo->scrny - 16;}sprintf(s, "(%3d, %3d)", mx, my);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 隐藏坐标 */putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示坐标 */putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); /* 描画鼠标 */}
}
先隐藏鼠标,再以当前鼠标位置加上移动量作为鼠标的新坐标,并根据屏幕对鼠标进行调整,最后再描绘出鼠标图案。
看起来鼠标可以移动了,但是会把下边栏的图案弄花。
自此,完成了GDT/IDT/PIC的初始化,还有自由使用栈和FIFO缓冲区,以及处理键盘中断。
5. 通往32位模式之路
在asmhead.nas中,存在一些还未解释的代码:
# asmhead.nas
# PIC关闭一切中断
# 根据AT兼容机的规则,如果要初始化PIC,必须在CLI关闭中断之前进行,否则有时会挂起MOV AL,0xffOUT 0x21,ALNOP ; 连续执行OUT,有可能会出现无法正常运行,所以NOPOUT 0xa1,ALCLI ; 禁止CPU级别的中断# 如果使用c语言,会这样写:
# io_out(PIC0_IMR, 0xff); // 禁止主PIC的全部中断
# io_out(PIC1_IMR, 0xff); // 禁止从PIC的全部中断
# io_cli(); // 禁止CPU级别的中断
NOP指令什么都不做,只是让CPU休息一段时间(一个时钟的时间长度)。
还有下面一段:
# asmhead.nas 节选
# 为了让CPU可以访问1M以上的内存空间,需要设定A20GATECALL waitkbdoutMOV AL,0xd1OUT 0x64,ALCALL waitkbdoutMOV AL,0xdf ; enable A20OUT 0x60,ALCALL waitkbdout# 如果使用c语言,会这样写:
# #define KEYCMD_WRITE_OUTPUT 0xd1
# #define KBC_OUTPUT_A20G_ENABLE 0xdf
# /* A20GATE的设定 */
# wait_KBC_sendready();
# io_out8(PORT_KEYCMD, KEYCMD_WRITE_OUTPUT);
# wait_KBC_sendready();
# io_out8(PORT_KEYDAT, KBC_OUTPUT_A20G_ENABLE);
# wait_KBC_sendready(); // 为了等待完成执行指令
基本结构看起来与init_keyboard函数类似,都是往键盘控制电路发送指令。使键盘控制电路的附属端口输出0xdf。该附属端口连接主板的很多地方,通过这个端口发送不同指令可以实现很多控制功能。本次输出0xdf所要完成的功能是使能A20GATE信号线,它使内存的1M以上的部分变成可用状态。
最初的电脑,CPU只有16bits模式,所以内存最大只有1MB,后来为了兼容旧版的操作系统,在激活指令之前,电路被限制为只能使用1MB内存。A20GATE就是为了解除这个限制。
; 切换到保护模式[INSTRSET "i486p"] ; 表示期望使用486指令LGDT [GDTR0] ; 设定临时GDTMOV EAX,CR0AND EAX,0x7fffffff ; 设定bit31为0(为了禁止颁)OR EAX,0x00000001 ; 设定bit0为1(为了切换到保护模式)MOV CR0,EAXJMP pipelineflush
pipelineflush:MOV AX,1*8 ; 可读写的段 32bitMOV DS,AXMOV ES,AXMOV FS,AXMOV GS,AXMOV SS,AX
INSTRSET是为了能使用386以后的LGDT,EAX,CR0等关键字。
LGDT指令把随意准备的GDT(以后还会重新设置)读进来。将CR0寄存器(control register 0)的最高位设置为0,最低位设置位1,就完成了模式转换,进入到不使用颁的保护模式。CR0是一个特殊的寄存器,只有操作系统才能操作它。
保护模式与先前的16位模式不同,段寄存器的解释不是16倍,而是能够使用GDT。这种模式下应用程序既不能随便改变段的设定,也不能使用操作系统专用的段,操作系统收到CPU的保护。
虽然16bits模式下也有带保护的,但是我们要使用的是带保护的32bits模式。
保护模式(protected virtual address mode)全称是受保护的虚拟内存地址模式。从前的16位模式称为“real address mode”。这里的 virtual 和 real 的区别在与计算内存地址时,是使用段寄存器的值直接指定一部分,还是通过GDT使用段寄存器的值指定并非实际存在的地址号码。
通过代入CR0进入保护模式时,由于机器语言的解释会发生变化,因此需要立即执行JMP指令。CPU为了加快指令的执行速度而使用管道(pipeline)机制,即前一条指令还在执行的时候,就开始解释下一条或者更下一条指令。由于模式变了,指令需要重新解释,所以加入JMP指令。
进入保护模式后,段寄存器的意思有变化(不再是乘以16后,再加法计算的意思)。除了CS以外所有的段寄存器的值都从0x0000变为0x0008(即,gdt+1)。
memcpy:MOV EAX,[ESI]ADD ESI,4MOV [EDI],EAXADD EDI,4SUB ECX,1JNZ memcpyRET; bootpack的转送MOV ESI,bootpack ; 转送源MOV EDI,BOTPAK ; 转送目的地MOV ECX,512*1024/4CALL memcpy; 磁盘数据最终会转送到它本来的位置
; 首先从启动扇区开始MOV ESI,0x7c00 ; 转送源MOV EDI,DSKCAC ; 转送目的地MOV ECX,512/4CALL memcpy; 所有剩下的MOV ESI,DSKCAC0+512 ; 转送源MOV EDI,DSKCAC+512 ; 转送目的地MOV ECX,0MOV CL,BYTE [CYLS]IMUL ECX,512*18*2/4 ; 从柱面数变换为字节数/4,IMUL乘法运算SUB ECX,512/4 ; 减去IPL,SUB减法运算CALL memcpy
这段程序主要是在调用memcpy函数:
#define BOTPAK 0x00280000
#define DSKCAC 0x00100000
#define DSKCAC0 0x00008000memcpy(bootpack, BOTPAK, 512*1024/4);
memcpy(0x7c00, DSKCAC, 512/4); // 将磁盘启动扇区的512字节拷贝到0x00100000内存地址后
memcpy(DSKCAC0+512, DSKCAC+512, cyls * 512*18*2/4 - 512/4); // 将始于0x00008200的磁盘内容拷贝到0x00100200内存地址
bootpack是asmhead.nas的最后一个标签,asmhead.bin结束的地方紧接着串联着bootpack.hrb最前面的部分。在Makefile中存在这样一句,用来表示asmhead.bin和bootpack.hrb的连接关系:
haribote.sys : asmhead.bin bootpack.hrb Makefilecopy /B asmhead.bin+bootpack.hrb haribote.sys
由asmhead完成的工作,至此就全部完毕,以后的工作由bootpack完成。
; bootpack的启动MOV EBX,BOTPAKMOV ECX,[EBX+16] ; 传送的数据大小ADD ECX,3 ; ECX += 3;SHR ECX,2 ; ECX /= 4;JZ skip ; 没有要转送的内容时MOV ESI,[EBX+20] ; 转送源ADD ESI,EBXMOV EDI,[EBX+12] ; 转送目的地CALL memcpy
skip:MOV ESP,[EBX+12] ; 栈初始值JMP DWORD 2*8:0x0000001b
对bootpack.hrb的头部进行解析,EBX+16
表示的就是bootpack.hrb之后的第16号地址(可以使用二进制编辑器打开bootpack.hrb查看该地址内容)。
SHR
是向右移位指令,ECX,2
表示ECX >>= 2
。JZ(jump if zero)
根据ECX的结果判断是否跳转。
所做的memcpy操作就是将[EBX+20]
开始的[EBX+16]
大小的数据拷贝到[EBX+12]
。这样就开始执行bootpack.hrb了。
纸娃娃操作系统的内存分布:
0x0000 0000 ~ 0x000f ffff : 虽然在启动中会多次使用,但是之后会变为空(1MB)
0x0010 0000 ~ 0x0026 7fff : 用于保存软盘的内容(1440kb)
0x0026 8000 ~ 0x0026 f7ff : 空(30kb)
0x0026 f800 ~ 0x0026 ffff : IDT(2KB)
0x0027 0000 ~ 0x0027 ffff : GDT(64KB)
0x0028 0000 ~ 0x002f ffff : bootpack.hrb(512KB)
0x0030 0000 ~ 0x003f ffff : 栈及其他(1KB)
0x0040 0000 ~ : 空
BIOS和VRAM也会存储在最低地址的1M内,并不是直接变为空。正是最初先定好了这个内存分布图,才确定了将磁盘中的内容读到0x0010 0000地址后 ,也能确认了IDT和GDT的地址。
asmhead.nas中还有几个函数没有聊到:
# waitkbdout与wait_KBC_sendready类似
waitkbdout:IN AL,0x64AND AL,0x02IN AL,0x60 ; 清空数据接收缓冲区的垃圾内容(书上有,代码中无)JNZ waitkbdout ; AND的结果,如果不是0就跳转到waitkbdoutRET
最后的一部分代码:
ALIGNB 16 ; 强制16字节对齐
GDT0:RESB 8 ; NULL selectorDW 0xffff,0x0000,0x9200,0x00cf ;可读写的段(segment)32bitDW 0xffff,0x0000,0x9a28,0x0047 ;可执行的段(segment)32bit(bootpack使用)DW 0
GDTR0:DW 8*3-1DD GDT0ALIGNB 16
bootpack:
最初状态时,GDT在asmhead.nas里,并不在0x00270000 ~ 0x0027ffff的范围里,IDT也没有设定,所以仍旧不在禁止中断的状态。应当趁着硬件上积累过多数据而产生误动作前,放开中断,接收数据。因此,在bootpack.c的HariMain里,应该在进行调色板(palette)的初始化以及画面的准备之前,先重新创建GDT和IDT,初始化IPC,并执行io_sti();
相关文章:
Day8 鼠标控制与32位模式切换
文章目录 1. 例程harib05a(鼠标解读1)2. 例程harib05b(代码整理)3. 例程harib05c(鼠标解读2)4. 例程harib05d(移动鼠标指针)5. 通往32位模式之路 1. 例程harib05a(鼠标解…...
塔能科技:点亮节能之光,赋能工厂与城市
在能源形势日益严峻的当下,节能成为了各行各业的关键任务。工厂作为能耗大户,降低能耗迫在眉睫;市政照明作为城市运行的基本保障,也急需向绿色节能转型。塔能科技凭借其能源精准节能和定制开发的核心能力,为工厂节能和…...
UDP 报文结构与注意事项总结
目录 一、UDP报文结构简介 1. 源端口号(Source Port,16位): 2. 目的端口号(Destination Port,16位): 3. 长度(Length,16位): 4. 校…...
DBeaver CE 24.1.3 (Windows 64位) 详细安装教程
1. 下载安装包 dbeaver-ce-24.1.3-x86_64-setup.exe下载链接:https://pan.quark.cn/s/5a8dc9ad747f。 下载完成后,双击运行安装程序。 2. 运行安装向导 选择语言:安装程序启动后,选择安装语言(如英文或中文ÿ…...
Java多线程之线程控制
1、线程睡眠——sleep 如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法 注意如下几点问题 ①、sleep是静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运…...
任意波形发生器——2路同步DA模拟量输出卡
定义 任意波形发生器(Arbitrary Waveform Generator, AWG)是一种电子测试仪器,能够通过数字信号处理(DSP)和数模转换(DAC)技术生成非周期性、可编程的任意形状电信号。与传统函数发生器仅支持…...
【Java】 使用 HTTP 响应状态码定义web系统返回码
系统状态码定义 public interface GlobalErrorCodeConstants {ErrorCode SUCCESS new ErrorCode(0, "成功");// 客户端错误段 ErrorCode BAD_REQUEST new ErrorCode(400, "请求参数不正确");ErrorCode UNAUTHORIZED new ErrorCode(401, "账号未登…...
测试反馈陷入死循环?5大策略拆解新旧Bug难题
新旧Bug堆叠,测试反馈陷入死循环,如果不及时解决此问题,往往容易导致项目延期、成本增加、团队效率降低,直接影响产品的市场竞争力 。因此需及时应对此问题,进而保障项目进度如期进行,提升软件质量…...
结合大语言模型的机械臂抓取操作学习
结合大语言模型的机械臂抓取操作学习(完整ppt和代码)无视频 代码能正常运行时不负责答疑! 电子产品,一经出售,概不退换 算法设计、毕业设计、期刊专利!感兴趣可以联系我。 🏆代码获取方式1: 私信…...
待验证---Oracle 19c 在 CentOS 7 上的快速安装部署指南
Oracle 19c 在 CentOS 7 上的快速安装部署指南 Oracle Database 19c 是一个功能强大的企业级数据库系统,下面我将为您提供在 CentOS 7 上快速安装部署 Oracle 19c 的详细步骤。 一、准备工作 1. 系统要求 CentOS 7 (64位)最小内存: 2GB (推荐 8GB 以上)最小磁盘…...
风力发电领域canopen转Profinet网关的应用
在风力发电领域,开疆canopen转Profinet网关KJ-PNG-205的应用案例通常涉及将风力涡轮机内部的CANopen网络与外部的Profinet工业以太网连接起来。这种转换网关允许风力发电场的控制系统通过Profinet协议收集和监控涡轮机的状态信息,同时发送控制命令。 风力…...
vr全景相机如何选择?
VR全景相机,作为虚拟现实技术的核心设备之一,能够拍摄360度全景照片和视频,使用户通过虚拟现实设备身临其境地体验拍摄场景。 这种技术的快速发展,得益于传感器、图像处理和计算机视觉技术的不断进步。 选择一台合适的VR全景相机…...
在 Conda 中,包的安装路径在电脑的哪里
在 Conda 中,包的安装路径取决于你的 Conda 安装方式 和 环境类型(base 或其他虚拟环境)。以下是不同情况下的详细说明: 📌 1. Conda 包的默认安装路径 Conda 将所有包存储在 pkgs 目录 下,并在各个环境中…...
phpstorm用php连接数据库报错
项目场景: phpstorm用php连接数据库 问题描述 用php使用mysql_connect 的时候报错了,没有这个函数 原因分析: php解释器问题,后来查资料得知mysql_connct只适用于php5.5以下解释器。一开始用的7,改成5.3以后还是报…...
今日行情明日机会——20250428
指数依然在震荡区间,等待方向选择~ 2025年4月28日涨停主要行业方向分析 一、核心主线方向 一季报增长(业绩驱动资金避险) • 涨停家数:10家(最强方向)。 • 代表标的: ◦ 珀莱雅(2…...
Spring Boot 3与JDK 8环境下的单元测试实践指南
一、引言 在Java后端开发中,单元测试是保障代码质量的核心手段。Spring Boot作为主流框架,其单元测试体系随着版本迭代不断优化。本文聚焦于JDK 8与Spring Boot 3的组合场景,深入解析单元测试的多种实现方式,对比不同测试策略的异…...
微分与积分(前言)
导数 导数是一个非常重要的概念,先来看一个引例:速度问题。历史上速度问题与倒数概念的形成有着密切的关系。 平均速度 v s t v\frac{s}{t} vts那么如何表示瞬时速度呢? 瞬时经过路程: Δ s s ( t 0 Δ t ) − s ( t 0 ) Δ…...
61. Java 类和对象 - 使用 this 关键字
文章目录 61. Java 类和对象 - 使用 this 关键字1. 在方法或构造函数中引用对象成员1.1 区分同名变量1.2 在普通方法中引用字段或调用方法 2. 在构造函数中调用另一个构造函数示例:构造函数重载 3. 其他用法:返回当前对象4. 注意事项5. 总结 61. Java 类…...
安达发|高效智能塑料切割数控系统 - 全自动化软件解决方案
在当今的制造业中,塑料作为一种轻便、耐用且成本效益高的材料,被广泛应用于各个领域。随着科技的进步和市场需求的变化,塑料加工行业正面临着前所未有的挑战和机遇。为了提高生产效率,降低成本,并满足日益严格的质量标…...
20250428-AI Agent:智能体的演进与未来
目录 一、AI Agent的定义与演进 1.1 传统AI Agent的发展历程 1.2 现代AI Agent的技术突破 二、AI Agent的核心组件 2.1 大模型动态推理规划 2.2 工具系统的演进 2.3 记忆模块的设计 三、AI Agent的工作流程 3.1 感知阶段 3.2 推理阶段 3.3 决策阶段 3.4 执行阶段 …...
微信小程序分页和下拉刷新
在page.json中配置下拉刷新和底部距顶部的距离: {"path": "school-detail","style": {"navigationStyle": "custom","navigationBarTextStyle": "black","enablePullDownRefresh&quo…...
文献阅读(一)植物应对干旱的生理学反应 | The physiology of plant responses to drought
分享一篇Science上的综述文章,主要探讨了植物应对干旱的生理机制,强调通过调控激素信号提升植物耐旱性、保障粮食安全的重要性。 摘要 干旱每年致使农作物产量的损失,比所有病原体造成损失的总和还要多。为适应土壤中的湿度梯度变化&#x…...
开源CMS系统的SEO优化功能主要依赖哪些插件?
在当今互联网时代,搜索引擎优化(SEO)是网站获取流量的核心手段之一。开源内容管理系统(CMS)因其灵活性和丰富的插件生态,成为许多开发者和企业的首选。本文将以主流开源CMS为例,深入解析其SEO优…...
YUM/DNF管理工具
YUM (Yellow dog Updater, Modified) , RHEL8 中默认使用的软件批量管理工具由原版本的 yum 换成了速度更快的 dnf ( DNF Dandified YUM ),原有的 yum 命令仅为 dnf 的软链接,当然依旧可以使用。 [root…...
Deepseek 生成新玩法:从文本到可下载 Word 文档?思路与实践
大家好!最近有朋友问到,能不能用 Deepseek 这类强大的 AI 模型,根据一个包含具体格式要求(比如字体、字号、行距、对齐方式等)的提示词,直接生成一篇论文,并且输出一个能直接下载的 Word (.docx…...
【OSG学习笔记】Day 13: 事件处理——响应键盘与鼠标
在OpenSceneGraph (OSG) 中,事件处理是实现用户交互功能的重要部分。 通过自定义按键事件(如 WASD 键控制模型移动),可以让用户与场景进行互动。 osgGA::GUIEventHandler osgGA::GUIEventHandler 是 OpenSceneGraph (OSG) 中用…...
element-ui carousel 组件源码分享
carousel 走马灯源码简单分享,主要从以下几个方面: 1、carousel 组件页面结构。 2、carousel 组件属性。 3、carousel 组件事件。 4、carousel 组件方法。 5、carousel-item 组件属性。 一、组件页面结构。 二、组件属性。 2.1 height 走马灯的高…...
在视图中交互 闪退问题
程序闪退 //void mouseEventOccurred(const pcl::visualization::MouseEvent &event, // void* viewer_void) //{ // boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer *static_cast<boost::shared_ptr<pcl::visualization::PCLVisualizer> …...
python 线程池顺序执行
在Python中,线程池(ThreadPoolExecutor)默认是并发执行任务的,但若需要实现任务的顺序执行(按提交顺序执行或按结果顺序处理),可以通过以下方案实现: 方案一:强制单线程&…...
DeepSeek智能时空数据分析(六):大模型NL2SQL绘制城市之间连线
序言:时空数据分析很有用,但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要,然而,三大挑战仍制约其发展:技术门槛高,需融合GIS理论、SQL开发与时空数据库等多领域知识;空…...
[250428] Nginx 1.28.0 发布:性能优化、安全增强及新特性
目录 Nginx 1.28.0 稳定版发布主要亮点包括:功能增强:安全性改进:其他: Nginx 1.28.0 稳定版发布 Nginx 官方于 4 月 24 日发布了最新的 1.28.0 稳定版本。此版本基于之前的 1.27.x 主线分支,整合了多项新功能、性能优…...
第二章:Agent System
Chapter 2: Agent System 从用户界面到代理系统:背后的“大脑”如何运作? 在上一章的用户界面抽象中,我们已经能通过命令行与AI简单对话了。但你有没有好奇过:输入的问题是如何变成“北京今天晴,气温25C”这样的回答的…...
自动驾驶L4级技术落地:特斯拉、Waymo与华为的路线之争
自动驾驶L4级技术落地:特斯拉、Waymo与华为的路线之争 摘要 随着自动驾驶技术进入L4级(高度自动化驾驶)商业化探索的关键阶段,全球头部企业围绕技术路线与商业模式展开激烈竞争。特斯拉、Waymo与华为分别代表视觉优先、全栈自研…...
K8s新手系列之K8s中的资源
K8s中资源的概念 在kubernetes中,所有的内容都抽象为资源,用户需要通过操作资源来管理kubernetes。 kubernetes的本质上就是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,其实就是在kubernetes集群中运行…...
万亿参数大模型网络瓶颈突破:突破90%网络利用率的技术实践
AI数据中心网络热潮下,如何突破传统以太网利用率瓶颈? 近年来,随着AI大模型训练(如GPT-4、Gemini)的爆发式增长,数据中心网络的流量压力急剧上升。单次训练任务可能涉及数千张GPU卡协同工作,生成…...
【KWDB 创作者计划】_企业数据管理的利刃:技术剖析与应用实践
【KWDB 创作者计划】_企业数据管理的利刃:技术剖析与应用实践 引言 作为一名在企业级开发领域摸爬滚打多年的开发者,见证了数据库技术的不断迭代与革新,众多数据库产品中,KWDB 以其独特的技术架构和卓越性能吸引了我的目光。本文将…...
vue复习91~135
1.空仓库 vuex的空仓库,写在store>index.js里面 语法:new Vuex.store 最后在main.js中导入挂载 import Vue from vue import Vuex from vuex //插件安装 Vue.use(Vuex) //创建仓库 export default new Vuex.Store() //导出main.js使用 export defau…...
正常流布局
布局决定了元素的排列方式。如果让浏览器按照默认方式排列,这叫做正常流(normal flow)布局。正常布局是怎么排列元素的呢?各行从上到下,行内从左到右。 那么什么情况下会开始新的一行呢?块元素会产生新行。…...
图论---拓扑排序(DFS)
时间复杂度: 最坏情况下为O(V!),其中V是顶点数 实际运行时间取决于图的拓扑结构 这个实现可以输出有向无环图的所有可能的拓扑排序,并能检测图中是否存在环。 算法思想: 使用回溯法枚举所有可能的拓扑排序 在每一步选择当前入…...
探索 Redis 缓存对系统性能的提升——项目启动与操作指南
探索 Redis 缓存对系统性能的提升——项目启动与操作指南 一、项目简介 Redis是一款高性能的键值存储数据库,以其出色的读写速度和丰富的数据结构著称,被广泛用作应用系统的缓存层。作为缓存,Redis通过将热点数据存储在内存中,显…...
第十四届蓝桥杯Scratch03月stema选拔赛——九九乘法表
题目可点击下方地址查看,支持在线编程,获取素材和源码~ 九九乘法表_scratch_少儿编程题库学习中心-嗨信奥https://www.hixinao.com/tiku/scratch/show-3789.html?_shareid3 程序演示可点击下方地址查看,可获取源码~…...
【优选算法-二分查找】二分查找算法解析:如何通过二段性优化搜索效率
算法相关知识点可以通过点击以下链接进行学习一起加油!双指针滑动窗口 在本篇文章中,我们将深入解析二分查找算法的核心原理。从基本概念到实际应用,带你了解如何利用二分查找高效定位元素,提升搜索效率。无论你是刚接触算法的新手…...
如何搭建spark yarn模式的集群
一、基础环境准备 安装JDK 1.8 所有节点需安装JDK并配置环境变量,确保JAVA_HOME正确指向安装路径14。部署Hadoop集群 安装Hadoop(推荐3.x版本),配置YARN资源管理器4。在yarn-site.xml中启用资源调度: <pro…...
OpenResty深度解析:从卓伊凡的”隐形主流”论看其深度原理与应用生态-卓伊凡
OpenResty深度解析:从卓伊凡的”隐形主流”论看其深度原理与应用生态-卓伊凡 一、OpenResty技术概述:悄然成为基础设施的”隐形冠军” 1.1 OpenResty的”附带安装”现象 正如技术观察者卓伊凡在其《现代Web基础设施的隐形架构》一文中首次提出的观点:”OpenResty正在以一…...
CSS 预处理器与模块化:Sass/LESS 实战技巧
CSS 预处理器与模块化:Sass/LESS 实战技巧 引言 在现代前端开发中,CSS 预处理器已成为构建可维护、可扩展前端项目的核心工具。随着项目规模扩大,原生 CSS 的局限性日益明显:缺乏变量、嵌套结构和模块化机制导致代码冗余、难以维…...
杰里芯片 7083G 之通话数据dump
前期准备工作: 硬件:杰里test_audio 开发板 读卡器 SD卡 软件:dump 脚本 通过网盘分享的文件:PCM写卡工具使用说明和数据导出脚本.rar 链接: https://pan.baidu.com/s/18fSxMPe-gmPtHlJekUK4yw 提取码: c54i 制作调试固件&…...
Redis ⑦-set | Zset
set类型基本介绍 set 为集合,该集合为无序集合,可以存储多个不同的数据类型,包括字符串、整数、浮点数等。 集合中的元素是唯一的,不可重复。 set类型常用命令 SADD SADD key member [member...]集合中的值称为 member将一个…...
在线图书管理系统的结构化需求分析过程讲解
一、引言 结构化分析是一种面向数据流进行需求分析的方法,其总体步骤包括: 1. 需求获取; 2. 分析建模; 3. 需求文档化; 4. 需求验证与评审。 本文将以在线图书管理系统为例,详细展示按照这些步骤进行…...
【Linux】基于环形队列的生产消费者模型
个人主页~ 基于环形队列的生产消费者模型 一、POSIX信号量1、概述2、调用接口(一)初始化信号量(二)销毁信号量(三)等待信号量(四)发布信号量 3、在环形队列中的作用 二、基于环形队列…...
如何实现Kafka的Exactly-Once语义?
Kafka 的 Exactly-Once(精确一次)语义是分布式消息系统中最高等级的数据一致性保证,包含三个层面的含义: 消息不会丢失消息不会重复消费消息处理结果具有确定性 模式局限性: 这里模式有个问题,会导致性能…...