【读书笔记-《30天自制操作系统》-22】Day23
本篇内容比较简单,集中于显示问题。首先编写了应用程序使用的api_malloc,然后实现了在窗口中画点与画线的API与应用程序。有了窗口显示,还要实现关闭窗口的功能,于是在键盘输入API的基础上实现了按下按键关闭窗口。最后发现用上文的强制结束按键结束应用程序,程序的窗口还没有关闭,又增加了强制结束程序时关闭窗口的功能。
1. 实现api_malloc
在上一篇中,显示窗口定义了char buf[150 * 50]。编译之后,相当于增加了150*50个字节的0x00,导致编译后的应用程序大了很多。为了精简应用程序的大小,这里改为使用分配的内存空间。
为应用程序分配内存空间,不能直接使用操作系统管理内存的memman_alloc函数。因为应用程序不能直接读写操作系统管理的内存,这样会产生异常并强制结束应用程序。应用程序可以读写的仅仅是操作系统为其分配好的内存空间,位于数据段中。
这里我们在应用程序中写好需要使用的内存大小。指定内存大小的数值后,会和栈等的大小相累加,写入到.hrb文件最开头的4个字节中。这个内存空间在数据段中的开始位置,被保存在.hrb文件的0x0020处。
设计API如下:
menman初始化:
- EDX = 8
- EBX = memman的地址
- EAX = memman所管理内存空间的起始地址
- ECX = memman所管理的内存空间的字节数
malloc:
- EDX = 9
- EBX = memman的地址
- ECX = 需要请求的字节数
- EAX = 分配到的内存空间地址
free:
- EDX = 10
- EBX = memman的地址
- EAX = 需要释放的内存空间地址
- ECX = 需要释放的字节数
根据以上设计,在hrb_api中增加以下处理:
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{int ds_base = *((int *) 0xfe8);struct TASK *task = task_now();struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);struct SHEET *sht;int *reg = &eax + 1; /* reg[0] : EDI, reg[1] : ESI, reg[2] : EBP, reg[3] : ESP *//* reg[4] : EBX, reg[5] : EDX, reg[6] : ECX, reg[7] : EAX */if (edx == 1) {cons_putchar(cons, eax & 0xff, 1);} else if (edx == 2) {cons_putstr0(cons, (char *) ebx + ds_base);} else if (edx == 3) {cons_putstr1(cons, (char *) ebx + ds_base, ecx);} else if (edx == 4) {return &(task->tss.esp0);} else if (edx == 5) {sht = sheet_alloc(shtctl);sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);sheet_slide(sht, 100, 50);sheet_updown(sht, 3); reg[7] = (int) sht;} else if (edx == 6) {sht = (struct SHEET *) ebx;putfonts8_asc(sht->buf, sht->bxsize, esi, edi, eax, (char *) ebp + ds_base);sheet_refresh(sht, esi, edi, esi + ecx * 8, edi + 16);} else if (edx == 7) {sht = (struct SHEET *) ebx;boxfill8(sht->buf, sht->bxsize, ebp, eax, ecx, esi, edi);sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);} else if (edx == 8) {memman_init((struct MEMMAN *) (ebx + ds_base));ecx &= 0xfffffff0; /* 以16字节为单位 */memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);} else if (edx == 9) {ecx = (ecx + 0x0f) & 0xfffffff0; /* 以16字节为单位进位取整 */reg[7] = memman_alloc((struct MEMMAN *) (ebx + ds_base), ecx);} else if (edx == 10) {ecx = (ecx + 0x0f) & 0xfffffff0; /* 以16字节为单位进位取整 */memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);}return 0;
}
应用程序调用的api:
_api_initmalloc: ; void api_initmalloc(void);PUSH EBXMOV EDX,8MOV EBX,[CS:0x0020] ; malloc内存空间的地址MOV EAX,EBXADD EAX,32*1024 ; 加上32KBMOV ECX,[CS:0x0000] ; 数据段的大小SUB ECX,EAXINT 0x40POP EBXRET_api_malloc: ; char *api_malloc(int size);PUSH EBXMOV EDX,9MOV EBX,[CS:0x0020]MOV ECX,[ESP+8] ; sizeINT 0x40POP EBXRET_api_free: ; void api_free(char *addr, int size);PUSH EBXMOV EDX,10MOV EBX,[CS:0x0020]MOV EAX,[ESP+ 8] ; addrMOV ECX,[ESP+12] ; sizeINT 0x40POP EBXRET
应用程序:
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_end(void);void HariMain(void)
{char *buf;int win;api_initmalloc();buf = api_malloc(150 * 50);win = api_openwin(buf, 150, 50, -1, "hello");api_boxfilwin(win, 8, 36, 141, 43, 6 /* 浅蓝色 */);api_putstrwin(win, 28, 28, 0 /* 黑色 */, 12, "hello, world");api_end();
}
运行程序之后可以实现同样的功能,但是应用程序文件大大减小了。
2. 画点、线与刷新窗口
接下来就进入图形处理,分别来描绘点和线。
1.1 描绘点
在窗口中画点的设计很简单:
- EDX = 11
- EBX = 窗口句柄
- ESI = 显示位置的x坐标
- EDI = 显示位置的y坐标
- EAX = 色号
应用程序调用的api和应用程序的代码也比较简单。
应用程序调用API:
_api_point: ; void api_point(int win, int x, int y, int col);PUSH EDIPUSH ESIPUSH EBXMOV EDX,11MOV EBX,[ESP+16] ; winMOV ESI,[ESP+20] ; xMOV EDI,[ESP+24] ; yMOV EAX,[ESP+28] ; colINT 0x40POP EBXPOP ESIPOP EDIRET
操作系统API:
……
else if (edx == 11)
{sht = (struct SHEET *) ebx;sht->buf[sht->bxsize * edi + esi] = eax;sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
}
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_point(int win, int x, int y, int col);
void api_end(void);void HariMain(void)
{char *buf;int win;api_initmalloc();buf = api_malloc(150 * 100);win = api_openwin(buf, 150, 100, -1, "star1");api_boxfilwin(win, 6, 26, 143, 93, 0 /* 黑色*/);api_point(win, 75, 59, 3 /* 黄色 */);api_end();
}
实现的功能也比较简单,只是在窗口中显示一个点:
还可以用随机数的方式在窗口中显示多个点:
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_point(int win, int x, int y, int col);
void api_end(void);int rand(void); /* 0-32767之间的随机数 */void HariMain(void)
{char *buf;int win, i, x, y;api_initmalloc();buf = api_malloc(150 * 100);win = api_openwin(buf, 150, 100, -1, "stars");api_boxfilwin(win, 6, 26, 143, 93, 0 /* 黑色 */);for (i = 0; i < 50; i++) {x = (rand() % 137) + 6;y = (rand() % 67) + 26;api_point(win, x, y, 3 /* 黄色*/);}api_end();
}
在上面生成多个点的程序中,每生成一个点都会执行一次刷新窗口的操作。其实可以在描绘完所有点之后统一进行一次刷新。刷新窗口的句柄是struct SHEET的地址,是一个偶数。这样可以在所有窗口绘图命令中设置一个选项和一个仅用于刷新的API:
……
else if (edx == 11)
{sht = (struct SHEET *) (ebx & 0xfffffffe);sht->buf[sht->bxsize * edi + esi] = eax;if ((ebx & 1) == 0) {sheet_refresh(sht, esi, edi, esi + 1, edi + 1);}
}
else if (edx == 12)
{sht = (struct SHEET *) ebx;sheet_refresh(sht, eax, ecx, esi, edi);
}
……
以上的处理,当ebx结尾为0,即偶数时,执行刷新窗口的操作。应用程序调用的执行刷新的API:
_api_refreshwin: ; void api_refreshwin(int win, int x0, int y0, int x1, int y1);PUSH EDIPUSH ESIPUSH EBXMOV EDX,12MOV EBX,[ESP+16] ; winMOV EAX,[ESP+20] ; x0MOV ECX,[ESP+24] ; y0MOV ESI,[ESP+28] ; x1MOV EDI,[ESP+32] ; y1INT 0x40POP EBXPOP ESIPOP EDIRET
修改后的应用程序:
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_point(int win, int x, int y, int col);
void api_end(void);int rand(void); /* 0-32767的随机数 */void HariMain(void)
{char *buf;int win, i, x, y;api_initmalloc();buf = api_malloc(150 * 100);win = api_openwin(buf, 150, 100, -1, "stars");api_boxfilwin(win, 6, 26, 143, 93, 0 /* 黑色 */);for (i = 0; i < 50; i++) {x = (rand() % 137) + 6;y = (rand() % 67) + 26;api_point(win, x, y, 3 /* 黄色 */);}api_end();
}
1.2 描绘直线
描绘直线的基本方法:
for(i = 0; i < len; i++)
{
api_point(win, x, y, col);
x += dx;
y += dy;
}
len为线的长度,x,y为线的起始坐标,dx和dy表示线延伸的方向。
dx和dy既不能太大,导致线看起来像虚线,又不能太小,导致CPU多次在同一个坐标画点。
程序如下:
……
else if (edx == 13)
{sht = (struct SHEET *) (ebx & 0xfffffffe);hrb_api_linewin(sht, eax, ecx, esi, edi, ebp);if ((ebx & 1) == 0) {sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);}
}
……
void hrb_api_linewin(struct SHEET *sht, int x0, int y0, int x1, int y1, int col)
{int i, x, y, len, dx, dy;dx = x1 - x0;dy = y1 - y0;x = x0 << 10;y = y0 << 10;if (dx < 0) {dx = - dx;}if (dy < 0) {dy = - dy;}if (dx >= dy) {len = dx + 1;if (x0 > x1) {dx = -1024;} else {dx = 1024;}if (y0 <= y1) {dy = ((y1 - y0 + 1) << 10) / len;} else {dy = ((y1 - y0 - 1) << 10) / len;}} else {len = dy + 1;if (y0 > y1) {dy = -1024;} else {dy = 1024;}if (x0 <= x1) {dx = ((x1 - x0 + 1) << 10) / len;} else {dx = ((x1 - x0 - 1) << 10) / len;}}for (i = 0; i < len; i++) {sht->buf[(y >> 10) * sht->bxsize + (x >> 10)] = col;x += dx;y += dy;}return;
}
当前还不能处理小数。为了处理dx,dy更加精确,采用了将x和y预先扩大1024倍的方法,这样增加1024实际上就是增加1。
可以看出计算长度的方法比较粗糙,通过比较起点和终点的x,y坐标,将变化比较大的一方加上1作为长度len。这是因为当起点与终点相同时,需要在画面上显示一个点。而计算dx和dy时应用了一个小技巧:根据直线延伸的方向,将变化较大的一方设为1024或-1024,而将变化较小的一方的变化量除以len。这样的计算方式能够使描绘出的直线更平滑一些。
应用程序代码:
_api_linewin: ; void api_linewin(int win, int x0, int y0, int x1, int y1, int col);PUSH EDIPUSH ESIPUSH EBPPUSH EBXMOV EDX,13MOV EBX,[ESP+20] ; winMOV EAX,[ESP+24] ; x0MOV ECX,[ESP+28] ; y0MOV ESI,[ESP+32] ; x1MOV EDI,[ESP+36] ; y1MOV EBP,[ESP+40] ; colINT 0x40POP EBXPOP EBPPOP ESIPOP EDIRET
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
void api_end(void);void HariMain(void)
{char *buf;int win, i;api_initmalloc();buf = api_malloc(160 * 100);win = api_openwin(buf, 160, 100, -1, "lines");for (i = 0; i < 8; i++) {api_linewin(win + 1, 8, 26, 77, i * 9 + 26, i);api_linewin(win + 1, 88, 26, i * 9 + 88, 89, i);}api_refreshwin(win, 6, 26, 154, 90);api_end();
}
运行程序,绘制出了几条直线:
3. 键盘输入API
接下来我们来做通过键盘按键结束API的功能。
键盘输入
- EDX = 15
- EAX = 0——没有键盘输入时返回-1,不休眠
- EAX = 1——休眠直到发生键盘输入
- EAX = 输入的字符编码
else if (edx == 15) {for (;;) {io_cli();if (fifo32_status(&task->fifo) == 0) {if (eax != 0) {task_sleep(task); /* FIFO为空,休眠并等待 */} else {io_sti();reg[7] = -1;return 0;}}i = fifo32_get(&task->fifo);io_sti();if (i <= 1) { /* 光标用定时器 *//*应用程序运行时不需要显示光标,因此总是将下次显示用的值置为1 */timer_init(cons->timer, &task->fifo, 1); /* 下次置为1 */timer_settime(cons->timer, 50);}if (i == 2) { /* 光标ON */cons->cur_c = COL8_FFFFFF;}if (i == 3) { /* 光标OFF */cons->cur_c = -1;}if (256 <= i && i <= 511) { /* 键盘数据(通过任务A) */reg[7] = i - 256;return 0;}}}
由于应用程序窗口的光标显示需要用到定时器,于是也将其包含到struct CONSOLE之中了。
struct CONSOLE {struct SHEET *sht;int cur_x, cur_y, cur_c;struct TIMER *timer;
};
键盘输入的API如下:
_api_getkey: ; int api_getkey(int mode);MOV EDX,15MOV EAX,[ESP+4] ; modeINT 0x40RET
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
void api_closewin(int win);
int api_getkey(int mode);
void api_end(void);void HariMain(void)
{char *buf;int win, i;api_initmalloc();buf = api_malloc(160 * 100);win = api_openwin(buf, 160, 100, -1, "lines");for (i = 0; i < 8; i++) {api_linewin(win + 1, 8, 26, 77, i * 9 + 26, i);api_linewin(win + 1, 88, 26, i * 9 + 88, 89, i);}api_refreshwin(win, 6, 26, 154, 90);for (;;) {if (api_getkey(1) == 0x0a) {break; /* 按下回车键则break; */}}api_closewin(win);api_end();
}
运行程序,显示出直线;按下回车键,窗口消失。
接下来用键盘实现一个小程序:
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
void api_closewin(int win);
int api_getkey(int mode);
void api_end(void);void HariMain(void)
{char *buf;int win, i, x, y;api_initmalloc();buf = api_malloc(160 * 100);win = api_openwin(buf, 160, 100, -1, "walk");api_boxfilwin(win, 4, 24, 155, 95, 0 /* 黑色 */);x = 76;y = 56;api_putstrwin(win, x, y, 3 /* 黄色 */, 1, "*");for (;;) {i = api_getkey(1);api_putstrwin(win, x, y, 0 /* 黑色 */, 1, "*"); /* 用黑色擦除 */if (i == '4' && x > 4) { x -= 8; }if (i == '6' && x < 148) { x += 8; }if (i == '8' && y > 24) { y -= 8; }if (i == '2' && y < 80) { y += 8; }if (i == 0x0a) { break; } /* 按回车结束 */api_putstrwin(win, x, y, 3 /* 黄色 */, 1, "*");} api_closewin(win);api_end();
}
运行程序,窗口中的’*'在按下2,4,6,8键时可以上下左右地移动。
4. 关闭窗口
最后还有一个问题,使用上一篇完成的Shift+F1按键结束程序时,程序结束后窗口仍然保留在屏幕上。因为上一篇的强制结束按键在结束程序后没有消除窗口,因此这一结果也在预料之中。
解决这个问题也很简单。在struct SHEET中添加一个用来存放task的成员,当应用程序结束时,查询所有的图层,找到图层中与结束的程序一样的task,将该图层关闭就可以了。
修改struct SHEET:
struct SHEET {unsigned char *buf;int bxsize, bysize, vx0, vy0, col_inv, height, flags;struct SHTCTL *ctl;struct TASK *task;
};
需要修改的程序代码:
struct SHEET *sheet_alloc(struct SHTCTL *ctl)
{struct SHEET *sht;int i;for (i = 0; i < MAX_SHEETS; i++) {if (ctl->sheets0[i].flags == 0) {sht = &ctl->sheets0[i];sht->flags = SHEET_USE; /* 正在使用 */sht->height = -1; /* 不显示 */sht->task = 0; /* 不使用自动关闭功能 */return sht;}}return 0;
}
……} else if (edx == 5) {sht = sheet_alloc(shtctl);sht->task = task; //给sht->task赋值sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);sheet_slide(sht, 100, 50);sheet_updown(sht, 3); reg[7] = (int) sht;}
……
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
……struct SHTCTL *shtctl;struct SHEET *sht;
……if (finfo != 0) {/* 找到文件的情况 */p = (char *) memman_alloc_4k(memman, finfo->size);file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {……start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));shtctl = (struct SHTCTL *) *((int *) 0x0fe4);for (i = 0; i < MAX_SHEETS; i++) {sht = &(shtctl->sheets0[i]);if (sht->flags != 0 && sht->task == task) {/* 找到应用程序遗留的窗口 */sheet_free(sht); /* 关闭该窗口*/}}memman_free_4k(memman, (int) q, segsiz);} else {cons_putstr0(cons, ".hrb file format error.\n");}memman_free_4k(memman, (int) p, finfo->size);cons_newline(cons);return 1;}return 0;
}
这样在运行程序时,按下Shift+F1,结束程序的同时也可以关闭窗口了。
本篇内容比较简单,思路清晰,程序代码也不需要多作解释,大多还是利用前面完成的内容。下一篇中将继续开发窗口的功能,敬请期待。
相关文章:

【读书笔记-《30天自制操作系统》-22】Day23
本篇内容比较简单,集中于显示问题。首先编写了应用程序使用的api_malloc,然后实现了在窗口中画点与画线的API与应用程序。有了窗口显示,还要实现关闭窗口的功能,于是在键盘输入API的基础上实现了按下按键关闭窗口。最后发现用上文…...

C++学习笔记(33)
三十五、栈 示例: #include <iostream> using namespace std; typedef int ElemType; // 自定义链栈的数据元素为整数。 struct SNode // 链栈的结点。 { ElemType data; // 存放结点的数据元素。 struct SNode* next; // 指向下一个结点的指针。 }; // 初始化…...

智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
文章目录 1 效果展示2 CogVideoX 前世今生3 CogVideoX 部署实践流程3.1 创建丹摩实例3.2 配置环境和依赖3.3 模型与配置文件3.4 运行4 遇到问题 1 效果展示 A street artist, clad in a worn-out denim jacket and a colorful bandana, stands before a vast concrete wall in …...

探索Java中的设计模式:原则与实例
探索Java中的设计模式:原则与实例 大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天,我们将深入探讨Java中的设计模式,包括一些关键的设计原则和具体的实例。设计…...

【Java】关键字-static【主线学习笔记】
文章目录 前言关键字:static静态变量静态方法设置为静态的场景 下一篇 前言 Java是一门功能强大且广泛应用的编程语言,具有跨平台性和高效的执行速度,广受开发者喜爱。在接下来的学习过程中,我将记录学习过程中的基础语法、框架和…...

