RVOS环境搭建-01

news2024/11/29 10:37:20

RVOS环境搭建-01

  • 背景介绍
    • 操作系统的定义
    • 操作系统的分类
    • 典型的 RTOS 介绍
    • 课程系统RVOS简介
  • Hello World
    • QEMU介绍
      • QEMU-virt 地址映射
    • 系统引导
      • 引导程序要做哪些事情
        • 如何判断当前hart是不是第一个hart?
        • 如何初始化栈?
    • 如何在屏幕输出Hello World
      • 通过串口输出
      • UART特点
      • UART的物理接口
      • UART的通信协议
      • NS16550a 编程接口介绍
      • NS16550a 的初始化
      • NS16550a 的数据读写


本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来,主要作为xv6操作系统学习的一个前置基础。

RVOS是本课程基于RISC-V搭建的简易操作系统名称。

课程代码和环境搭建教程参考github仓库: https://github.com/plctlab/riscv-operating-system-mooc/blob/main/howto-run-with-ubuntu1804_zh.md


背景介绍

操作系统的定义

在这里插入图片描述


操作系统的分类

在这里插入图片描述


典型的 RTOS 介绍

在这里插入图片描述

在这里插入图片描述


课程系统RVOS简介

在这里插入图片描述


Hello World

QEMU介绍

在这里插入图片描述
片上系统(System-on-Chip,SoC)是指将多个组件和功能集成到单个集成电路(IC)上的完整电子系统或微芯片。它将处理器核心、存储器、输入/输出接口、外设和其他系统组件等多个硬件组件集成在一颗芯片上。

  • SoC旨在为特定应用提供全面的解决方案,例如移动设备、嵌入式系统或物联网(IoT)设备。通过在单个芯片上集成多个组件,SoC具有功耗降低、尺寸更小、性能提升和成本效益等优势。
  • SoC广泛应用于各种电子设备,包括智能手机、平板电脑、智能电视、游戏机、汽车系统和许多其他消费电子产品和工业应用。它们在实现现代电子设备的功能和性能方面发挥着至关重要的作用。

在这里插入图片描述
QEMU(Quick EMUlator)是一个开源的虚拟化软件,用于模拟多种硬件平台和体系结构。“QEMU virt” 是 QEMU 中的一个虚拟平台,用于模拟基于 ARM 架构的虚拟机。它是 QEMU 支持的多个虚拟平台之一,常用于开发和测试 ARM 架构相关的软件和系统。

  • QEMU virt 平台支持运行多种操作系统,包括 Linux、Android 等。它提供了一组虚拟设备和功能,包括 CPU、内存、磁盘、网络等,可以模拟一个完整的虚拟环境,使开发人员能够在此环境中进行应用程序的调试、性能优化、驱动程序的开发等。
  • 通过使用 QEMU virt 平台,开发人员可以在不需要实际硬件的情况下进行 ARM 架构相关软件的开发和测试工作,极大地提高了开发效率和灵活性。同时,QEMU 还提供了丰富的命令行和配置选项,使用户可以根据自己的需求对虚拟机进行定制和扩展。

QEMU-virt 地址映射

在这里插入图片描述
QEMU 是一个虚拟化平台,它通过模拟不同的硬件设备和处理器架构,提供了统一的编址和访问方式。在 QEMU 中,所有的设备都被虚拟化为统一的地址空间,并通过内存映射来访问这些设备。这样,操作系统和应用程序可以使用统一的编程接口和地址空间访问不同的设备,而不需要关注实际的物理硬件细节。


系统引导

在这里插入图片描述
QEMU跑起来之后,BootLoader会跳转到0X8000-0000处继续执行。

QEMU的运行命令参数会携带-kernel参数,该参数指明加载我们的os.elf内核文件到内存。并且os.elf文件在链接时也指明了text代码段被加载到内存中的0x8000 0000位置处。


引导程序要做哪些事情

QEMU默认提供8个模拟的hart,这8个hart一上电都会去运行我们的kernel程序,课程为了简单起见,默认只会使用一个hart,其余hart让其进行空转:
在这里插入图片描述
因此,我们需要在kernel启动程序中编写程序完成上面的需求。


如何判断当前hart是不是第一个hart?

  • 除了x0 - x31 这32个默认提供的通用寄存器外,RSIC-V对于每个特权模式都有自己的一条寄存器,也就是控制状态寄存器CSR
    在这里插入图片描述
  • RSIC-V定义了特殊的指令来访问这些CSR寄存器,因为机器一上电默认运行在Machine模式下,所以我们先关注如何读写Machine模式下的CSR寄存器
    在这里插入图片描述

