存储课程学习笔记1_访问scsi磁盘读写测试(struct sg_io_hdr,ioctl,mmap)

news2025/1/16 13:45:04

创建虚拟机时,可以选择SCSI,STAT,NVME不同类型的磁盘。

0:总结

===》了解内核提供的访问scsi的结构和方法 (主要是sg_io_hdr_t 结构体和ioctl函数)。

===》需要读scsi协议文档,了解相关指令,只演示了16字节固定长度读和写指令。

===》了解mmap,直接映射磁盘可以实现读写功能。

1:简单了解概念。

sata 是串行接口,访问sata设备, 除了使用控制指令(原语交互),就是使用fis数据包进行数据交互。(直接使用串口连接进行通信)

scsi 采用并行传输方式,支持多个指令同时发送,需要参考对应的协议文档,构造协议进行磁盘的交互。

nvme是一种基于pcie总线封装的存储接口协议,支持多个队列、并行操作和低延迟I/O操作等。

2:构造scsi相关交互指令,和scsi设备进行读写交互。

这里采用虚拟机测试的方法,新增磁盘,选择磁盘类型为scsi,操作改磁盘。

2.1 内核中提供了专门的结构,使用ioctl进行写入。

主要关注struct sg_io_hdr 结构体,构造对应的对象,用ioctl进行与scsi设备的交互。

sg_io_hdr_t 是用于与 SCSI 设备进行通信的数据结构,它包含了执行 SCSI I/O 操作时所需的各种参数和状态信息。这个结构体在进行 SCSI 命令的传输和数据交互时起到关键的作用。

主要了解 #include <scsi/sg.h> 头文件内容 
typedef struct sg_io_hdr
{
    int interface_id;           //表示接口标识,通常设置为 ‘S’,表示 SCSI generic。
    int dxfer_direction;        // 数据传输方向   SG_DXFER_NONE: 没有数据传输。   SG_DXFER_TO_DEV: 将数据从主机传输到设备。  SG_DXFER_FROM_DEV: 将数据从设备传输到主机。  SG_DXFER_TO_FROM_DEV: 双向数据传输。
    unsigned char cmd_len;      // SCSI 命令的长度(字节数)。
    unsigned char mx_sb_len;    //可写入 sense_buffer(感知缓冲区)的最大长度
    unsigned short iovec_count; //scatter-gather 元素的数量。0 表示没有 scatter-gather 操作。
    unsigned int dxfer_len;     //数据传输的总字节数
    void __user *dxferp;	    //指向数据传输内存或 scatter-gather 列表的指针   可以传多个地址
    unsigned char __user *cmdp;  //指向要执行的 SCSI 命令的指针
    void __user *sbp;		 //指向 sense_buffer 内存的指针,用于存储设备返回的感知数据
    unsigned int timeout;       // 操作的超时时间,单位为毫秒。MAX_UINT 表示没有超时限制
    unsigned int flags;         //标志位,控制操作的一些行为。可以使用 SG_FLAG... 常量
    int pack_id;                // 用于内部用途的包标识,通常不使用。
    void __user * usr_ptr;      // 内部用途的用户指针,通常不使用
    unsigned char status;       //SCSI 命令的状态
    unsigned char masked_status;//经过位移和掩码处理后的 SCSI 状态
    unsigned char msg_status;   //消息级别的数据(可选)。
    unsigned char sb_len_wr;    //实际写入到 sense_buffer 的字节数
    unsigned short host_status;   //主机适配器返回的错误状态
    unsigned short driver_status; //软件驱动程序返回的错误状态。
    int resid;                  //实际传输的字节数与预期传输的字节数之间的差值
    unsigned int duration;      // 命令执行的时间,单位为毫秒。
    unsigned int info;          /* [o] auxiliary information */
} sg_io_hdr_t;  /* 64 bytes long (on i386) */

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...); //request 是依赖于设备的请求码,使用SG_IO标志与scsi设备进行交互

2.2 参考scsi协议文档,构造对应指令与scsi设备进行交互。

