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

news2024/12/28 18:36:43

本篇主要解决两个问题。一是扩充上一篇内存管理部分的功能,实现一种按4KB大小分配内存的函数;二是解决鼠标显示中鼠标覆盖任务栏的问题。
在这里插入图片描述

1. 以4KB大小为单位分配内存的函数

如果每次分配内存都需要按字节去分配,效率还是比较低的,可用内存也会变得越来越零碎。这里作者引入一种以4KB大小为单位进行内存分配的函数。

首先要根据应用程序所需的内存大小,进行4KB向上取整,也就是不足4KB的部分用4KB补足。相应的代码如下:

unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size)
{
	unsigned int a;
	size = (size + 0xfff) & 0xfffff000;
	a = memman_alloc(man, size);
	return a;
}

int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size)
{
	int i;
	size = (size + 0xfff) & 0xfffff000;
	i = memman_free(man, addr, size);
	return i;
}

可以看出对于4KB(0x1000)的向上取整,采用的方法是(size + 0xfff) & 0xfffff000
用十进制来举例子就很简单,比如以10为单位向上舍入,123舍入后就是130,这个很好理解。

2. 图层的叠加处理

引入了以4KB为单位进行内存分配的函数之后,本篇的重点是完成叠加处理。从鼠标和任务栏的叠加扩展开去,扩展到多个不同图层的叠加显示。这部分确实比较复杂,如果没明白的话,还是需要认认真真地多看几遍。

2.1 图层概念的引入

为了讲解叠加处理,需要引入图层的概念。这里作者引入的图层概念就是一张图片,除了有图案的地方,背景色是透明的。而多个窗口叠加在一起,可能是多个大小不同的图层的叠放。最上面的小图层用来描绘鼠标,下面依次是叠加在一起的不同图层,而最下面则是桌面背景。另外还需要实现窗口的移动。

首先考虑描述一个图层信息的数据结构:

struct SHEET {
	unsigned char *buf;
	int bxsize, bysize, vx0, vy0, col_inv, height, flags;
};

这个结构体中,buf用来存放图层上的图案内容。bxsize,bysize用于描述图层的大小,vx0和vy0用来描述图层在整个显示画面中的位置,col_inv表示图层的透明色色号,height表示图层的高度,flags用于存放有关图层设置的信息。
这样一个图层就可以通过这个结构体进行描述了。

当然,只有一个图层,我们无法实现叠加处理,还需要一个管理多个图层信息的结构体:

#define MAX_SHEETS		256
struct SHTCTL {
	unsigned char *vram;
	int xsize, ysize, top;
	struct SHEET *sheets[MAX_SHEETS];
	struct SHEET sheets0[MAX_SHEETS];
};

SHTCTL这个结构体用于管理图层。其中MAX_SHEETS是最大图层数,设置为256。结构体中,vram表示显存地址,xsize与ysize表示画面的大小,初始化时就从BOOTINFO中获取到并存起来。top用来表示最上面图层的高度。sheets0是一个结构体数组,用于保存图层的信息;而sheets是一个结构体指针数组。sheets0中存放的图层信息是乱序的,而通过sheets结构体指针数组将经过升序处理后的图层信息保存起来。

2.2 图层叠加处理

具备了这些数据结构之后,我们开始编写程序。首先需要对图层管理的数据结构进行初始化:

struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{
	struct SHTCTL *ctl;
	int i;
	ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));
	if (ctl == 0) {
		goto err;
	}
	ctl->vram = vram;
	ctl->xsize = xsize;
	ctl->ysize = ysize;
	ctl->top = -1; /* 当前画面中没有显示的图层 */
	for (i = 0; i < MAX_SHEETS; i++) {
		ctl->sheets0[i].flags = 0; /* 图层标记为未使用 */
	}
err:
	return ctl;
}

这段程序的内容比较清楚。首先是通过内存分配函数给图层控制结构体分配足够的内存,通过sizeof(struct SHTCTL)来由编译器计算所需的内存大小。这个内存大小超过了4KB,所以用前面的内存分配函数来分配内存很合适。接下来将显存地址、画面大小等信息存放在结构体中。将top设置为-1,表示当前没有显示的图层;将sheets0数组中所有元素的flags成员置为0,表示当前图层都没有使用。