我们关注的是最上面的Machine Information Registers这组寄存器,这组寄存器中存放了当前机器的相关状态信息,比如: 当前hart的id.

为了读写这组状态寄存器,我们需要使用专门的CSR指令:
在这里插入图片描述
CSRRW指令(原子读写CSR寄存器): 一般可用于实现两个寄存器值的交换,并且这个过程是原子性的,不可打断

如果RD位为x0,则相当于将rs赋值给csr寄存器,因为向x0寄存器写入数据是没有意义的。

在这里插入图片描述
CSRRS(原子读并设置CSR中某一位的值):

如果RS位为x0,则只是单独对CSR寄存器进行读取。

在这里插入图片描述


经过上面的分析可知,如果要实现我们的需求,则需要读取mhartid寄存器:
在这里插入图片描述
核心汇编代码如下:

_start:
	# park harts with id != 0
	csrr	t0, mhartid		# read current hart id
	mv	tp, t0			# keep CPU's hartid in its tp for later usage.
	bnez	t0, park		# if we're not on the halt0,we park the hart
   ...
park:
	wfi
	j	park

Wait for Interrupt instruction (WFI) 是 RISC-V架构定义的一条休眠指令。当处理器执行到 WFI 指令之后,将会停止执行当前的指令流,进入一种空闲状态。这种空闲状态可以被称为“休眠;"状态,直到处理器接收到中断,


如何初始化栈?

这里给出start.S汇编代码:

#include "platform.h"
	 # 每个硬件线程的栈大小为1024字节
	.equ	STACK_SIZE, 1024
    # 声明符号 _start 为全局符号。它是程序的入口点。 
	.global	_start
    # 指定以下代码属于 .text 段,其中包含可执行指令。它标志着 _start 代码的开始。
	.text
_start:
	# park harts with id != 0
    csrr t0, mhartid     # 读取当前硬件线程的ID
    mv tp, t0            # 将CPU的硬件线程ID保存在tp寄存器中以备后用
    bnez t0, park        # 如果不是硬件线程0,则进入休眠状态

   slli t0, t0, 10      # 将硬件线程ID左移10位(相当于乘以1024)
   la sp, stacks + STACK_SIZE    # 将初始栈指针设置为第一个栈空间的末尾
   add sp, sp, t0       # 将当前硬件线程的栈指针移动到栈空间中的相应位置

	j	start_kernel		# hart 0 jump to c

park:
	wfi
	j	park

stacks:
	.skip	STACK_SIZE * MAXNUM_CPU  # 为所有硬件线程分配栈空间
	.end				# 文件结束

这里我们主要关注,如何做到为每个hart分配独立的栈空间(彼此隔离):

	# Setup stacks, the stack grows from bottom to top, so we put the
	# stack pointer to the very end of the stack range.
	slli	t0, t0, 10		# 将当前硬件线程 ID 左移 10 位(相当于乘以 1024)
	la	sp, stacks + STACK_SIZE	# 将栈指针设置为第一个栈空间的末尾
	add	sp, sp, t0		# 将当前硬件线程的栈指针移动到其在栈空间中的位置

这段代码通过使用每个硬件线程的唯一硬件线程ID来隔离每个hart的栈空间。每个硬件线程的ID不同,因此通过将硬件线程ID左移10位(相当于乘以1024),可以为每个硬件线程分配独立的栈空间。

  • 首先,通过读取当前硬件线程的ID并将其存储在寄存器t0中,使用csrr指令。
  • 然后,使用la指令将栈指针sp设置为stacks标签加上STACK_SIZE,即第一个栈空间的末尾地址。这样可以将栈指针设置为栈空间的最末尾。
  • 最后,使用add指令将栈指针sp与硬件线程ID左移10位的结果相加,以将当前硬件线程的栈指针移动到其在栈空间中的位置。由于每个硬件线程的ID不同,因此每个硬件线程的栈指针会被正确地定位到其分配的独立栈空间的位置。
  • 这样,每个硬件线程就具有了自己独立的栈空间,实现了栈空间的隔离。