这里参考文档中直接访问指令相关 写和读磁盘相关指令,可以看到提供不同长度的固定长度协议指令(6,10,12,16,32),这里只用固定长度16字节构造指令实现写和读的功能进行测试。

2.2.1 16字节固定长度读指令构造

文档中对应的16位固定长度读指令如下:

在这里插入图片描述

对应协议构造与触发指令如下:

//参考对应的协议  设置相关指令进行读取  blkname为对应的scsi设备 lba为读位置, cnt_of_blocks为读的块数
int scsi_cmd16_read(char *blkname, int lba, int cnt_of_blocks)
{
	int fd;
	if ((fd = open(blkname, O_RDWR)) < 0) {
		printf("device file opening failed\n");
		return -1;
	}

	//按上面固定长度构造对应的指令
	char cmd[16] = {
		0x88, 0, 0, 0, 0, 0, (lba >> 24), (lba >> 16), (lba >> 8), lba,
		(cnt_of_blocks >> 24), (cnt_of_blocks >> 16), (cnt_of_blocks >> 8), cnt_of_blocks,
		0, 0
	};

	char *buffer = (char *)malloc(cnt_of_blocks * BLOCK_SIZE);
	char sense_buffer[32] = {0};
	
	sg_io_hdr_t io_hdr;
	io_hdr.interface_id = 'S';
	io_hdr.cmdp = cmd;
	io_hdr.cmd_len = 16;

	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
	io_hdr.dxfer_len = BLOCK_SIZE * cnt_of_blocks;
	io_hdr.dxferp = buffer;

	io_hdr.sbp = sense_buffer; //附加数据地址
	io_hdr.mx_sb_len = 32;     //附加数据数据长度

	io_hdr.timeout = 20000;

	// sync
	int i = 0;
	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
		for (i = 0;i < 32;i ++) { //附加数据中信息 
			printf("%hx ", sense_buffer[i]);
		}
		printf("\n");
		return -1;
	}

	for (i = 0;i < BLOCK_SIZE * cnt_of_blocks;i ++) {
		if (i % BLOCK_SIZE == 0) {
			printf("\n\n new block \n");
		}
		printf("%hx ", buffer[i]);
	}

	printf("\n");
	close(fd);
}
2.2.2 16字节固定长度写指令构造

协议文档文档中写16位固定长度对应的协议

在这里插入图片描述

对应的代码模块:

int scsi_cmd16_write(char *blkname, int lba, int cnt_of_blocks)
{
	int fd;
	if ((fd = open(blkname, O_RDWR)) < 0) {
		printf("device file opening failed\n");
	}
	//参考协议构造对应的指令 
	char cmd[16] = {
		0x8A, 0, 0, 0, 0, 0, (lba >> 24), (lba >> 16), (lba >> 8), lba,
		(cnt_of_blocks >> 24), (cnt_of_blocks >> 16), (cnt_of_blocks >> 8), cnt_of_blocks,
		0, 0
	};

	char *buffer = (char *)malloc(cnt_of_blocks * BLOCK_SIZE);
	int i = 0;
	for (i = 0;i < cnt_of_blocks * BLOCK_SIZE;i ++) {
		buffer[i] = i % 0x80;
	}
	
	char sense_buffer[32] = {0};  //附加信息 用于存储对应的执行结果

	sg_io_hdr_t io_hdr;
	io_hdr.interface_id = 'S';
	io_hdr.cmdp = cmd;
	io_hdr.cmd_len = 16;

	io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
	io_hdr.dxfer_len = BLOCK_SIZE * cnt_of_blocks;
	io_hdr.dxferp = buffer;
	io_hdr.sbp = sense_buffer;
	io_hdr.mx_sb_len = 32;
	io_hdr.timeout = 20000;

	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
		for (i = 0;i < 32;i ++) {
			printf("%hx ", sense_buffer[i]);
		}
		printf("\n");
		return -1;
	}

	printf("write successfull\n");
	return 0;
}
2.2.3 16字节固定长度读写指令进行测试 main函数。
//了解scsi文档 参考内部其中块控制指令read和write实现对其进行demo测试
int main(int argc, char *argv[]) {

	char *blkname;

	if (argc != 4) return -1;

	blkname = argv[1];      //scsi设备  

	//逻辑地址和物理地址有映射关系 可以研究,逻辑地址一般是512字节
	int lba = atoi(argv[2]);             //逻辑块地址 和物理地址有映射关系 
	int cnt_of_blocks = atoi(argv[3]);   //块个数

	int ret = scsi_cmd16_write(blkname, lba, cnt_of_blocks);
	if (ret) return -1; 
	
	ret = scsi_cmd16_read(blkname, lba, cnt_of_blocks);
	if (ret) return -1;

	return 0;
}
2.2.4 测试运行
#查找对应设备 找到新增的scsi设备
ubuntu@ubuntu:~/start_test$ lsblk
...
sdb                         8:16   0   10G  0 disk 
sr0                        11:0    1  1.8G  0 rom  
root@ubuntu:/home/ubuntu/start_test# gcc scsi_cmd_test.c -o scsi_cmd_test
root@ubuntu:/home/ubuntu/start_test# ./scsi_cmd_test /dev/sdb 32 2
write successfull

 new block 
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 ...

 new block 
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 ...

