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

news2024/11/26 2:52:35

本篇的内容不少,主要围绕着文件操作与文字显示展开。
在这里插入图片描述

1. alloca函数

在开发文件操作与文字显示之前,需要先做一些准备,引入alloca函数。首先看下面的代码:

#include <stdio.h>
#include "apilib.h"

#define MAX		1000

void HariMain(void)
{
	char flag[MAX], s[8];
	int i, j;
	for (i = 0; i < MAX; i++) {
		flag[i] = 0;
	}
	for (i = 2; i < MAX; i++) {
		if (flag[i] == 0) {
			/* 没有标记的为质数 */
			sprintf(s, "%d ", i);
			api_putstr0(s);
			for (j = i * 2; j < MAX; j += i) {
				flag[j] = 1;	/* 给它的倍数做上标记 */
			}
		}
	}
	api_end();
}

运行该程序,可以展示1000以内的质数。
在这里插入图片描述
接下来将MAX修改为10000,使程序能够展示1-10000以内的质数。由于flags[10000]需要大概10k的空间,因此在Makefile中需要将栈的大小指定为11k。

但是在编译过程中,会出现一条告警“Warning: can’t link __alloca”。忽略告警,运行程序也会出现问题。

这与使用的C语言编译有关。编译器规定,如果栈中的变量超过4KB,则需要调用__alloca函数,该函数的作用是根据操作系统的规格来获取栈中的空间。对于Windows和Linux系统,如果不调用__alloca函数,会无法正常获取内存空间。虽然本操作系统不存在这个问题,但是为了适应编译器,我们也需要编写一个__alloca函数,只对ESP进行减法运算,而不进行其他操作。

不过其实我们也可以换一种方式,通过malloc获取所需要的内存:

#include <stdio.h>
#include "apilib.h"

#define MAX		10000

void HariMain(void)
{
	char *flag, s[8];
	int i, j;
	api_initmalloc();
	flag = api_malloc(MAX);
	for (i = 0; i < MAX; i++) {
		flag[i] = 0;
	}
	for (i = 2; i < MAX; i++) {
		if (flag[i] == 0) {
			sprintf(s, "%d ", i);
			api_putstr0(s);
			for (j = i * 2; j < MAX; j += i) {
				flag[j] = 1;	
			}
		}
	}
	api_end();
}

这样就可以避开栈空间的问题了。但是栈空间的问题还是需要解决。

alloca函数的代码如下:

[FORMAT "WCOFF"]
[INSTRSET "i486p"]
[BITS 32]
[FILE "alloca.nas"]

		GLOBAL	__alloca

[SECTION .text]

__alloca:
		ADD		EAX,-4
		SUB		ESP,EAX
		JMP		DWORD [ESP+EAX]		; 代替RET

__alloca函数会在以下情况中被C语言程序调用:

  • 要执行的操作从栈中分配EAX个字节的内存地址(ESP -= EAX)
  • 不能改变ECX,EDX,EBX,EBP,ESI,EDI的值(可以临时改变,但需要通过PUSH/POP恢复)

据此,我们来看alloca函数的代码是怎么来的。

首先会想到如下的代码:

SUB		ESP,EAX
RET

但这样不行,因为RET的返回地址保存在ESP中,而我们又对ESP进行了操作,导致通过RET无法正确返回。

于是改进为如下的代码:

SUB		ESP,EAX
JMP		DWORD [ESP + EAX]

这里通过JMP来代替RET指令,但还是有问题。

RET指令相当于POP EIP指令,而POP EIP指令又相当于如下的两条指令:

MOV		EIP, [ESP]
ADD		ESP, 4

除了ESP-EAX外,由于POP EIP操作,ESP的值又增加了4,因此还需要将这一点纳入考虑。最终修改成以下的程序:

SUB		ESP, EAX
ADD		ESP, 4
JMP		DWORD [ESP + EAX -4]

这样就既保证了ESP寄存器值得正确,又使程序能够正确返回。

在C语言中,在函数外部声明得变量和带static的变量一样,都会被解释为DB和RESB,而在函数内部不带static声明的变量则会从栈中分配空间。因此将变量设置在函数内部,可以减少编译出来的应用程序的大小。

