【读书笔记-《30天自制操作系统》-25】Day26

news2024/9/29 20:29:25

本篇仍然是围绕着命令行窗口做文章。首先优化命令行窗口的移动速度,然后增加多个命令行窗口功能。接着优化了命令行窗口的关闭,最后增加了两个命令start与ncst。
在这里插入图片描述

1. 优化命令行窗口移动速度

首先对命令行窗口的移动速度进行优化。主要的优化点有以下几个:

(1) sheet_refreshmap函数中过多的不必要的if语句

sheet_refreshmap函数相关代码如下:

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
{
	int h, bx, by, vx, vy, bx0, by0, bx1, by1;
	unsigned char *buf, *vram = ctl->vram, *map = ctl->map, sid;
	struct SHEET *sht;
	if (vx0 < 0) { vx0 = 0; }
	if (vy0 < 0) { vy0 = 0; }
	if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }
	if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }
	for (h = h0; h <= h1; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		sid = sht - ctl->sheets0;
		bx0 = vx0 - sht->vx0;
		by0 = vy0 - sht->vy0;
		bx1 = vx1 - sht->vx0;
		by1 = vy1 - sht->vy0;
		if (bx0 < 0) { bx0 = 0; }
		if (by0 < 0) { by0 = 0; }
		if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }
		if (by1 > sht->bysize) { by1 = sht->bysize; }
		for (by = by0; by < by1; by++) {
			vy = sht->vy0 + by;
			for (bx = bx0; bx < bx1; bx++) {
				vx = sht->vx0 + bx;
				//执行多次的if语句
				if (map[vy * ctl->xsize + vx] == sid) {
					vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
				}
			}
		}
	}
	return;
}

可以看出其中一条if语句位于三层循环之中,会执行成千上万次,如果能够去掉的话,速度应该会有不小的提升。