2.3 使用mmap映射直接进行写和读测试

测试代码如下:

int main(int argc, char *argv[]) {
	char *blkname;
	if (argc != 4) return -1;
	blkname = argv[1];      //scsi设备  

	//逻辑地址和物理地址有映射关系 可以研究,逻辑地址一般是512字节
	int lba = atoi(argv[2]);             //逻辑块地址 和物理地址有映射关系 
	int cnt_of_blocks = atoi(argv[3]);   //块个数

	//这里注意mmap映射时相关参数的设置  普通文件 设备文件的映射参数用MAP_SHARED才写入成功
	int ret;
	ret = scsi_mmap_write(blkname, lba, cnt_of_blocks);
	if (ret) return -1;

	ret = scsi_mmap_read(blkname, lba, cnt_of_blocks);
	if (ret) return -1;
	return 0;
}

运行结果如下:

root@ubuntu:/home/ubuntu/start_test# ./scsi_cmd_test /dev/sdb 128 2

 new block 
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19...

 new block 
0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19...

2.4:完整测试代码

可以尝试使用指令和mmap交互写和读,查看结果。

//sata scsi nvme几种设备类型 sata串口通信的方式  scsi使用并行交互  nvme借助pcie
//sata 使用控制指令和fsi命令进行控制
//scsi 参考对应的文档中指令  按指令协议构造 sg_io_hdr_t 用ioctl 和scsi设备进行交互
//nvme 设备内核中也提供了对应的结构和函数 控制对应设备 可以设计对应的block数据结构依次控制整个磁盘 扇区 inode节点  block之间的关系