2. 文件操作API

完成了上面的准备工作,接下来就来完成文件操作API的开发。

所谓文件操作API,就是指定文件并能自由读写文件内容的API。一般的操作系统中,输入输出文件的API基本具有以下几种功能:

  • 打开…………open
  • 定位…………seek
  • 读取…………read
  • 写入…………write
  • 关闭…………close

当前操作系统还不能实现写入文件,因此先完成其他四种操作的API设计:

(1) 打开文件

  • EDX = 21
  • EBX = 文件名
  • EAX = 文件句柄(为0时表示打开失败,由操作系统返回)

(2) 关闭文件

  • EDX = 22
  • EAX = 文件句柄

(3) 文件定位

  • EDX = 23
  • EAX = 文件句柄
  • ECX = 定位模式 0:定位的起点为文件开头 1: 定位的起点为当前的访问位置 2:定位的起点为文件末尾
  • EDX = 定位偏移量

(4) 获取文件大小

  • EDX = 24
  • EAX = 文件句柄
  • ECX = 文件大小获取模式 0:普通文件大小 1:当前读取位置从文件开头起算的偏移量 2:当前读取位置从文件末尾起算的偏移量
  • EAX= 文件大小(由操作系统返回)

(5) 文件读取

  • EDX = 25
  • EAX = 文件句柄
  • EBX = 缓冲区地址
  • ECX = 最大读取字节数
  • EAX = 本次读取到的字节数(由操作系统返回)

接下来来编程实现这些API。首先修改TASK结构体,在其中增加文件句柄

struct FILEHANDLE {
	char *buf;
	int size;
	int pos;
};

struct TASK {
	int sel, flags; /* selはGDTの番号のこと */
	int level, priority;
	struct FIFO32 fifo;
	struct TSS32 tss;
	struct SEGMENT_DESCRIPTOR ldt[2];
	struct CONSOLE *cons;
	int ds_base, cons_stack;
	struct FILEHANDLE *fhandle;
	int *fat;
};

在console_task与cmd_app中需要相应地增加对文件的操作:

void console_task(struct SHEET *sheet, int memtotal)
{
	……
	struct FILEHANDLE fhandle[8];
	……
	for (i = 0; i < 8; i++) {
		fhandle[i].buf = 0;	/* 未使用标记 */
	}
	task->fhandle = fhandle;
	task->fat = fat;
	……
}

int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
	……
	if (finfo != 0) {
		/* 找到文件的情况 */
		……
		if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
			……
			start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));
			……
			for (i = 0; i < 8; i++) {	/* 将未关闭的文件关闭 */
				if (task->fhandle[i].buf != 0) {
					memman_free_4k(memman, (int) task->fhandle[i].buf, task->fhandle[i].size);
					task->fhandle[i].buf = 0;
				}
			}
			timer_cancelall(&task->fifo);
			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;
}