例如:

  • 当有多个 hart(核心)运行时,每个 hart 都需要有自己独立的栈空间,以避免互相干扰和冲突。

  • 假设有两个 harts,hart0 和 hart1,每个 hart 的栈空间大小为 1024 字节(STACK_SIZE)。

  • 首先,我们将每个 hart 的 ID 左移 10 位(slli t0, t0, 10),相当于将 hart0 的 ID 乘以 1024。然后,我们将 stacks 的起始地址(栈空间的起始地址)与这个偏移量相加,即 la sp, stacks + STACK_SIZE。

  • 假设 stacks 的起始地址为 0x1000。对于 hart0,它的 ID 是 0,因此 t0 的值为 0,偏移量为 0。所以 la sp, stacks + STACK_SIZE 将 sp 设置为 0x1000 + 1024 = 0x1400, add sp, sp, t0 —> 0x1400 + 0,即 hart0 的栈空间的起始地址。

  • 对于 hart1,它的 ID 是 1,因此 t0 的值为 1,偏移量为 1024。所以 la sp, stacks + STACK_SIZE 将 sp 设置为 0x1000 + 1024 = 0x1400, add sp, sp, t0 --> 0x1400 + 1024 = 0x1800,即 hart1 的栈空间的起始地址。

  • 通过这样的操作,我们将不同的 harts 的栈空间隔离开来,每个 hart 都有自己独立的栈空间,互不干扰。当各个 harts 运行时,它们可以在各自的栈空间上进行栈操作,而不会相互冲突。


如何在屏幕输出Hello World

通过串口输出

在这里插入图片描述

UART代表通用异步收发器(Universal Asynchronous Receiver-Transmitter)。它是一种常用的串行通信协议,用于两个设备之间的通信。UART协议允许一次只传输和接收一位数据,通过单个数据线进行通信。

UART被广泛应用于各种应用中,包括嵌入式系统、微控制器以及计算机、调制解调器和传感器等不同设备之间的通信接口。它提供了一种简单高效的方法,用于设备之间的数据传输和接收。

UART通信包括起始位,随后是数据位(通常为8位),用于错误检测的可选奇偶校验位,以及停止位或多个停止位。起始位表示数据帧的开始,而停止位表示帧的结束。数据以异步方式传输,意味着设备之间没有共享时钟信号。

UART在点对点配置中运行,其中两个设备直接连接使用两条数据线:一条用于发送数据(TX),一条用于接收数据(RX)。一个设备的TX线连接到另一个设备的RX线,反之亦然。这允许设备之间的双向通信。


UART特点

在这里插入图片描述


UART的物理接口

在这里插入图片描述
UART物理接口通常由以下几个引脚组成:

  1. TX (Transmit): 该引脚用于发送数据。数据从UART发送器输出到这个引脚,经过串行传输发送到接收设备。

  2. RX (Receive): 该引脚用于接收数据。接收设备通过该引脚接收从发送设备发送的数据。

  3. GND (Ground): 地线引脚,用于提供电路的地连接。

除了上述必要的引脚外,UART接口可能还包括其他可选引脚,如:

  1. RTS (Request to Send): 请求发送引脚,用于控制数据发送。发送设备通过该引脚向接收设备发出发送请求。

  2. CTS (Clear to Send): 清除发送引脚,用于确认接收设备准备好接收数据。接收设备通过该引脚向发送设备发送准备好接收的信号。

  3. DTR (Data Terminal Ready): 数据终端就绪引脚,用于指示设备准备好进行数据通信。

  4. DSR (Data Set Ready): 数据集就绪引脚,用于指示接收设备准备好接收数据。

  5. RI (Ring Indicator): 响铃指示引脚,用于指示电话线路上是否有电话呼叫信号。

这些引脚的具体命名和功能可能在不同的设备和应用中有所不同,但上述列举的是UART接口常见的引脚。


UART的通信协议

在这里插入图片描述


NS16550a 编程接口介绍

QUME是一个知名的串口模拟器和仿真工具,它可以模拟各种串口设备,包括NS16550A芯片所提供的功能。因此,通过QUME,可以模拟NS16550A串口芯片的行为和接口。

使用QUME,可以创建虚拟串口设备,并通过配置参数来模拟NS16550A芯片的寄存器、数据传输、中断和状态等功能。因此我们能够进行串口通信的仿真和测试,而无需实际的硬件设备。

QUME提供了丰富的功能,包括模拟不同的串口参数、配置波特率、数据位数、校验位、停止位等,并且可以模拟接收和发送数据,监测串口状态和中断等。这样可以在虚拟环境中进行串口编程和调试,以确保代码在实际环境中正常工作。