/*************************************
struct sg_io_hdr {
    int interface_id;       // 接口标识符(通常设置为 'S')
    int dxfer_direction;    // 数据传输方向,可选值:SG_DXFER_NONE, SG_DXFER_TO_DEV, SG_DXFER_FROM_DEV, SG_DXFER_TO_FROM_DEV
    unsigned char cmd_len;  // 命令长度(单位字节)
    unsigned char mx_sb_len;  // 最大附加数据长度(单位字节)
    unsigned short iovec_count;  // 散列/聚合缓冲区数量
    unsigned int dxfer_len;  // 数据传输长度(单位字节)
    void *dxferp;           // 数据缓冲区指针
    void *cmdp;             // 命令缓冲区指针
    void *sbp;              // 附加数据缓冲区指针
    unsigned int timeout;   // 超时时间(毫秒)
    unsigned int flags;     // 标志位控制参数
    int pack_id;            // 请求包 ID (多个请求可以使用相同的 ID 进行关联)
    void *usr_ptr;          // 用户定义的指针,可用于自定义操作或回调函数等
}

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
	addr:指定映射的起始地址,通常设置为 NULL,由操作系统自动选择合适的地址。
	length:指定映射的长度,以字节为单位。
	prot:指定映射区域的保护方式(权限)。可以是以下值之一:
		PROT_NONE:无权限,不能访问。
		PROT_READ:可读权限。
		PROT_WRITE:可写权限。
		PROT_EXEC:可执行权限。 这些值也可以通过按位或运算组合使用。
	flags:指定了一些选项标志:
	MAP_SHARED:对映射区域的修改会反映到底层文件中,并且多个进程可以共享该区域(需要有正确设置的文件描述符)。
	MAP_PRIVATE:对映射区域进行修改不会影响底层文件,并且对该区域的写入操作会产生私有副本(每个进程独立拥有一份副本)。
	MAP_FIXED:指定映射到的地址必须是准确的,如果不可用则会报错。
	fd:要映射的文件描述符(仅在映射文件时使用),如果是共享内存或匿名映射,则为 -1。
	offset:文件中的偏移量,指定从哪个位置开始映射文件(仅在映射文件时使用)。
*************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/sg.h>
#include <sys/mman.h>

#define BLOCK_SIZE		512
int scsi_cmd16_write(char *blkname, int lba, int cnt_of_blocks);
int scsi_cmd16_read(char *blkname, int lba, int cnt_of_blocks);

int scsi_mmap_write(char *blkname, int lba, int cnt_of_blocks);
int scsi_mmap_read(char *blkname, int lba, int cnt_of_blocks);
//了解scsi文档 参考内部其中块控制指令read和write实现对其进行demo测试
int main(int argc, char *argv[]) {

	char *blkname;

	if (argc != 4) return -1;

	blkname = argv[1];      //scsi设备  

	//逻辑地址和物理地址有映射关系 可以研究,逻辑地址一般是512字节
	int lba = atoi(argv[2]);             //逻辑块地址 和物理地址有映射关系 
	int cnt_of_blocks = atoi(argv[3]);   //块个数

	// int ret = scsi_cmd16_write(blkname, lba, cnt_of_blocks);
	// if (ret) return -1; 
	
	// ret = scsi_cmd16_read(blkname, lba, cnt_of_blocks);
	// if (ret) return -1;

	//这里注意mmap映射时相关参数的设置  普通文件 设备文件的映射参数用MAP_SHARED才写入成功
	int ret;
	ret = scsi_mmap_write(blkname, lba, cnt_of_blocks);
	if (ret) return -1;

	ret = scsi_mmap_read(blkname, lba, cnt_of_blocks);
	if (ret) return -1;
	return 0;
}

int scsi_cmd16_write(char *blkname, int lba, int cnt_of_blocks)
{
	int fd;
	if ((fd = open(blkname, O_RDWR)) < 0) {
		printf("device file opening failed\n");
	}

	char cmd[16] = {
		0x8A, 0, 0, 0, 0, 0, (lba >> 24), (lba >> 16), (lba >> 8), lba,
		(cnt_of_blocks >> 24), (cnt_of_blocks >> 16), (cnt_of_blocks >> 8), cnt_of_blocks,
		0, 0
	};

	char *buffer = (char *)malloc(cnt_of_blocks * BLOCK_SIZE);
	int i = 0;
	for (i = 0;i < cnt_of_blocks * BLOCK_SIZE;i ++) {
		buffer[i] = i % 0x80;
	}
	
	char sense_buffer[32] = {0};  //附加信息 用于存储对应的执行结果

	sg_io_hdr_t io_hdr;
	io_hdr.interface_id = 'S';
	io_hdr.cmdp = cmd;
	io_hdr.cmd_len = 16;

	io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
	io_hdr.dxfer_len = BLOCK_SIZE * cnt_of_blocks;
	io_hdr.dxferp = buffer;

	io_hdr.sbp = sense_buffer;
	io_hdr.mx_sb_len = 32;
	
	io_hdr.timeout = 20000;

	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
		for (i = 0;i < 32;i ++) {
			printf("%hx ", sense_buffer[i]);
		}
		printf("\n");
		return -1;
	}

	printf("write successfull\n");

	return 0;
}
//参考对应的协议  设置相关指令进行读取
int scsi_cmd16_read(char *blkname, int lba, int cnt_of_blocks)
{
	int fd;
	if ((fd = open(blkname, O_RDWR)) < 0) {
		printf("device file opening failed\n");
		return -1;
	}

	// 1024, 0x400
	char cmd[16] = {
		0x88, 0, 0, 0, 0, 0, (lba >> 24), (lba >> 16), (lba >> 8), lba,
		(cnt_of_blocks >> 24), (cnt_of_blocks >> 16), (cnt_of_blocks >> 8), cnt_of_blocks,
		0, 0
	};

	char *buffer = (char *)malloc(cnt_of_blocks * BLOCK_SIZE);
	char sense_buffer[32] = {0};
	
	sg_io_hdr_t io_hdr;
	io_hdr.interface_id = 'S';
	io_hdr.cmdp = cmd;
	io_hdr.cmd_len = 16;

	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
	io_hdr.dxfer_len = BLOCK_SIZE * cnt_of_blocks;
	io_hdr.dxferp = buffer;

	io_hdr.sbp = sense_buffer; //附加数据地址
	io_hdr.mx_sb_len = 32;     //附加数据数据长度

	io_hdr.timeout = 20000;

	// sync
	int i = 0;
	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
		for (i = 0;i < 32;i ++) { //附加数据中信息 
			printf("%hx ", sense_buffer[i]);
		}
		printf("\n");
		return -1;
	}

	for (i = 0;i < BLOCK_SIZE * cnt_of_blocks;i ++) {
		if (i % BLOCK_SIZE == 0) {
			printf("\n\n new block \n");
		}
		printf("%hx ", buffer[i]);
	}

	printf("\n");
	close(fd);
}

int scsi_mmap_read(char *blkname, int lba, int cnt_of_blocks) {

	int fd;
	if ((fd = open(blkname, O_RDWR)) < 0) {
		printf("device file opening failed\n");
		return -1;
	}

	off_t size = lseek(fd, 0, SEEK_END);
	if (size == -1) {
		perror("Failed to get disk size");
		close(fd);
		exit(EXIT_FAILURE);
	}


	void *mmaped = mmap(NULL, BLOCK_SIZE * cnt_of_blocks, PROT_WRITE | PROT_READ, MAP_PRIVATE, fd, lba * BLOCK_SIZE); 
	if (mmaped == MAP_FAILED) {
		perror("Failed to mmap disk\n");
		close(fd);
		return -1;
	}

	char *buffer = (char *)mmaped;
	int i = 0;
	for (i = 0;i < BLOCK_SIZE * cnt_of_blocks;i ++) {
		if (i % BLOCK_SIZE == 0) {
			printf("\n\n new block \n");
		}
		printf("%hx ", buffer[i]);
	}
	printf("\n");

	munmap(mmaped, BLOCK_SIZE * cnt_of_blocks);
	close(fd);

	return 0;
}

//这里的写入是没有用的 需要通过协议写入 
int scsi_mmap_write(char *blkname, int lba, int cnt_of_blocks) {

	int fd;
	if ((fd = open(blkname, O_RDWR)) < 0) {
		printf("device file opening failed\n");
		return -1;
	}

	void *mmaped = mmap(NULL, BLOCK_SIZE * cnt_of_blocks, PROT_READ | PROT_WRITE, MAP_SHARED, fd, lba * BLOCK_SIZE); 
	if (mmaped == MAP_FAILED) {
		perror("Failed to mmap disk\n");
		close(fd);
		return -1;
	}

	char *buffer = (char *)mmaped;
	int i = 0;
	for (i = 0;i < cnt_of_blocks * BLOCK_SIZE;i ++) {
		buffer[i] = i % 0x80;
	}

	if (msync(mmaped, BLOCK_SIZE * cnt_of_blocks, MS_SYNC) == -1) {
		perror("msync");
		close(fd);
		return 1;
	}

	munmap(mmaped, BLOCK_SIZE * cnt_of_blocks);
	close(fd);

	return 0;
}

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

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

相关文章

华为eNSP :WLAN的配置

一、WLAN的知识点&#xff1a; VLAN配置&#xff1a; VLAN&#xff1a;可以想象成一个大房子&#xff08;网络&#xff09;里划分的不同房间&#xff08;VLAN&#xff09;。每个房间可以有自己的功能&#xff0c;比如一个用于睡觉&#xff08;管理&#xff09;&#xff0c;另一…

软件工程知识点总结(5):详细设计

面向对象详细设计举例&#xff1a;接口描述、算法描述、数据描述 类的详细描述&#xff0c;内含数据、 方法及方法的参数返回值 public class User { private String userId; private String userName; private String password; private int type; public User(String userId…

基于APISIX实现API网关案例分享

一、APISIX介绍 1、定义 Apache APISIX 是一个动态、实时、高性能的云原生 API 网关。它构建于 NGINX + ngx_lua 的技术基础之上,充分利用了 LuaJIT 所提供的强大性能。 2、软件架构 2.1、架构图 APISIX 主要分为两个部分: APISIX 核心:包括 Lua 插件、多语言插件运行时…

ICM20948 DMP代码详解(12)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;11&#xff09; 上一回开始解析icm20948_sensor_setup函数的第2段代码也即inv_icm20948_init_matrix函数&#xff1a; /* Setup accel and gyro mounting matrix and associated angle for current board */inv_icm20…