在hrb_api中增加对于这些api的处理:

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
	struct FILEINFO *finfo;
	struct FILEHANDLE *fh;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

	…………
	} else if (edx == 21) {
		for (i = 0; i < 8; i++) {
			if (task->fhandle[i].buf == 0) {
				break;
			}
		}
		fh = &task->fhandle[i];
		reg[7] = 0;
		if (i < 8) {
			finfo = file_search((char *) ebx + ds_base,
					(struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
			if (finfo != 0) {
				reg[7] = (int) fh;
				fh->buf = (char *) memman_alloc_4k(memman, finfo->size);
				fh->size = finfo->size;
				fh->pos = 0;
				file_loadfile(finfo->clustno, finfo->size, fh->buf, task->fat, (char *) (ADR_DISKIMG + 0x003e00));
			}
		}
	} else if (edx == 22) {
		fh = (struct FILEHANDLE *) eax;
		memman_free_4k(memman, (int) fh->buf, fh->size);
		fh->buf = 0;
	} else if (edx == 23) {
		fh = (struct FILEHANDLE *) eax;
		if (ecx == 0) {
			fh->pos = ebx;
		} else if (ecx == 1) {
			fh->pos += ebx;
		} else if (ecx == 2) {
			fh->pos = fh->size + ebx;
		}
		if (fh->pos < 0) {
			fh->pos = 0;
		}
		if (fh->pos > fh->size) {
			fh->pos = fh->size;
		}
	} else if (edx == 24) {
		fh = (struct FILEHANDLE *) eax;
		if (ecx == 0) {
			reg[7] = fh->size;
		} else if (ecx == 1) {
			reg[7] = fh->pos;
		} else if (ecx == 2) {
			reg[7] = fh->pos - fh->size;
		}
	} else if (edx == 25) {
		fh = (struct FILEHANDLE *) eax;
		for (i = 0; i < ecx; i++) {
			if (fh->pos == fh->size) {
				break;
			}
			*((char *) ebx + ds_base + i) = fh->buf[fh->pos];
			fh->pos++;
		}
		reg[7] = i;
	}
	return 0;
}

增加汇编语言中的api函数:

_api_fopen:			; int api_fopen(char *fname);
		PUSH	EBX
		MOV		EDX,21
		MOV		EBX,[ESP+8]			; fname
		INT		0x40
		POP		EBX
		RET

_api_fclose:		; void api_fclose(int fhandle);
		MOV		EDX,22
		MOV		EAX,[ESP+4]			; fhandle
		INT		0x40
		RET
_api_fseek:			; void api_fseek(int fhandle, int offset, int mode);
		PUSH	EBX
		MOV		EDX,23
		MOV		EAX,[ESP+8]			; fhandle
		MOV		ECX,[ESP+16]		; mode
		MOV		EBX,[ESP+12]		; offset
		INT		0x40
		POP		EBX
		RET
		
_api_fsize:			; int api_fsize(int fhandle, int mode);
		MOV		EDX,24
		MOV		EAX,[ESP+4]			; fhandle
		MOV		ECX,[ESP+8]			; mode
		INT		0x40
		RET
		
_api_fread:			; int api_fread(char *buf, int maxsize, int fhandle);
		PUSH	EBX
		MOV		EDX,25
		MOV		EAX,[ESP+16]		; fhandle
		MOV		ECX,[ESP+12]		; maxsize
		MOV		EBX,[ESP+8]			; buf
		INT		0x40
		POP		EBX
		RET

代码内容比较简单,与上文的设计相符合。

最后再编写一个用于测试的应用程序,该程序实现的功能是将ipl10.nas的内容展示出来:

#include "apilib.h"

void HariMain(void)
{
	int fh;
	char c;
	fh = api_fopen("ipl10.nas");
	if (fh != 0) {
		for (;;) {
			if (api_fread(&c, 1, fh) == 0) {
				break;
			}
			api_putchar(c);
		}
	}
	api_end();
}

应用程序命名为typeipl,运行该应用程序,结果如下:
在这里插入图片描述
该应用程序看起来还是比较好用的,接下来用它来替换掉之前命令行中的type命令。当前的应用程序只能用来显示ipl10.nas的内容,要使其能够显示任意的文件,还需要运行时获取文件名,这个功能称为获取命令行。下面通过编写一个API来实现。将该API编写为可以返回完整的命令行内容,即包含应用程序的内容和文件名的完整内容。

获取命令行

  • EDX = 26
  • EBX = 存放命令行内容的地址
  • ECX = 最多可存放多少字节
  • EAX = 实际存放了多少字节(由操作系统返回)

对程序做一些修改:

struct TASK {
	int sel, flags; 
	int level, priority;
	struct FIFO32 fifo;
	struct TSS32 tss;
	struct SEGMENT_DESCRIPTOR ldt[2];
	struct CONSOLE *cons;
	int ds_base, cons_stack;
	struct FILEHANDLE *fhandle;
	int *fat;
	char *cmdline;// 增加获取的命令行
};

void console_task(struct SHEET *sheet, int memtotal)
{
	……
	task->cons = &cons;
	task->cmdline = cmdline; // 初始化
	……
}

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
	…………
		} else if (edx == 26) {
		i = 0;
		for (;;) {
			*((char *) ebx + ds_base + i) =  task->cmdline[i];
			if (task->cmdline[i] == 0) {
				break;
			}
			if (i >= ecx) {
				break;
			}
			i++;
		}
		reg[7] = i;
	}
	return 0;
}