需要注意的是,QUME是一个软件工具,它提供了对串口功能的模拟和仿真,但并不直接与硬件设备通信。因此,在实际使用中,QUME可以作为开发、测试和调试串口通信应用程序的有用工具,但在实际的硬件系统中,需要使用NS16550A芯片或其他串口硬件来实现真正的串口通信。
在这里插入图片描述
NS16550A是一种常用的串口通信芯片,它提供了一个编程接口,用于配置和控制串口通信功能。以下是NS16550A芯片的编程接口的基本介绍:

  1. 数据寄存器 (Data Register):用于读取和写入串口数据。通过读取数据寄存器,可以获取接收到的数据;通过写入数据寄存器,可以发送数据。

  2. 状态寄存器 (Status Register):用于获取串口的状态信息。可以通过读取状态寄存器来了解串口的接收和发送状态,包括是否有接收到数据、是否可以发送数据等。

  3. 控制寄存器 (Control Register):用于配置和控制串口的各种参数和功能。通过写入控制寄存器,可以设置波特率、数据位数、校验位、停止位等串口参数,以及启用或禁用接收和发送功能。

  4. 波特率发生器 (Baud Rate Generator):用于设置串口的波特率。通过设置波特率发生器的值,可以定义串口通信的传输速率。

  5. 中断控制寄存器 (Interrupt Control Register):用于配置和控制串口的中断功能。通过写入中断控制寄存器,可以启用或禁用不同类型的中断,如接收中断、发送中断等。

在这里插入图片描述

通过访问这些寄存器,可以对NS16550A芯片进行编程控制,实现对串口通信的配置、数据传输和状态监测等操作。具体的编程接口使用方式和寄存器地址等信息可以参考NS16550A芯片的数据手册或相关文档。


NS16550a 的初始化

  • 关闭中断
  • 设置波特率
  • 设置异步数据通信格式
    在这里插入图片描述

在这里,"关闭中断"指的是禁用串口(UART)的中断功能,即禁止串口触发和处理中断事件。

串口通信中的中断通常用于以下目的:

  • 接收中断:当串口接收到数据时,会触发接收中断,通知处理器有新的数据可供处理。
  • 发送中断:当串口发送缓冲区为空时,会触发发送中断,通知处理器可以继续发送新的数据。

通过禁用中断,就是告诉串口不要触发和处理这些中断事件。这样可以避免在初始化期间由于中断的发生而引起的干扰和错误。

禁用中断不会影响串口的数据传输功能,它仅仅是关闭了中断的触发和处理机制。一旦初始化完成,并且需要启用中断来处理接收和发送数据的中断事件时,可以通过适当的设置和配置重新启用中断。

完整代码注释如下: (可参考NS16550a相关文档进行学习)

void uart_init()
{
	/* disable interrupts. */
	uart_write_reg(IER, 0x00);

	/*
	 * Setting baud rate. Just a demo here if we care about the divisor,
	 * but for our purpose [QEMU-virt], this doesn't really do anything.
	 *
	 * Notice that the divisor register DLL (divisor latch least) and DLM (divisor
	 * latch most) have the same base address as the receiver/transmitter and the
	 * interrupt enable register. To change what the base address points to, we
	 * open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
	 * (DLAB), which is bit index 7 of the Line Control Register (LCR).
	 *
	 * Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
	 * We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
	 * And due to the divisor register is two bytes (16 bits), so we need to
	 * split the value of 3(0x0003) into two bytes, DLL stores the low byte,
	 * DLM stores the high byte.
	 */
	uint8_t lcr = uart_read_reg(LCR);
	uart_write_reg(LCR, lcr | (1 << 7));
	uart_write_reg(DLL, 0x03);
	uart_write_reg(DLM, 0x00);

	/*
	 * Continue setting the asynchronous data communication format.
	 * - number of the word length: 8 bits
	 * - number of stop bits: 1 bit when word length is 8 bits
	 * - no parity
	 * - no break control
	 * - disabled baud latch
	 */
	lcr = 0;
	uart_write_reg(LCR, lcr | (3 << 0));
}

相关宏定义:

//读写uart寄存器的相关宏定义
#define uart_read_reg(reg) (*(UART_REG(reg)))
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))
/*
 * The UART control registers are memory-mapped at address UART0. 
 * This macro returns the address of one of the registers.
 */
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))
/* This machine puts UART registers here in physical memory. */
#define UART0 0x10000000L