前端技术(七)——less 教程

一、less简介 1. less是什么&#xff1f; less是一种动态样式语言&#xff0c;属于css预处理器的范畴&#xff0c;它扩展了CSS语言&#xff0c;增加了变量、Mixin、函数等特性&#xff0c;使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 &#xff0c;也可以借助Node.js在服…

关于武汉芯景科技有限公司的IIC缓冲器芯片XJ4307开发指南(兼容LTC4307)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.总线超时&#xff0c;自动断开连接 当 SDAOUT 或 SCLOUT 为低电平时&#xff0c;将启动内部定时器。定时器仅在相应输入变为高电平时重置。如果在 30ms &#xff08;典型值&#xff09; 内没有变为高…

社交媒体的未来:Facebook如何通过AI技术引领潮流

在数字化时代的浪潮中&#xff0c;社交媒体平台不断演变&#xff0c;以适应用户需求和技术发展的变化。作为全球领先的社交媒体平台&#xff0c;Facebook在这一进程中扮演了重要角色。尤其是人工智能&#xff08;AI&#xff09;技术的应用&#xff0c;正在深刻地改变Facebook的…

Docker零基础入门

参考课程https://www.bilibili.com/video/BV1VC4y177re/?vd_source=b15169a302bee35f484245aecc69d4dd 参考书籍Docker 实践 - 面向 AI 开发人员的 Docker 实践 (dockerpractice.readthedocs.io) 1. 什么是Docker 1.1. Docker起源 随着计算机的发展,计算机上已经可以运行多…