添加汇编语言API:

_api_cmdline:		; int api_cmdline(char *buf, int maxsize);
		PUSH	EBX
		MOV		EDX,26
		MOV		ECX,[ESP+12]		; maxsize
		MOV		EBX,[ESP+8]			; buf
		INT		0x40
		POP		EBX
		RET

最后是编写应用程序,命名为type.c:

#include "apilib.h"

void HariMain(void)
{
	int fh;
	char c, cmdline[30], *p;

	api_cmdline(cmdline, 30);
	for (p = cmdline; *p > ' '; p++) { }	/* 跳过之前的内容,直到遇见空格 */
	for (; *p == ' '; p++) { }	/* 跳过空格 */
	fh = api_fopen(p);
	if (fh != 0) {
		for (;;) {
			if (api_fread(&c, 1, fh) == 0) {
				break;
			}
			api_putchar(c);
		}
	} else {
		api_putstr0("File not found.\n");
	}
	api_end();
}

前面在实现type命令时,程序代码中直接跳过了5个字符(“type” + 空格),来读取后面的文件名。而这里修改为从空格跳过,这样读取到的命令行中即使命令的长度不同,如“cat”,“type”等,也能准确地分离出后面的文件名了。

运行命令type ipl10.nas,运行结果与上面相同:
在这里插入图片描述

3. 日文文字显示

作者主要面对的是日本读者,因此这里引入的是日文文字显示。但一方面这部分功能可谓牵一发动全身,另一方面日文显示中也有很多汉字,因此这里译者保留了原书日文显示的内容,并补充了一些中文显示的内容。

其实归根结底,显示日文和显示中文都一样,只是要准备好相应的字库就可以了。如果将字库文件内置到操作系统核心中,会导致操作系统很大,更换字体时还需要重新make。因此这里单独生成一个字库文件nihongo.fnt,在操作系统启动时检查到存在该文件,则自动将其读入内存。

日文的字符是采用全角模式显示的,一个全角字符为16 x 16点阵,需要32字节存储。根据JIS的汉字编码表,将所有的汉字都加入到字库中,共需要276KB的容量,这个容量对于本操作系统来说过大了,在启动时会变得很慢。因此这里选取了部分常用的汉字来生成简化版的字库。最终nihongo.fnt的内容如下:

  • 000000 - 000FFF: 显示日文用半角字模,共256个字符(4096字节)
  • 001000 - 02383F: 显示日文用全角字模,共4418个字符(141376字节)

接下来修改主程序,增加自动装载字库的功能:

	/* 载入nihongo.fnt */
	nihongo = (unsigned char *) memman_alloc_4k(memman, 16 * 256 + 32 * 94 * 47);
	fat = (int *) memman_alloc_4k(memman, 4 * 2880);
	file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
	finfo = file_search("nihongo.fnt", (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
	if (finfo != 0) {
		file_loadfile(finfo->clustno, finfo->size, nihongo, fat, (char *) (ADR_DISKIMG + 0x003e00));
	} else {
		for (i = 0; i < 16 * 256; i++) {
			nihongo[i] = hankaku[i]; /* 未找到字库时,半角部分直接复制英文字库 */
		}
		for (i = 16 * 256; i < 16 * 256 + 32 * 94 * 47; i++) {
			nihongo[i] = 0xff; /* 未找到字库,全角部分以0xff填充 */
		}
	}
	*((int *) 0x0fe8) = (int) nihongo;
	memman_free_4k(memman, (int) fat, 4 * 2880);

实现用日文字库来显示字符,首先在struct TASK中添加了一个langmode变量,用于指定一个任务使用内置的英文字库还是使用nihongo.fnt的日文字库。在字符显示时,根据langmode进行不同的处理。

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
	extern char hankaku[4096];
	struct TASK *task = task_now();
	char *nihongo = (char *) *((int *) 0x0fe8);

	if (task->langmode == 0) {
		for (; *s != 0x00; s++) {
			putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
			x += 8;
		}
	}
	if (task->langmode == 1) {
		for (; *s != 0x00; s++) {
			putfont8(vram, xsize, x, y, c, nihongo + *s * 16);
			x += 8;
		}
	}
	return;
}

此外我们还需要一个命令来对langmode的值进行设置:

void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, int memtotal)
{
	……
	} else if (strncmp(cmdline, "langmode ", 9) == 0) {
		cmd_langmode(cons, cmdline);
	} else if (cmdline[0] != 0) {
	……
}