数字自然资源领域的实现路径
在数字化浪潮的推动下,自然资源的管理与利用正经历着前所未有的变革。本文将从测绘地理信息与遥感专业的角度,深度分析数字自然资源领域的实现路径。 1. 基础数据的数字化 数字自然资源的构建,首先需要实现基础数据的数字化。这包括地形地貌…...

GitLab邮箱发送邮件:如何实现自动化发信?
gitlab邮箱发送邮件设置教程?Gitlab邮箱配置和使用? GitLab不仅提供了代码版本控制、持续集成/持续部署等功能,还支持通过其内置的邮件功能实现自动化邮件发送。AokSend将深入探讨如何在GitLab中配置和使用邮箱发送邮件功能。 GitLab邮箱发…...

sqli-labs靶场搭建
下载了一个phpstudy进行搭靶场搭建 然后打开phpstudy安装好php,mysql等环境 正式sqli-labs靶场搭建 第一步:下载源码:https://codeload.github.com/Audi-1/sqli-labs/zip/master 解压后放进网站根目录,进到 sqli-labs的文件夹下࿰…...

Leetcode Hot 100刷题记录 -Day14(矩阵置0)
矩阵置0 问题描述: 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0。 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例 2:…...

每日刷题(算法)
我们N个真是太厉害了 思路: 我们先给数组排序,如果最小的元素不为1,那么肯定是吹牛的,我们拿一个变量记录前缀和,如果当前元素大于它前面所有元素的和1,那么sum1是不能到达的值。 代码: #def…...