这个语句的作用是判断图层是否为透明部分,这主要是对于鼠标来说的。鼠标的显示图层整体是一个矩形,箭头之外的部分是透明的,这样显示出来才是一个箭头;而对于窗口等其他图层来说,不存在透明的部分。因此我们在进入循环之前可以先判断图层是否有透明部分,如果没有的话就不需要执行if语句了。

		if (sht->col_inv == -1) {
			/* 无透明图层,可以去掉if语句提高速度 */
			for (by = by0; by < by1; by++) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0 + bx;
					map[vy * ctl->xsize + vx] = sid;
				}
			}
		} else {
			/* 有透明图层,仍需要执行if语句 */
			for (by = by0; by < by1; by++) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0 + bx;
					if (buf[by * sht->bxsize + bx] != sht->col_inv) {
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		}

在QEMU下运行可能没有太明显的感觉,但是这种逻辑上的修改肯定是更为优化的。

(2) 将一次写入一个字节的MOV指令替换为一次写入四个字节的MOV指令

上面的代码中有一句:

map[vy * ctl->xsize + vx] = sid;

其作用是向某个内存地址写入sid的值。这条语句位于for循环中,后面的很多内存地址都要执行同样的操作。如果将这样一次写入一个字节替换为一次写入4个字节,执行一条指令的时间仍然是一样的,这样写入的速度就大大加快了。

if (sht->col_inv == -1) 
{
	if ((sht->vx0 & 3) == 0 && (bx0 & 3) == 0 && (bx1 & 3) == 0) 
	{
		/* 无透明色,一次写入4字节 */
		bx1 = (bx1 - bx0) / 4; /* MOV次数 */
		sid4 = sid | sid << 8 | sid << 16 | sid << 24;
		for (by = by0; by < by1; by++) 
		{
			vy = sht->vy0 + by;
			vx = sht->vx0 + bx0;
			p = (int *) &map[vy * ctl->xsize + vx];
			for (bx = 0; bx < bx1; bx++) 
			{
				p[bx] = sid4;
			}
		}
	} 
	else 
	{
		/* 无透明色,一次写入1字节 */
		for (by = by0; by < by1; by++) 
		{
			vy = sht->vy0 + by;
			for (bx = bx0; bx < bx1; bx++) 
			{
				vx = sht->vx0 + bx;
				map[vy * ctl->xsize + vx] = sid;
			}
		}
	}
……

写入的字节数为4个,我们需要使窗口在x方向上的大小以及窗口的x坐标也为4的倍数。目前窗口大小都是4的倍数,而对于窗口坐标,需要通过AND来取整,使打开窗口显示的位置为4的倍数。

else if (edx == 5) {
		sht = sheet_alloc(shtctl);
		sht->task = task;
		sht->flags |= 0x10;
		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, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2);/* 取整为4的倍数 */
		sheet_updown(sht, shtctl->top); 
		reg[7] = (int) sht;
	} 

当然,移动之后的窗口坐标也需要使4的倍数:

else 
{
	/* 鼠标处于移动模式 */
	x = mx - mmx;	/* 计算鼠标移动量 */
	y = my - mmy;
	sheet_slide(sht, (mmx2 + x + 2) & ~3, sht->vy0 + y);/* 取整为4的倍数*/
	mmy = my;
}

(3) QEMU中运行时,窗口移动的速度仍然赶不上鼠标移动的速度

因为图层移动需要进行的绘图操作非常耗时,导致操作系统来不及处理FIFO中的鼠标移动数据,这样就会出现放开鼠标键窗口还在移动的现象。优化为在接收到鼠标移动数据后不立即进行绘图操作,而等到FIFO为空时再进行绘图操作。

for (;;) {
……
		if (fifo32_status(&fifo) == 0) {
			/* FIFO为空,当存在搁置的绘图操作时立即执行 */
			if (new_mx >= 0) {
				io_sti();
				sheet_slide(sht_mouse, new_mx, new_my);
				new_mx = -1;
			} else if (new_wx != 0x7fffffff) {
				io_sti();
				sheet_slide(sht, new_wx, new_wy);
				new_wx = 0x7fffffff;
			} else {
				task_sleep(task_a);
				io_sti();
			}
		} else {
……
			} else if (512 <= i && i <= 767) { /* マウスデータ */
				if (mouse_decode(&mdec, i - 512) != 0) {
……
					new_mx = mx;
					new_my = my;
					if ((mdec.btn & 0x01) != 0) {
						/* 按下鼠标左键 */
						if (mmx < 0) {
							for (j = shtctl->top - 1; j > 0; j--) {
								……
								if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
									if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
								……
										if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
											mmx = mx;	/* ウィンドウ移動モードへ */
											mmy = my;
											mmx2 = sht->vx0;
											new_wy = sht->vy0;
										}
										……
									}
								}
							}
						} else {
							/* 如果窗口处于移动模式 */
							x = mx - mmx;	/* 计算鼠标指针移动量 */
							y = my - mmy;
							new_wx = (mmx2 + x + 2) & ~3;
							new_wy = new_wy + y;
							mmy = my;	/* 更新到移动后的坐标 */
						}
					} else {
						/* 没有按下左键 */
						mmx = -1;	/* 切换到一般模式 */
						if (new_wx != 0x7fffffff) {
							sheet_slide(sht, new_wx, new_wy);	/* 固定图层位置 */
							new_wx = 0x7fffffff;
						}
					}
				}
			}
		}
	}
}

通过new_mx与new_my将移动后的坐标暂时保存起来,在FIFO为空时再执行sheet_slide(sht_mouse, new_mx, new_my)更新鼠标的位置。而当放开鼠标左键退出窗口移动模式时,用户可能马上会去移动其他窗口,因此这里即使FIFO不为空也要立即更新窗口的位置。

2. 增加任意多个命令行窗口

在操作系统中可以根据需要打开多个命令行窗口,这里我们增加通过Shift+F2的按键来打开新的命令行窗口的命令。

if (i == 256 + 0x3c && key_shift != 0) 
{	/* Shift+F2 */
	/* 自动将输入切换到新打开的命令行窗口 */
	keywin_off(key_win);
	key_win = open_console(shtctl, memtotal);
	sheet_slide(key_win, 32, 4);
	sheet_updown(key_win, shtctl->top);
	keywin_on(key_win);
}

在这里插入图片描述
3. 优化命令行窗口关闭

打开的窗口多了,我们还要考虑关闭的问题。

首先是通过在命令行窗口中输入exit命令来关闭。
关闭一个命令行窗口,我们需要将创建时的内存空间以及窗口的图层和任务结构全部释放。但之前我们为命令行窗口准备了专用的栈,却没有将栈地址保存,这样就无法释放这些内存。所以我们需要在TASK结构中添加一个cons_stack成员保存栈地址:

struct TASK {
	int sel, flags; 
	int level, priority;
	struct FIFO32 fifo;
	struct TSS32 tss;
	struct CONSOLE *cons;
	int ds_base, cons_stack;
};
struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal)
{
……
	task->cons_stack = memman_alloc_4k(memman, 64 * 1024);
	task->tss.esp = task->cons_stack + 64 * 1024 - 12;
……
}
void close_constask(struct TASK *task)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	task_sleep(task);
	memman_free_4k(memman, task->cons_stack, 64 * 1024);
	memman_free_4k(memman, (int) task->fifo.buf, 128 * 4);
	task->flags = 0; /* 同来替代task_free(task); */
	return;
}

void close_console(struct SHEET *sht)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct TASK *task = sht->task;
	memman_free_4k(memman, (int) sht->buf, 256 * 165);
	sheet_free(sht);
	close_constask(task);
	return;
}

在close_consoletask中使任务进入休眠状态。这样任务就被从等待切换的列表中移除,绝对不会再切换到该任务,就可以安全地释放栈与FIFO缓冲区了。为了使task_alloc还能使用这些空间,需要将flags置为0。

exit命令的实现如下:

……
else if (strcmp(cmdline, "exit") == 0) {
		cmd_exit(cons, fat);
	}
……

void cmd_exit(struct CONSOLE *cons, int *fat)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct TASK *task = task_now();
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
	timer_cancel(cons->timer);
	memman_free_4k(memman, (int) fat, 4 * 2880);
	io_cli();
	fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768);	/* 768~1023 */
	io_sti();
	for (;;) {
		task_sleep(task);
	}
}

这里如果在cmd_exit函数中直接调用close_console,则相当于对自身任务执行了休眠,这样就无法继续执行其他程序了。因此这里通过给任务A发消息来让任务A调用close_console。发送给任务A之后,当前任务再进入休眠就没问题了。
在任务A中,需要增加对关闭命令行窗口命令的响应:

……
else if (768 <= i && i <= 1023) 
{	/* 命令行窗口关闭处理 */
	close_console(shtctl->sheets0 + (i - 768));
}
……

此外,之前还没有出现过画面上完全没有窗口的情况,也需要进行处理:

……
if (key_win != 0 && key_win->flags == 0) 
{	/* 窗口被关闭 */
	if (shtctl->top == 1) 
	{	/* 只有鼠标和背景 */
		key_win = 0;
	} 
	else 
	{
		key_win = shtctl->sheets[shtctl->top - 1];
		keywin_on(key_win);
	}
}

当画面上没有窗口时,我们将key_win置为0,与通常情况进行区别。
在这里插入图片描述
在这里插入图片描述
接下来继续实现通过鼠标关闭命令行窗口,只需要增加简单的处理。主程序中增加的处理:

	if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) 
	{
		/* 点击「×」按钮 */
		if ((sht->flags & 0x10) != 0) 
		{		/*是否为应用程序窗口*/
			task = sht->task;
			cons_putstr0(task->cons, "\nBreak(mouse) :\n");
			io_cli();	/* 禁止在强制结束处理时切换任务 */
			task->tss.eax = (int) &(task->tss.esp0);
			task->tss.eip = (int) asm_end_app;
			io_sti();
		} 
		else 
		{	/* 命令行窗口 */
			task = sht->task;
			io_cli();
			fifo32_put(&task->fifo, 4);
			io_sti();
		}
	}

在console_task中增加以下处理:

……
	for (;;) {
		io_cli();
		if (fifo32_status(&task->fifo) == 0) {
			task_sleep(task);
			io_sti();
		} else {
			i = fifo32_get(&task->fifo);
			io_sti();
			if (i <= 1) {
			……
			if (i == 4) {	/* 点击命令行窗口的x按钮 */
				cmd_exit(&cons, fat);
			}
			……
	}

4. 增加start与nsct命令

当前我们要运行一个程序,需要在命令行窗口中输入对应的指令。下面来开发start命令,用于打开一个新的命令行的窗口并直接运行相应的应用程序。

void cmd_start(struct CONSOLE *cons, char *cmdline, int memtotal)
{
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct SHEET *sht = open_console(shtctl, memtotal);
	struct FIFO32 *fifo = &sht->task->fifo;
	int i;
	sheet_slide(sht, 32, 4);
	sheet_updown(sht, shtctl->top);
	/* 将命令行输入的字符串逐字复制到新的命令行窗口中 */
	for (i = 6; cmdline[i] != 0; i++) {
		fifo32_put(fifo, cmdline[i] + 256);
	}
	fifo32_put(fifo, 10 + 256);	/* Enter */
	cons_newline(cons);
	return;
}

运行效果如下:
在这里插入图片描述
这样可以直接通过start运行一个程序。但是这里还需要额外打开一个命令行窗口。有些应用程序不需要在命令行窗口显示,我们又想要直接运行应用程序,而不额外打开一个命令行窗口,因此开发一个ncst(no console start)命令。

首先我们将没有窗口的命令行任务的cons->sht规定为0。在没有窗口的情况下执行dir与cls等命令没有效果,因此将这些屏蔽掉。cmd_ncst先按照cmd_start的样子去写。

……
	if (strcmp(cmdline, "mem") == 0 && cons->sht != 0) {
		cmd_mem(cons, memtotal);
	} else if (strcmp(cmdline, "cls") == 0 && cons->sht != 0) {
		cmd_cls(cons);
	} else if (strcmp(cmdline, "dir") == 0 && cons->sht != 0) {
		cmd_dir(cons);
	} else if (strncmp(cmdline, "type ", 5) == 0 && cons->sht != 0) {
		cmd_type(cons, fat, cmdline);
……

void cmd_ncst(struct CONSOLE *cons, char *cmdline, int memtotal)
{
	struct TASK *task = open_constask(0, memtotal);
	struct FIFO32 *fifo = &task->fifo;
	int i;
	for (i = 5; cmdline[i] != 0; i++) {
		fifo32_put(fifo, cmdline[i] + 256);
	}
	fifo32_put(fifo, 10 + 256);	/* Enter */
	cons_newline(cons);
	return;
}

当cons->sht为0时,需要禁用命令行窗口的字符显示等操作,因此字符显示部分需要增加如下判断条件:

if (cons->sht != 0) 
{
	putfonts8_asc_sht(cons->sht, cons->cur_x, cons->cur_y, COL8_FFFFFF, COL8_000000, " ", 1);
}

对于console_task,需要做如下修改:在不显示命令行窗口时,需要禁用一些不必要的处理,与上面类似;命令执行完还要立即结束命令行窗口任务,否则无法继续输入其他命令。

……
if (sheet != 0) {
	cons.timer = timer_alloc();
	timer_init(cons.timer, &task->fifo, 1);
	timer_settime(cons.timer, 50);
}
if (sheet != 0) {
	if (cons.cur_c >= 0) {
		boxfill8(sheet->buf, sheet->bxsize, cons.cur_c, 
			cons.cur_x, cons.cur_y, cons.cur_x + 7, cons.cur_y + 15);
	}
	sheet_refresh(sheet, cons.cur_x, cons.cur_y, cons.cur_x + 8, cons.cur_y + 16);
}

cmd_exit则需要增加无命令行窗口的任务结束处理。

void cmd_exit(struct CONSOLE *cons, int *fat)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct TASK *task = task_now();
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
	if (cons->sht != 0) {
		timer_cancel(cons->timer);
	}
	memman_free_4k(memman, (int) fat, 4 * 2880);
	io_cli();
	if (cons->sht != 0) {
		fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768);	/* 768~1023 */
	} else {
		fifo32_put(fifo, task - taskctl->tasks0 + 1024);	/* 1024~2023 */
	}
	io_sti();
	for (;;) {
		task_sleep(task);
	}
}

在没有命令行窗口时,需要提供TASK结构的地址用于结束任务。

然后是cmd_ncst中调用的open_constask函数:

struct TASK *open_constask(struct SHEET *sht, unsigned int memtotal)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct TASK *task = task_alloc();
	int *cons_fifo = (int *) memman_alloc_4k(memman, 128 * 4);
	task->cons_stack = memman_alloc_4k(memman, 64 * 1024);
	task->tss.esp = task->cons_stack + 64 * 1024 - 12;
	task->tss.eip = (int) &console_task;
	task->tss.es = 1 * 8;
	task->tss.cs = 2 * 8;
	task->tss.ss = 1 * 8;
	task->tss.ds = 1 * 8;
	task->tss.fs = 1 * 8;
	task->tss.gs = 1 * 8;
	*((int *) (task->tss.esp + 4)) = (int) sht;
	*((int *) (task->tss.esp + 8)) = memtotal;
	task_run(task, 2, 2); /* level=2, priority=2 */
	fifo32_init(&task->fifo, 128, cons_fifo, task);
	return task;
}

struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct SHEET *sht = sheet_alloc(shtctl);
	unsigned char *buf = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
	sheet_setbuf(sht, buf, 256, 165, -1); 
	make_window8(buf, 256, 165, "console", 0);
	make_textbox8(sht, 8, 28, 240, 128, COL8_000000);
	sht->task = open_constask(sht, memtotal);
	sht->flags |= 0x20;	
	return sht;
}

最后在主程序中再增加一些代码就可以了:

……
else if (1024 <= i && i <= 2023) 
{
	close_constask(taskctl->tasks0 + (i - 1024));
}
……

这样在命令行窗口中可以通过ncst命令直接运行一个应用程序:

在这里插入图片描述
不过还存在一点问题,这里打开的应用程序窗口不能通过x按钮关闭。这个问题留待下一篇解决,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2177751.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WEB服务器——Tomcat

服务器是可以使用java完成编写&#xff0c;是可以接受页面发送的请求和响应数据给前端浏览器的&#xff0c;而在开发中真正用到的Web服务器&#xff0c;我们不会自己写的&#xff0c;都是使用目前比较流行的web服务器。 如&#xff1a;Tomcat 1. 简介 Tomcat 是一个开源的轻量…

二维数组的存放

今天我水的文章是二维数组的存放 二维数组的存放方式其实和一维数组没有区别&#xff0c;但如果想要更直观的了解&#xff0c;我们可以把它们的地址打印出来。 代码如下&#xff1a; #include <stdio.h> int main() {int arr[3][3];//二维数组&#xff0c;int数组类型…

【高效管理集合】并查集的实现与应用

文章目录 并查集的概念主要操作优化技术应用场景 并查集的实现基本框架并查集的主要接口总体代码 并查集的应用省份的数量等式方程的可满足性 总结 并查集的概念 并查集&#xff0c;也称为不相交集&#xff0c;是一种树形的数据结构&#xff0c;用于处理一些不相交集合的合并及…

ClickHouse | 查询

1 ALL 子句 2 ARRAY JOIN 使用别名 :在使用时可以为数组指定别名&#xff0c;数组元素可以通过此别名访问&#xff0c;但数组本身则通过原始名称访问 3 DISTINCT子句 DISTINCT不支持当包含有数组的列 4 FROM子句 FROM 子句指定从以下数据源中读取数据: 1.表 2.子…

建筑资质应该怎么选?

建筑资质是建筑企业承接工程项目的必备条件&#xff0c;它不仅关系到企业的市场竞争力&#xff0c;还直接影响到企业的经营效益。因此&#xff0c;选择适合自己企业的建筑资质至关重要。以下是一些选择建筑资质时需要考虑的关键因素&#xff1a; 1. 明确企业定位 首先&#x…

金融教育宣传月 | 平安养老险百色中心支公司开展金融知识“消保县域行”宣传活动

9月22日&#xff0c;平安养老险百色中心支公司积极落实国家金融监督管理总局关于开展金融教育宣传月活动的相关要求&#xff0c;联合平安人寿百色中心支公司共同组成了平安志愿者小队&#xff0c;走进百色市四塘镇百兰村开展了一场别开生面的金融消费者权益保护宣传活动。此次活…

如何给你的项目添加测试覆盖率徽章

看完我的测试教程之后&#xff0c;想必大家都能写出一个测试覆盖率极高的小项目了。测试覆盖率既然这么高&#xff0c;不秀一秀岂不是白瞎了&#xff0c;下面我们就来通过第三方服务来给你的项目加上测试覆盖率徽章&#xff0c;涉及到的内容有yaml配置&#xff0c;githubAction…

Vue下载pubsub-js中错误问题解决

错误&#xff1a; 解决方法&#xff1a; 执行&#xff1a; npm config set registry https://registry.npm.taobao.org我执行以上方法后安装成功

关于北斗卫星导航系统,你都了解多少?

北斗卫星导航系统&#xff08;简称“北斗系统”&#xff09;&#xff0c; 英文全称是&#xff1a;Beidou Navigation Satellite System&#xff08;简称&#xff1a;BDS&#xff09;&#xff0c; 研发 的 初衷 是中国着眼于国家安全和经济社会发展需要&#xff0c;选择自主研发…