void cmd_langmode(struct CONSOLE *cons, char *cmdline)
{
	struct TASK *task = task_now();
	unsigned char mode = cmdline[9] - '0';
	if (mode <= 1) {
		task->langmode = mode;
	} else {
		cons_putstr0(cons, "mode number error.\n");
	}
	cons_newline(cons);
	return;
}

这样输入langmode 0就设置为英文模式,langmode 1就设置为日文模式。

接下来显示全角字符。根据JIS规格,全角字符的编码以"点,区,面"为单位来进行定义:

  • 1个点对应1个全角字符
  • 1个区中包含94个点
  • 1个面中包含94个区

这是用来确定字符在字库中的位置的。比如对于字符0x82, 0xa0,根据表可知该字符位于04区02点,根据这个编号,就可以计算得到字模的内存地址用于显示。

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
	extern char hankaku[4096];
	struct TASK *task = task_now();
	char *nihongo = (char *) *((int *) 0x0fe8), *font;
	int k, t;

	if (task->langmode == 0) {
		for (; *s != 0x00; s++) {
			putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
			x += 8;
		}
	}
	if (task->langmode == 1) {
		for (; *s != 0x00; s++) {
			if (task->langbyte1 == 0) {
				if ((0x81 <= *s && *s <= 0x9f) || (0xe0 <= *s && *s <= 0xfc)) {
					task->langbyte1 = *s;
				} else {
					putfont8(vram, xsize, x, y, c, nihongo + *s * 16);
				}
			} else {
				if (0x81 <= task->langbyte1 && task->langbyte1 <= 0x9f) {
					k = (task->langbyte1 - 0x81) * 2;
				} else {
					k = (task->langbyte1 - 0xe0) * 2 + 62;
				}
				if (0x40 <= *s && *s <= 0x7e) {
					t = *s - 0x40;
				} else if (0x80 <= *s && *s <= 0x9e) {
					t = *s - 0x80 + 63;
				} else {
					t = *s - 0x9f;
					k++;
				}
				task->langbyte1 = 0;
				font = nihongo + 256 * 16 + (k * 94 + t) * 32;
				putfont8(vram, xsize, x - 8, y, c, font     );	/* 左半部分 */
				putfont8(vram, xsize, x    , y, c, font + 16);	/* 右半部分 */
			}
			x += 8;
		}
	}
	return;
}

putfonts8_asc函数中,每接受到一个字节就会执行一次x += 8,当显示全角字符时,需要在接收到第2个字节之后,再往左回移8个像素并绘制字模的左半部分。

对于换行,当字符串很长时,可能在全角字符的第1个字节处就需要自动换行,这样接收到第2个字节时,字模的左半部分就会画到命令行窗口外面去。所以在遇到第1个字节换行时,将cur_x再右移8个像素。

void cons_newline(struct CONSOLE *cons)
{
	int x, y;
	struct SHEET *sheet = cons->sht;
	struct TASK *task = task_now();
	if (cons->cur_y < 28 + 112) {
		cons->cur_y += 16;
	} else {
		if (sheet != 0) {
			for (y = 28; y < 28 + 112; y++) {
				for (x = 8; x < 8 + 240; x++) {
					sheet->buf[x + y * sheet->bxsize] = sheet->buf[x + (y + 16) * sheet->bxsize];
				}
			}
			for (y = 28 + 112; y < 28 + 128; y++) {
				for (x = 8; x < 8 + 240; x++) {
					sheet->buf[x + y * sheet->bxsize] = COL8_000000;
				}
			}
			sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);
		}
	}
	cons->cur_x = 8;
	if (task->langmode == 1 && task->langbyte1 != 0) {
		cons->cur_x = 16;
	}
	return;
}

运行程序,显示结果:
在这里插入图片描述