大牛直播SDK核心音视频模块探究
技术背景 视沃科技旗下”大牛直播SDK”,始于2015年,致力于传统行业极致体验的音视频直播技术解决方案,产品涵盖跨平台的实时RTMP推流、RTMP/RTSP直播播放(支持RTSP|RTMP H.265,Enhanced RTMP H.265)、GB28181设备接入、推送端播放…...

gin配置swagger文档
一、基本准备工作 1、安装依赖包 go get -u github.com/swaggo/swag/cmd/swag go get -u github.com/swaggo/gin-swagger go get -u github.com/swaggo/files2、在根目录上配置swagger的路由文件 //2.初始化路由router : initialize.Routers()// 配置swaggerdocs.SwaggerInfo…...

基于ssm的快餐店点餐系统设计与实现
需要项目源码请联系我,目前有各类成品 毕设 javaweb ssh ssm springboot等等项目框架,源码丰富。 专业团队,咨询就送开题报告,活动限时免费,有需要的朋友可以来留言咨询。 一、摘要 进入二十一世纪以来,计…...

集合框架底层使用了什么数据结构
1.是什么 集合框架(Collection Framework)是Java标准库的一部分,它提供了一系列接口和实现类,用于处理不同类型的集合。这些集合可以用于存储和操作对象,如列表、集合、映射等。集合框架的底层数据结构是多种多样的&am…...