Stable Diffusion绘画 | ControlNet应用-Inpaint(局部重绘):更完美的重绘

Inpaint(局部重绘) 相当于小号的AI版PS&#xff0c;不但可以进行局部画面的修改&#xff0c;还可以去除背景中多余的内容&#xff0c;或者是四周画面内容的扩充。 预处理器说明 Inpaint_Global_Harmonious&#xff1a;重绘-全局融合算法&#xff0c;会对整个图片的画面和色调…

【无标题】SAM(Segment Anything Model)

1.SAM是什么&#xff1f; SAM是基于NLP的一个基础模型&#xff0c;专注于提示分割任务&#xff0c;使用提升工程来适应不同的下游分割任务。 2.SAM有什么用&#xff1f; 1&#xff09;SAM 可以通过简单地单击或交互选择要包含或排除在对象中的点来分割对象。还可以通过使用多边…

成都爱尔综合眼病科李晓峰主任解析空调续命,干眼别忍!

高温酷暑&#xff0c;命都是空调给的。 凉风一直吹&#xff0c;根本不敢停。 热到大汗淋漓&#xff0c;身体缺水&#xff0c;眼睛也是。 屋外闷热湿度不低&#xff0c;屋内空调一开湿度“骤降”不够用。 房间被“除湿”&#xff0c;眼睛也不例外。 长时间吹空调&#xff0c…