对于显示中文汉字,可以对以上nihongo.fnt做如下的修改:

  • 000000 - 000FFF: 英文半角字模,共256个字符(4096字节),来自系统内置字库数据
  • 001000 - 02963F: 中文全角字模,共5170个字符(165440字节),来自HZK16或其他符合GB2312标准的汉字点阵字库

测试中文显示,可以用记事本等文本编辑器编写一个包含中文的文本文件,然后用GB2312编码进行保存。将文本文件装入磁盘映像,用type命令就可以显示出来了。

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

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

相关文章

美畅物联丨剖析 GB/T 28181 与 GB 35114:视频汇聚领域的关键协议

我们在使用畅联云平台进行视频汇聚时&#xff0c;经常会用的GB/T 28181协议&#xff0c;前面我们写了关于GB/T 28181的相关介绍&#xff0c;​ 详见《畅联云平台&#xff5c;关于GB28181你了解多少&#xff1f;》。 ​最近也有朋友向我们咨询GB 35114协议与GB/T 28181有什么不同…

基于STM32的智能运输机器人设计

引言 本项目设计了一个基于STM32的智能运输机器人&#xff0c;能够自动识别路径、避开障碍物&#xff0c;并根据用户的指令将物品运输到指定地点。该机器人集成了超声波传感器、红外传感器、无线通信模块等&#xff0c;通过STM32对电机的控制&#xff0c;实现平稳、智能的运输…

【深度学习】RNN的简单实现

目录 1.RNNCell 2.RNN 3.RNN_Embedding 1.RNNCell import torchinput_size 4 hidden_size 4 batch_size 1idx2char [e, h, l, o] x_data [1, 0, 2, 2, 3] # 输入&#xff1a;hello y_data [3, 1, 2, 3, 2] # 期待&#xff1a;ohlol# 独热向量 one_hot_lookup [[1, …

滑雪——记忆化搜索

题目 代码 //#pragma GCC optimize(3)#include <bits/stdc.h> const int N 310; using namespace std; int dx[4] {-1, 0, 1, 0}, dy[4] {0, 1, 0, -1}; int ans; int g[N][N]; int r, c; int f[N][N]; int dfs(int x, int y) {if(~f[x][y]) return f[x][y];f[x][y] …

TikTok直播带货话术分享,轻松实现销量翻倍

随着TikTok直播带货的不断壮大&#xff0c;越来越多的国内用户开始尝试使用英语进行直播带货。这不仅能够吸引国际观众&#xff0c;还能够扩大市场和提升品牌影响力。 TikTok直播通用带货话术 1. 开场白 开场时&#xff0c;主播可以用热情的语言吸引观众的注意力&#xff1a;…

闭着眼学机器学习——支持向量机分类

引言&#xff1a; 在正文开始之前&#xff0c;首先给大家介绍一个不错的人工智能学习教程&#xff1a;https://www.captainbed.cn/bbs。其中包含了机器学习、深度学习、强化学习等系列教程&#xff0c;感兴趣的读者可以自行查阅。 1. 算法介绍 支持向量机(Support Vector Mach…

AI 能否替代程序员?且听我来一唠!

关于 AI 替代程序员这事儿&#xff0c;咱得仔细唠唠。随着 AI 技术的飞速发展&#xff0c;越来越多人担心程序员会被 AI 取代。程序员会不会失业呢&#xff1f;答案是&#xff1a;没那么简单&#xff01; 首先&#xff0c;AI 确实已经可以干很多程序员的活儿了&#xff0c;比如…

如何构建高效的公路工程资料管理系统?

本文介绍了构建高效的公路工程资料管理系统的方法&#xff0c;涵盖了系统需求分析、功能设计、开发平台选择、开发过程、系统上线与培训、持续改进与维护等关键环节。通过合理规划和科学管理&#xff0c;可以确保系统满足用户需求&#xff0c;提高工作效率&#xff0c;保障公路…

基于Java的超级玛丽游戏的设计与实现(论文+源码)-kaic

摘 要 “超级玛丽”游戏是是任天堂情报开发本部开发的Family Computer横版卷轴动作游戏&#xff0c;它因操作简单、娱乐性强而广受欢迎。Java 的优势在于网络编程与多线程&#xff0c;但其作为一门全场景语言&#xff0c;依然提供了强大的GUI开发API。本论文利用Java的GUI界…