Activiti7《第二式:破剑式》——工作流中的以柔克刚
冲冲冲!开干 这篇文章将分为九个篇章,带你逐步掌握工作流的核心知识。这篇文章将带你深入探讨工作流中的 “破剑式”,揭示如何通过 柔与刚 的结合来破解工作流的复杂性。本篇包含了 Activiti7 环境的进一步优化和表结构的深入分析࿰…...

docker快速搭建kafka
1、拉取镜像 kafka和 zk镜像 docker pull wurstmeister/zookeeper docker pull wurstmeister/kafka:1.1.02、运行zk容器 docker run -itd --restart always --name zookeeper -p 2181:2181 wurstmeister/zookeeper3、运行kafka容器 192.168.31.112 这个地址为zk地址 docker…...

基于 onsemi NCV78343 NCV78964的汽车矩阵式大灯方案
一、方案描述 大联大世平集团针对汽车矩阵大灯,推出 基于 onsemi NCV78343 & NCV78964的汽车矩阵式大灯方案。 开发板搭载的主要器件有 onsemi 的 Matrix Controller NCV78343、LED Driver NCV78964、Motor Driver NCV70517、以及 NXP 的 MCU S32K344。 二、开…...

OpenAl o1论文:Let’s Verify Step by Step 快速解读
OpenAl又火了,这次是o1又带给大家惊艳。网上的博主已经有了真真假假的各种评测,我这篇来一点硬核的,解读OpenAl o1背后的论文:Let’s Verify Step by Step 大模型在给定的上下文资料正确的情况下也有可能出现幻觉,这篇…...