Java类的生命周期-初始化阶段

Java类的生命周期-初始化阶段 前两篇讲述了类生命周期的加载阶段和连接阶段&#xff0c;那么本篇我们来讲最为重要的初始化阶段&#xff0c;借助字节码文件与大厂面试题更好的理解类的初始化 头篇提到&#xff0c;类的生命周期可疑将他分为五个阶段&#xff0c;本篇要讲述的就是…

RIP路由(已被淘汰)

一、rip 路由原理 RIP&#xff08;Routing Information Protocol&#xff0c;路由信息协议&#xff09;早期的动态路由协议&#xff0c;被广泛应用于TCP/IP网络中&#xff0c;尤其是在中小型网络中。基于距离矢量&#xff08;Distance-Vector&#xff09;算法来计算到达目的网络…

农场小程序带你走进生态农产品的世界

在快节奏的现代生活中&#xff0c;人们对食品安全的关注日益增强&#xff0c;对环境、健康农产品的需求也愈发迫切。然而&#xff0c;传统农产品市场往往信息不透明&#xff0c;消费者难以直接了解农产品的生长环境和生产过程&#xff0c;导致信任缺失。而农场小程序的出现&…

工程安全监测分析模型与智能算法模型方案

工程安全监测分析模型与智能算法模型 构建大坝安全监测智能分析模型&#xff0c;以大坝立体智能感知体系为依托&#xff0c;获取大坝变形、渗流渗压、环境变量等实时监测数据&#xff0c;作为模型输入&#xff0c;实现监测数据自动预处理、特征提取、误差分析、变化趋势分析等…

大模型增量训练--基于transformer制作一个大模型聊天机器人

针对夸夸闲聊数据集&#xff0c;利用UniLM模型进行模型训练及测试&#xff0c;更深入地了解预训练语言模型的使用方法&#xff0c;完成一个生成式闲聊机器人任务。 项目主要结构如下&#xff1a; data 存放数据的文件夹 dirty_word.txt 敏感词数据douban_kuakua_qa.txt 原始语…

Qt——如何创建一个项目

前言 本文主要通过实操带领大家来实现基础文件的操作&#xff0c;主要包括文件的打开&#xff0c;读取&#xff0c;写入&#xff0c;当然文件读写我们可以有几种不同的方式来进行操作&#xff0c;分别是文件流&#xff0c;字节流来进行的操作这里就需要两个类分别是文件流&…

迈威通信闪耀工博会,以创新科技赋能工业自动化

昨日&#xff0c;在圆满落幕的第24届中国国际工业博览会上&#xff0c;迈威通信作为工业自动化与智慧化领域的先行者&#xff0c;以“创新打造新质通信&#xff0c;赋能工业数字化”为主题精彩亮相&#xff0c;向全球业界展示了我们在工业自动化领域的最新成果与创新技术。此次…

elementUI表格中某个字段(state)使用计算属性进行转换为对应中文显示

代码案例&#xff1a; <template><el-table:data"tableData"style"width: 100%"><el-table-columnprop"date"label"日期"width"180"/><el-table-columnprop"name"label"姓名"wid…

count(1),count(*)与 count(‘列名‘) 的区别

文章目录 COUNT(expr)性能对比count(*) VS count(1)count(*) VS count(列名) count(*)会走索引吗MyISAM count优化InnoDB如何处理count(*)总结 参考官方文档&#xff1a; https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html#function_count COUNT(expr) coun…

叶国富“推翻”马云新零售,零售新王此刻登基?

63亿入主永辉超市&#xff0c;拿到29.4%股份&#xff0c;坐上永辉超市第一大股东的宝座&#xff0c;名创优品创始人叶国富&#xff0c;成为了新科“零售之王”。 很是霸气外漏。 有投资者表示费解&#xff0c;不明白为何此时入局超市行业&#xff0c;叶国富当即召开电话会议&…

数据结构:树的定义及其性质

树的定义 树是一种重要的非线性数据结构&#xff0c;树作为一种逻辑结构&#xff0c;同时也是一种分层结构。具有以下两个特点&#xff1a; 1.树的根结点没有前驱&#xff0c;除根结点意外的节点只有一个前驱 2.树中所有结点都可以有0个或多个后继 树结构在多个领域都有广泛…