某普SSLVPN 任意文件读取

0x01 产品描述&#xff1a; ‌ 迪普科技的VPN产品是一款面向广域互联应用场景的专业安全网关产品&#xff0c;集成了IPSec、SSL、L2TP、GRE等多种VPN技术&#xff0c;支持国密算法&#xff0c;实现分支机构、移动办公人员的统一安全接入&#xff0c;提供内部业务跨互联网的…

公开课 | 2024最新清华大模型公开课 第4课 大模型学习方法

本文由readlecture.cn转录总结。ReadLecture专注于音、视频转录与总结&#xff0c;2小时视频&#xff0c;5分钟阅读&#xff0c;加速内容学习与传播。 大纲 引言 介绍大模型的训练方法 强调大模型在多领域的应用 大模型的训练阶段 预训练过程 Tokenization的重要性 预训练模…

​面向异构硬件架构:软件支撑和优化技术

面向异构硬件架构&#xff1a;软件支撑和优化技术 本文来自“面向异构硬件架构软件支撑和优化技术”&#xff0c;重点分析了异构硬件成为发展新趋势&#xff0c;系统软件扮演重要新角色&#xff0c;硬件能力单一性与应用需求多样性间的矛盾带来系统性挑战。为了解决这个问题&am…

第五届大数据、人工智能与物联网工程国际会议

第五届大数据、人工智能与物联网工程国际会议&#xff08;ICBAIE 2024&#xff09;定于2024年10月25-27号在中国深圳隆重举行。会议主要围绕大数据、人工智能与物联网工程等研究领域展开讨论。会议旨在为从事大数据、人工智能与物联网工程研究的专家学者、工程技术人员、技术研…

Pandas DataFrame在预测时同样需要传入一个带有相同特征名称的数据框

问题 修改前的代码 import pandas as pd from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_splitmusic_datapd.read_csv("music.csv") X music_data.drop(columns[genre]) ymusic_data[genre] modelDecisionTree…

Vivado时序报告五:Report Exceptions详解

目录 一、前言 二 Report Exceptions 2.1 配置界面 2.2 设计示例 2.3 Exception报告 2.3.1 General information 2.3.2 Summary 2.3.3 Exceptions 2.3.4 Ignored Objects 一、前言 时序约束中&#xff0c;有一类约束属于Exceptions类&#xff0c;之所以称为Exceptions…

Base16编码解码在线工具

具体请前往&#xff1a;在线Base16编码/解码工具-支持utf-8,Latin1,ascii,GBK,Hex等编码

【股市人生】中年投资者的教训:短期盈利与家庭幸福的抉择,理智投资才是成功之道!

大家好&#xff0c;我是肝脑图弟&#xff0c;一个每天都在肝脑图的男人。今天我们来聊聊一个在股市中跌宕起伏的中年人的故事&#xff0c;这个故事不仅让人感慨&#xff0c;也给我们带来了深刻的反思。 股市就像一块美味的蛋糕&#xff0c;吸引着无数人前来品尝。初入股市的人&…

Gin框架教程01:创建一个简单的 Gin 应用

Gin是目前最流行&#xff0c;性能最好的的GOWEB框架&#xff0c;是学习GOLANG必备的知识。本人最近也在学Gin&#xff0c;在b站搜了很多教程&#xff0c;发现有的教程不够详细&#xff0c;有的教程工具包安装有问题&#xff0c;而官方文档又太简短&#xff0c;于是我就想&#…

Java项目:152 基于springboot的仓库管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 系统概要 ​ 本文将介绍一款基于Java开发的仓库管理系统&#xff0c;该系统可以帮助企业实现对仓库物品的高效管理&#xff0c;提高仓库运营效率。文章将详细介…

EMQX服务器的搭建,实现本地机和虚拟机之间的MQTT通信(详细教程)

前言 MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的&#xff0c;这些特点使它适用范围非常广泛。 MQTT协议中有三种身份&#xff1a;发布者&#xff08;Publish&#xff09;、代理&#xff08;Broker&#xff09;&#xff08;…