NS16550a 的数据读写

在这里插入图片描述

  • TSR寄存器(Transmit Status Register)是一个用于发送状态信息的寄存器,通常在串口通信芯片中存在。
  • LSR寄存器(Line Status Register)是一个用于线路状态信息的寄存器,通常在串口通信芯片中存在。

这里采用轮询的方式实现数据的发送—>putc:

int uart_putc(char ch){
    //不断读取LSR寄存器,获取其第五位,判断是否为0,为0表示空闲
	while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
	//如果空闲就向THR寄存器中写入数据
	return uart_write_reg(THR, ch);
}

启动函数:

extern void uart_init(void);
extern void uart_puts(char *s);

void start_kernel(void){
    //初始化串口设备
	uart_init();
	//输出字符
	uart_puts("Hello, RVOS!\n");

	while (1) {}; // stop here!
}

编译运行:
在这里插入图片描述

注意如何退出qemu:

在这里插入图片描述

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

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

相关文章

无线通信技术

无线通信网包括面向语言通信的移动电话系统以及面向数据传输的无线局域网和无线广域网。 蜂窝通信系统&#xff1a; 1978年&#xff0c;美国贝尔实验室开发了高级移动电话系统&#xff08;AMPS&#xff09;。 AMPS采用模拟制式的频分双工&#xff08;FDD&#xff09;技术 第…

mysql数据类型有哪几种

Mysql支持的多种数据类型主要有&#xff1a;数值数据类型、日期/时间类型、字符串类型。 整数 浮点数&定点数 注&#xff1a;定点数以字符串形式存储&#xff0c;对精度要求高时使用decimal较好&#xff1b;尽量避免对浮点数进行减法和比较运算。 时间/日期类型 字符串类型…

Temporal.Duration 规范用法

后端突然告诉我返回给我的时间用了一个新的规范&#xff0c;我展示的时候突然发现这个规范蛮有意思&#xff0c;算是一个新的规范&#xff0c;展示到页面的时候也思考了很多&#xff0c;记录一下子~&#xff08;注&#xff1a;此 blog 主要目的仅是供自己记录&#xff0c;所以写…

chatgpt赋能python:Python取出列表中的某个数

Python取出列表中的某个数 在Python中&#xff0c;列表是一种非常重要的数据类型&#xff0c;它可以用来存储一系列有序的元素。在实际的开发中&#xff0c;经常会需要从列表中取出某个特定的数值&#xff0c;本文将介绍如何在Python中完成这个操作。 1. 使用index方法 Pyth…

AI实战营:MMPose开源算法库

RTMPose关键点检测全流程 MMPose预训练模型预测-命令行 预测单张图 # HRNet python .\demo\topdown_demo_with_mmdet.py .\demo\mmdetection_cfg\faster_rcnn_r50_fpn_coco.py https://download.openmmlab.com/mmdetection/v2.0/faster_rcnn/faster_rcnn_r50_fpn_1x_coco/faste…

chatgpt赋能python:Python反转语句:实用技巧提升编程能力

Python反转语句&#xff1a;实用技巧提升编程能力 Python是一门流行的编程语言&#xff0c;许多开发者都会选择Python作为主要的开发语言。其中一个原因是Python的语法简单易学&#xff0c;不仅适合初学者入门&#xff0c;也能被经验丰富的开发者用来构建复杂应用程序。在本文…

【网络原理】数据链路层 和 应用层 重点协议

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; 目 录 &#x1f340;一. 以太网协议&#xff08;数据链路层&#xff09;&#x1f33b;二. DNS &#xff08;应用层&#xff09;&#x1f33f;三. 网络原理知识面试总结 &#x1f34…

【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究SpringMVC的核心原理和运作机制(总体框架原理篇)

全方位渗透和探究SpringMVC的核心原理和运作机制 SpringMVC简介Spring Web MVC是什么Spring Web MVC的工作模式Spring Web MVC能帮我们做什么SpringMVC的底层依赖 SpringMVC工作流程SpringMVC中的组件DispatcherServlet前端控制器DispatcherServlet的职责和作用 HandlerMapping…

chatgpt赋能python:Python如何发送手机短信:实现方法解析和应用场景介绍

Python如何发送手机短信&#xff1a;实现方法解析和应用场景介绍 随着移动 App 的兴起&#xff0c;手机短信已成为现代人日常生活中不可或缺的一部分。在有些场景下需要编写 Python 程序去自动化发送短信&#xff0c;例如企业营销活动、警报系统等。本文将介绍 Python 发送短信…