初始化之后,是分配空闲图层的函数:

#define SHEET_USE		1

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; /* 表示当前图层隐藏 */
			return sht;
		}
	}
	return 0;	/* 走到这里说明所有图层都处于使用状态,没有空闲图层 */
}

从图层管理结构体的sheets0数组中寻找未使用的图层,标记为使用,将height先设置为-1表示隐藏。

获取空闲图层之后,就可以根据实际情况对图层的参数进行设置,这也没什么难点。

void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv)
{
	sht->buf = buf;
	sht->bxsize = xsize;
	sht->bysize = ysize;
	sht->col_inv = col_inv;
	return;
}

下面是叠加显示中最关键的设置图层高度的函数,也是最复杂的:

void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height)
{
	int h, old = sht->height; /* 存储图层原来位置的高度信息 */

	/* 如果指定的高度过高或过低,则进行修正 */
	if (height > ctl->top + 1) {
		height = ctl->top + 1;
	}
	if (height < -1) {
		height = -1;
	}
	sht->height = height; /* 设定图层高度 */

	/* 以下通过sheets指针数组对图层进行重新排序 */
	if (old > height) {	/* 图层的新位置比原来低*/
		if (height >= 0) {
			/* 图层往下移动了,但还不是隐藏起来,这时把原位置和新位置之间的图层都向上升 */
			for (h = old; h > height; h--) {
				ctl->sheets[h] = ctl->sheets[h - 1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht;
		} else {	/* 把图层隐藏起来 */
			if (ctl->top > old) {
				/* 图层的原位置不在最上层,则把图层原位置到最上层的图层依次向下降 */
				for (h = old; h < ctl->top; h++) {
					ctl->sheets[h] = ctl->sheets[h + 1];
					ctl->sheets[h]->height = h;
				}
			}
			ctl->top--; /* 隐藏了一个图层,显示的图层少了一个,最上面的图层高度下降 */
		}
		sheet_refresh(ctl); /* 根据更新后的图层信息,重新绘制画面 */
	} else if (old < height) {	/* 图层的新位置比原来的位置高 */
		if (old >= 0) {
			/* 图层原高度大于0,表示图层原来就处于显示状态,变更位置后,需要把原位置到新位置之间的图层都向下降 */
			for (h = old; h < height; h++) {
				ctl->sheets[h] = ctl->sheets[h + 1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht;
		} else {	/* 图层原来处于隐藏状态,现在变成显示状态了 */
			/* 把图层新位置与最上层之间的图层向上升 */
			for (h = ctl->top; h >= height; h--) {
				ctl->sheets[h + 1] = ctl->sheets[h];
				ctl->sheets[h + 1]->height = h + 1;
			}
			ctl->sheets[height] = sht;
			ctl->top++; /* 显示的图层增加了一个,最上面的图层高度也增加了 */
		}
		sheet_refresh(ctl); /* 根据更新后的图层信息,重新绘制画面 */
	}
	return;
}

这一段代码比较复杂。简单地理解,这段代码做的其实就是从一堆按顺序堆放的图层中抽出一张,改变其高度,再放回堆中,重新进行排序的过程。如果当前图层向下移动,那么处在该图层新老位置之间的图层要依次向上移动,填补该图层留出的空位,而总高度不变;如果该图层由显示变为隐藏,那么原来处在该图层上面的图层要依次向下移动,填补该图层留出的空位,这样总高度由于该图层的隐藏而减少了。
同理,如果图层向上移动,那么原来处于该图层新老位置之间的图层要依次向下移动,填补该图层留出的空位,而总高度不变;如果该图层由隐藏变为显示,那么原来处在该图层与最顶层之间的图层要依次向上移动,给该图层腾出空间,总高度由于该图层的显示而增加了。
如此解释,这段程序是否更好懂一些了呢?
在这里插入图片描述

在这里插入图片描述排序之后,还需要从下到上依次描绘出各个图层的图案,最终最上层的图案会显示出来。这里就要用到sheet_refresh函数:

void sheet_refresh(struct SHTCTL *ctl)
{
	int h, bx, by, vx, vy;
	unsigned char *buf, c, *vram = ctl->vram;
	struct SHEET *sht;
	for (h = 0; h <= ctl->top; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		for (by = 0; by < sht->bysize; by++) {
			vy = sht->vy0 + by;
			for (bx = 0; bx < sht->bxsize; bx++) {
				vx = sht->vx0 + bx;
				c = buf[by * sht->bxsize + bx];
				if (c != sht->col_inv) {
					vram[vy * ctl->xsize + vx] = c;
				}
			}
		}
	}
	return;
}

从程序中可以看出,这个函数其实是将buf中不是透明色的像素点写入显存,这样自下而上层层写入下来,最终留下的就是最顶层的图案和下面未被覆盖的部分了。

叠加显示最重要的部分基本上就说完了,此外还有一些附加的函数,比如左右移动窗口的函数,释放图层的函数:

void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
{
	sht->vx0 = vx0;
	sht->vy0 = vy0;
	if (sht->height >= 0) { /* 当前图层正在显示 */
		sheet_refresh(ctl); /* 根据新的图层信息重新描绘图案 */
	}
	return;
}

void sheet_free(struct SHTCTL *ctl, struct SHEET *sht)
{
	if (sht->height >= 0) {
		sheet_updown(ctl, sht, -1); /* 如果处于显示状态,先设置位隐藏 */
	}
	sht->flags = 0; /* 将状态设置为未使用 */
	return;
}

接下来是修改主函数,在其中调用这部分功能了。相关程序如下:


	struct SHTCTL *shtctl;
	struct SHEET *sht_back, *sht_mouse;
	unsigned char *buf_back, buf_mouse[256];

init_palette();
	shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);
	sht_back  = sheet_alloc(shtctl);
	sht_mouse = sheet_alloc(shtctl);
	buf_back  = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);
	sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); /* 背景图层,没有透明色 */
	sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99); /* 鼠标图层,设置透明色色号为99 */
	init_screen8(buf_back, binfo->scrnx, binfo->scrny);
	init_mouse_cursor8(buf_mouse, 99);
	sheet_slide(shtctl, sht_back, 0, 0);
	mx = (binfo->scrnx - 16) / 2; /*按照鼠标图层显示在画面中央来计算坐标 */
	my = (binfo->scrny - 28 - 16) / 2;
	sheet_slide(shtctl, sht_mouse, mx, my);
	sheet_updown(shtctl, sht_back,  0);
	sheet_updown(shtctl, sht_mouse, 1);
	sprintf(s, "(%3d, %3d)", mx, my);
	putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
	sprintf(s, "memory %dMB   free : %dKB",
			memtotal / (1024 * 1024), memman_total(memman) / 1024);
	putfonts8_asc(buf_back, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
	sheet_refresh(shtctl);

	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(buf_back, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
				putfonts8_asc(buf_back, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
				sheet_refresh(shtctl);
			} 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(buf_back, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
					putfonts8_asc(buf_back, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
					/* 移动鼠标 */
					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(buf_back, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 清除原坐标显示 */
					putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示新坐标 */
					sheet_slide(shtctl, sht_mouse, mx, my); /* 包含了sheet_refresh */
				}
			}
		}
	}

主程序中当前还只有两个图层,一个是背景图层,另一个是鼠标图层。这样运行程序,移动鼠标时,就不会出现遮挡任务栏的情况了。
在这里插入图片描述

2.3 加快图层叠加处理速度

但是当前还存在着显示比较慢的问题,叠加处理的速度还可以进一步提升。

当前的鼠标图案是1616=256个像素,其实移动一次只要重新绘制256个像素点就可以了。但当前的实现是每次移动都会将整个画面共320200=64000个像素点重新描绘一遍,从这一点出发就可以提高速度:

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1)
{
	int h, bx, by, vx, vy;
	unsigned char *buf, c, *vram = ctl->vram;
	struct SHEET *sht;
	for (h = 0; h <= ctl->top; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		for (by = 0; by < sht->bysize; by++) {
			vy = sht->vy0 + by;
			for (bx = 0; bx < sht->bxsize; bx++) {
				vx = sht->vx0 + bx;
				if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) {
					c = buf[by * sht->bxsize + bx];
					if (c != sht->col_inv) {
						vram[vy * ctl->xsize + vx] = c;
					}
				}
			}
		}
	}
	return;
}