基于C++实现(控制台)模拟网上购书订单管理系统

模拟网上购书订单管理系统&#xff08;大一小学期C大作业&#xff09; 一、任务 1. 基础任务 建立继承了Buyer类的三个子类作为顾客的三种类型&#xff0c;用于管理顾客对象&#xff1b;建立Book类&#xff0c;管理书本对象&#xff1b;根据不同类型的顾客&#xff0c;计算出…

全球主要指数年度收益率汇总

1 美国 1.1 道琼斯工业平均指数 DJIA 1.2 纳斯达克综合指数 IXIC 1.3 纳斯达克100指数 NDX 1.4 标准普尔500 INX 2 中国 2.1 国债指数 000021 2.2 上证综指 000001 2.3 深证成指 399001 2.4 创业板 399006 2.5 中小100 399005 2.6 上证50 000016 3 香港

智能可视耳勺怎么用?智能可视耳勺使用方法!

随着科技的进步&#xff0c;有很多人摒弃了传统挖耳勺&#xff0c;选择更加高效直观的智能可视耳勺&#xff0c;这是因为智能可视耳勺能更加直观地看到耳朵的内部&#xff0c;让掏耳过程清晰明了&#xff0c;精准掏出耳垢。 但市场有的智能可视耳勺鱼龙混杂&#xff0c;很多人在…

【解决】vue 弹窗后面页面可以滚动问题

做web端项目过程中&#xff0c;发现点击弹窗后&#xff0c;弹窗后面的页面还可以滚动。 复现如下&#xff1a; 【方法1】 step1&#xff1a;在弹框页面使用 mousewheel.prevent <divv-show"workShowMenu"mousewheel.prevent>// TO DO...弹框内容 </div&…

C盘清理 拯救你的C盘!C盘从此不再爆满~!

C盘清理&#xff0c;拯救你的C盘&#xff01;C盘从此不再爆满~&#xff01;C盘爆满是许多人经常遇到的问题&#xff0c;它可能导致系统运行缓慢甚至崩溃&#xff0c;对于这种情况&#xff0c;我们要从根源触发&#xff0c;彻底的清理干净C盘垃圾。 一般的C盘清理有下面几种方法…

AI跟踪报道第55期-新加坡内哥谈技术-本周AI新闻: GPT NEXT (x100倍)即将在2024推出

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

[概率论] 随机变量的分布函数 (一)

文章目录 1.随机变量的分布函数2.离散型随机变量的分布函数3.连续性随机变量的分布函数 1.随机变量的分布函数 设X XX是一个随机变量&#xff0c;x xx是任意实数&#xff0c;则函数 几何表示 性质&#xff08;一个函数是分布函数的充要条件&#xff09; 2.离散型随机变量的分布…

区块链--代币之外的应用

数字货币是区块链技术的首次应用&#xff0c;但这可以说并没有真正发挥其潜力。比特币的发明首次引入了区块链的概念&#xff0c;但是直到 2013 年&#xff0c;区块链技术的真正潜力才得以展现&#xff0c;并在除加密货币之外的许多不同行业中得到应用。从那时起&#xff0c;人…

《机器学习》—— SVD奇异值分解方法对图像进行压缩

文章目录 一、SVD奇异值分解简单介绍二、代码实现—SVD奇异值分解方法对图像进行压缩 一、SVD奇异值分解简单介绍 SVD&#xff08;奇异值分解&#xff09;是一种在信号处理、统计学、线性代数、机器学习等多个领域广泛应用的矩阵分解方法。它将任何 mn 矩阵 A 分解为三个特定矩…