chatgpt赋能python:Python可以搜集网上资料吗?

Python 可以搜集网上资料吗&#xff1f; Python 是一种非常流行的编程语言&#xff0c;它广泛应用于数据科学、机器学习、人工智能等领域。作为一名 Python 工程师&#xff0c;我们经常需要在互联网上搜集资料来解决开发中的问题&#xff0c;但是有些人会问&#xff1a;Python…

设计模式B站学习(一)(java)

这里写目录标题 一、设计模式概述1.1 软件设计模式的产生背景1.2 软件设计模式的概念1.3 学习设计模式的必要性1.4 设计模式分类 二、UML图2.1 类图概述2.2 类图的作用2.3 类图表示法2.3.1 类图表示方法2.3.2 类与类之间关系的表示方法2.3.2.1 关联关系2.3.2.2 聚合关系2.3.2.3…

C语言,指针详解

1. 指针是什么&#xff1f; 1. 指针是内存中一个最小单元的编号 2. 口头上的“指针”&#xff0c;指的是指针变量 #include<stdio.h>int main() {int a10;int * pa&a;// pa 是一个指针变量,用来存放 a 的地址//int ——> pa 所指向对象的类型//* 代表 pa是个指针…

【ros/ros2】ros2 humble镜像制作过程中碰到的问题记录

文章目录 序言1. 避免手动设置时区2. docker build中间某一步失败了3. sudo apt install ros-humble-desktop安装报错 E: Unable to locate package ros-humble-desktop4. 编译ros2 humnble时报错5. sudo rosdep init失败6. 下载依赖时失败&#xff1a;rosdep install --from-p…

局域网技术

共享信道的分配技术是局域网的核心技术&#xff0c;而这一技术又与网络的拓扑结构和传输介质有关。 拓扑结构&#xff1a; 1.总线型拓扑&#xff1a; 总线一种多点广播介质&#xff0c;所有的站点通过接口硬件连接到总线上。 传输介质主要是同轴电缆&#xff08;基带和宽带…

chatgpt赋能python:Python取消空格:如何优化代码和改善SEO?

Python取消空格&#xff1a;如何优化代码和改善SEO&#xff1f; 如果你是一位有10年python编程经验的工程师&#xff0c;那么你一定深刻体会到代码的质量对软件应用的重要性。而在今天&#xff0c;随着搜索引擎优化&#xff08;SEO&#xff09;的发展和对网页排名的更高要求&a…

FastBup:计算机视觉大型图像数据集分析工具

0.简介 官方github网址项目目的&#xff1a;当前大规模图像数据集一团糟&#xff0c;数据量巨大但质量堪忧&#xff0c;有时候训练集、验证集、测试集会有重复数据造成数据泄露。FastBup可以识别重复项、近似重复项、异常图像、错误标注、异常值&#xff0c;在cpu上就可以处理…

Apache Kafka - 流式处理

文章目录 概述什么是流式处理流式处理的一些概念时间状态流和表的二元性时间窗口 流式处理的设计模式单个事件处理使用本地状态多阶段处理和重分区使用外部查找——流和表的连接流与流的连接乱序的事件重新处理 概述 Kafka被广泛认为是一种强大的消息总线&#xff0c;可以可靠…

Zabbix(一)

介绍 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 功能组件 Server &#xff1a; Zabbix server是zabbix软件的核心组件 Zabbix agent向其报告可用性、系统完整性和统计信息 Zabbix server存储所有的配置信息、统计信息和操作信…

chatgpt赋能python:Python取出列表中的若干元素

Python取出列表中的若干元素 Python是一个功能强大且易于学习的编程语言&#xff0c;具有广泛的应用领域。在数据科学和机器学习中&#xff0c;Python是一个非常流行的语言&#xff0c;因为它具有许多用于处理数据和矩阵运算的库&#xff0c;例如NumPy和pandas。在Python的列表…

chatgpt赋能python:使用Python向微信发送信息的方法详解

使用Python向微信发送信息的方法详解 Python作为一种广泛应用于科学计算、数据处理等多个领域的编程语言&#xff0c;也可以用于自动化工作流程和自动发送微信消息等操作&#xff0c;大大提高了工作效率。如果你想在日常工作中用Python向微信群或个人发送自定义信息&#xff0…