Errorresponsefromdaemon:toomanyrequests:Youhavereachedyourpullratelimit.
Errorresponsefromdaemon:toomanyrequests:Youhavereachedyourpullratelimit.Youmayincreasethelimitbyauthenticatingandupgrading:https://www.docker.com/increase−rate−limit.See ′ dockerrun−−help 在拉取docker进行的时候遇到这个问题,如何解决呢?本文提供的解决方…...

[2025]医院健康陪诊系统(源码+定制+服务)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...

Golang | Leetcode Golang题解之第405题数字转换为十六进制数
题目: 题解: func toHex(num int) string {if num 0 {return "0"}sb : &strings.Builder{}for i : 7; i > 0; i-- {val : num >> (4 * i) & 0xfif val > 0 || sb.Len() > 0 {var digit byteif val < 10 {digit 0…...

VB中如何使用正则表达式(Regular Expressions)
在Visual Basic (VB) 中使用正则表达式,你需要依赖于.NET Framework的System.Text.RegularExpressions命名空间。这个命名空间提供了一系列的类,其中最常用的是Regex类,它允许你在VB.NET中执行复杂的文本搜索和替换操作。 以下是如何在VB.NE…...

Docker FROM 指定基础镜像
所谓定制镜像,其一定是以一个镜像为基础,在其上进行定制。 比如一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必…...

19:I2C一:程序模拟I2C通信时序
I2C 1、什么是I2C2、I2C的通信时序2.1:起始信号2.2:停止信号2.3:主机向从机发送一个字节数据2.4:主机向从机读取一个字节数据2.5:主机接收应答2.6:主机发送应答 3、程序模拟I2C的通信时序3.1:指…...

最佳实践 · MySQL 分区表实战指南
引言 在数据量急剧增长的今天,传统的数据库管理方式可能无法有效处理海量数据的存储和查询需求。MySQL 提供了分区表功能,这不仅能够帮助优化性能,还能简化数据管理过程。分区表允许将数据表拆分成多个逻辑上的分区,每个分区可以…...

详细介绍 Redis 列表的应用场景
Redis 的列表(List)数据类型有以下一些主要应用场景: 一、消息队列 生产者-消费者模式 生产者可以使用LPUSH命令将消息从列表的左端(头部)插入到 Redis 列表中。消费者则使用BRPOP或RPOP命令从列表的右端(…...

游戏如何检测加速外挂
在游戏面临的众多外挂风险中,除了常见的内存修改挂、注入挂等作弊手段,黑灰产还常用「加速」手段实现作弊。 游戏安全风险分布占比图 「加速」顾名思义是指改变游戏内的速度。游戏在运行中需要以帧为单位播放画面,而计算每帧动画播放所需时间…...

【STM32 HAL库】OLED显示模块
【STM32 HAL库】OLED显示模块 前言理论OLED基本参数OLED基本驱动原理OLED坐标轴 应用CubeMx配置底层函数代码高层封装函数printf显示函数 前言 本文为笔者学习 OLED 的总结,基于keysking的视频内容,如有错误,欢迎指正 理论 OLED基本参数 …...

Redis---卸载Redis
简介 在Linux系统或者Mac系统卸载Redis。 步骤 1、停止Redis服务 #查看Redis服务进行 ps -ef | grep redis #停止Redis服务 redis-cli -a 111111 -p 6370 shutdown #再次查看Redis服务进程 ps -ef | grep redis2、删除/usr/local/bin目录下与Redis相关的文件 #查找Redis相…...

《C++模板元编程实战》阅读记录
目录 写在前面基本介绍第一部分 元编程基础技术第1章 基本技巧1.1元函数与type_traits1.1.1 元函数介绍 写在前面 这本书之前是在一片公众号里面介绍的,我觉的不错,想着提高一下自己C的水平,就买了一本,大概是2022年下半年买的&a…...