可以看出sheet_refreshsub函数中通过vx0-vy1指定了范围,这样就可以只在鼠标的256个像素范围内重新描绘,速度得到提升。
sheet_slide函数修改如下:

void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
{
	int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
	sht->vx0 = vx0;
	sht->vy0 = vy0;
	if (sht->height >= 0) { /* 如果当前图层正在显示,按照新的图层信息重新描绘画面 */
		sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);
		sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize);
	}
	return;
}

这里做的是记住移动前的显示位置,再设置新的位置,只需要将原位置的显示消除,在新位置描绘即可。

虽然不用将整个画面都描绘一遍,但还是通过if判断了画面内的所有位置。在描绘之前就限制好描绘的范围,减少不必要的if判断,可以进一步提高速度:

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1)
{
	int h, bx, by, vx, vy, bx0, by0, bx1, by1;
	unsigned char *buf, c, *vram = ctl->vram;
	struct SHEET *sht;
	for (h = 0; h <= ctl->top; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		/* vx0-vy1范围对bx0-by1的范围进行推断 */
		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;
				c = buf[by * sht->bxsize + bx];
				if (c != sht->col_inv) {
					vram[vy * ctl->xsize + vx] = c;
				}
			}
		}
	}
	return;
}

本篇从建立图层的数据结构出发,建立了图层动作的程序描述,并通过鼠标的实例进行实现。这部分从原理到程序内容都不少,看似简单实则又并不简单,自己就反复读了好几遍才理解。下一篇的制作窗口仍然与显示相关,敬请期待。

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

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

相关文章

chapter08-面向对象编程——(chapter08作业)——day10

343-作业01 package chapter08.homeworks;public class Homework01 {public static void main(String[] args) {/*1.定义一个Person类{name, age, job},初始化Person对象数组&#xff0c;有3个person对象&#xff0c;并按照age从大到小进行排序,提示&#xff0c;使用冒泡排序 H…

C# UserControl、Dockpanel和DockContent、Cursor、

一、UserControl类 UserControl 是 .NET 中的一个基类&#xff0c;用于创建自定义控件&#xff0c;主要用于 Windows Forms 和 WPF。通过继承 UserControl&#xff0c;你可以设计和实现具有特定界面和功能的控件组件。UserControl 允许你将多个标准控件组合在一起&#xff0c;…

实验室管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户信息管理&#xff0c;教学实验管理&#xff0c;学生签到管理&#xff0c;设备信息管理&#xff0c;设备预约管理&#xff0c;课程表管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统…

【Python数据结构与算法】队列----滑动窗口

题目&#xff1a;滑动窗口 描述 给定一个长度为n&#xff08;n<100,000&#xff09;的整数序列。有一个大小为k(0 < k < n)的滑动窗口从数组的最左端移动到最右端。你可以看到窗口中的k个数。窗口每次向右滑动一个数的距离。 下面是一个例子&#xff1a; 数组是 [1 3…

差分约束问题

差分约束 概念及解法一些题目 概念及解法 引用自OI Wiki 差分约束系统是一种特殊的 n 元一次不等式组&#xff0c;它包含 n 个变量 x 1 , x 2 , … , x n x_1,x_2,\dots,x_n x1​,x2​,…,xn​ 以及 m 个约束条件&#xff0c;每个约束条件是由两个其中的变量做差构成的&#x…

内网安装GitLab

内网安装GitLab 为什么安装GitLab GitHub&#xff08;国外&#xff09;和Gitee&#xff08;国内&#xff09;都是第三方托管平台&#xff0c;不能本地部署&#xff0c;对公网开放&#xff0c;代码保密性不高。 为代码保密性&#xff0c;需在内网搭建代码管理平台。有web操作…

Java设计模式之建造者模式详细讲解和案例示范

引言 在软件开发中&#xff0c;创建复杂对象常常伴随着众多参数和配置&#xff0c;这种情况容易导致“构造函数爆炸”或参数混乱的问题。为了解决这一问题&#xff0c;建造者模式&#xff08;Builder Pattern&#xff09;应运而生。它通过分步骤地构建对象&#xff0c;提供了更…

C++编程:高阶编程

本阶段主要针对泛型编程和STL技术作详细讲解。 1 模板 1.1 模板的概念 C提供一种泛型编程&#xff0c;主要利用模板的技术&#xff0c;模板的目的是提高代码复用性&#xff0c;将类型参数化。 C提供两种模板机制&#xff1a; 函数模板类模板 模板的特点&#xff1a; 模板…

实习结束总结20240828

长达两个月的实习终于在今天结束了&#xff0c;不知怎的&#xff0c;心如止水&#xff0c;没有高兴&#xff0c;没有伤心&#xff0c;毫无波澜的内心甚至让自己都感觉可怕&#xff0c;也许&#xff0c;这就是成长吧。 硬件上&#xff1a; 1.cadence需要继续深入学习&#xff…

Transformers for One-Shot Visual Imitation

发表时间&#xff1a;(CoRL 2020&#xff09; 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4546337571360890881&noteId2424798489658874880 作者单位&#xff1a;Carnegie Mellon University, USA Motivation&#xff1a;人类能够通过推断他们…

每日OJ_牛客_年会抽奖(错排问题)

牛客_年会抽奖&#xff08;错排问题&#xff09; 年会抽奖__牛客网 解析代码 该题为经典的错排问题 用A、B、C……表示写着&#xff4e;位友人名字的信封&#xff0c;a、b、c……表示&#xff4e;份相应的写好的信纸。把错装的总数为记作 D(n)。假设把&#xff41;错装进&…

【ubuntu使用笔记】Ubuntu Desktop 访问SMB共享文件夹

Ubuntu Desktop 访问SMB共享文件夹 Ubuntu Desktop 访问SMB共享文件夹文件夹打开 file managerother location输入 IP地址&#xff0c;smb://IP点击connect按钮正常进入 命令行安装客户端连接 Ubuntu Desktop 访问SMB共享文件夹 文件夹 打开 file manager other location 输入…

WATCH, TRY, LEARN: META-LEARNING FROM DEMONSTRATIONS AND REWARDS

&#xff08;感觉有点从失败中学习的意思&#xff09; 发表时间&#xff1a;30 Jan 2020 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4545005537963171841&noteId2453371997770644736 作者单位&#xff1a;UC Berkeley Motivation&#xff1a…

2024年高教社杯全国大学生数学建模竞赛

2024年高教社杯全国大学生数学建模竞赛通知 2024数模比赛确定时间为&#xff1a;9月5日18时至9月8日20时 报名截止日期&#xff1a;9月2日&#xff08;周一&#xff09;20时

使用maven 实现版本覆盖案例4【经典版】

一 原理介绍 1.1 原理逻辑 关系图&#xff1a;从使用方xinxiang_demo中引用A_parent的2.13的版本的层级要比B_Module继承A_parent&#xff1a;1.1 版本的层级要短&#xff0c;优先级更高&#xff0c;所以使用2.13&#xff0c;将B_Module继承A_parent 1.1 版本覆盖。 1.2 实操…

约 数个数

对于一个数 其中&#xff1a;是的各个质因数&#xff0c;上式是的质因数乘积式。 约数个数&#xff1a; 约数之和&#xff1a; step1&#xff1a; 采用分解质因数的方法&#xff0c;计算出的每一个质因数的次数 &#xff08;分解质因数的blog&#xff1a;http://t.csdni…

Kali学习(ms17-010、ms08-067漏洞复现)

目录 一、kali网络设置 NAT模式、桥接上网/仅主机 VMnet0、VMnet1、VMnet8 1.NAT模式 VMnet8 &#xff08;1&#xff09;检查服务 &#xff08;2&#xff09;创建虚拟网卡 &#xff08;3&#xff09;创建kali的网卡信息 &#xff08;4&#xff09;验证 2.桥接模式 VMn…

结果一。5.be doing表将来和 表 will的区别

be doing 表⽰近期、眼下就要发⽣的事情; will 表⽰将来的时间,则较远⼀些。如: He is going to write a letter tonight.He will write a book 。 be going to 表⽰根据主观判断将来肯定发⽣的事情。 will+ 动词原形表⽰⼀般将来时。 will ࿰

如何用Java SpringBoot+Vue打造高效产品订单管理系统?

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…