使用请求队列实验

2023-11-16

关于块设备架构就讲解这些,接下来我们使用开发板上的 RAM 模拟一段块设备,也就是ramdisk,然后编写块设备驱动。
首先是传统的使用请求队列的时候,也就是针对机械硬盘的时候如何编写驱动。先分段分析一下驱动代码。

1.重要的数据结构及宏定义

#define RAMDISK_SIZE (2 * 1024 * 1024) //容量大小为2MB
#define RAMDISK_NAME "ramdisk"         //名字
#define RAMDISK_MINOR 3                //表示有3个磁盘,不是次设备号

//ramdisk设备结构体
struct ramdisk_dev{
	int major; 		//主设备号
	unsigned char *ramdiskbuf; //ramdisk内存空间,用于模拟块设备
	spinlock_t lock; 	//自旋锁
	struct gendisk *gendisk; //gendisk(linux 内核使用gendisk来描述一个磁盘设备)
	struct request_queue *queue; //请求队列
}

struct ramdisk_dev ramdisk;   //ramdisk设备

2.驱动模块的加载与卸载

static int __init ramdisk_init(void)
{
	int ret = 0;
	printk("ramdisk init\r\n");
	
	//1.申请用于ramdisk内存
	//大小RAMDISK_SZIE,GFP_KERNEL用在可以睡眠的场合(使用GFP_KERNEL会引起休眠)!
	ramdisk.ramdiskbuf = kzalloc(RAMDISK_SZIE,GFP_KERNEL);
	if(NULL == ramdisk.ramdiskbuf)
	{
		ret = -EINVAL;
		goto ram_fail;
	}
	//2.初始化自旋锁
	spin_lock_init(&ramdisk.lock);
	//3.注册块设备
	//由系统自动分配主设备号,
	ramdisk.major = register_blkdev(0,RAMDISK_NAME);
	if(ramdisk.major < 0)
	{
		goto register_blkdev_fail;
	}
	printk("ramdisk major = %d\r\n",ramdisk.major);
	
	//4.分配并初始化gendisk
	ramdisk.gendisk = alloc_disk(RAMDISK_MINOR);
	if(!ramdisk.gendisk)
	{
		ret = -EINVAL;
		goto gendisk_alloc_fail;
	}
	//5.分配并初始化请求队列
	ramdisk.queue = blk_init_queue(ramdisk_request_fn,&ramdisk.lock);
	if(!ramdisk.queue)
	{
		ret = EINVAL;
		goto blk_init_fail;
	}
	//6.添加(注册)disk
	ramdisk.gendisk->major = ramdisk.major; //主设备号
    ramdisk.gendisk->first_minor = 0;       //第一个次设备号(起始次设备号)	
	ramdisk.gendisk->ops = &ramdisk_fops;	//操作函数
	ramdisk.gendisk->private_data = &ramdisk; //私有数据,指向该设备
	ramdisk.gendisk->queue = ramdisk.queue;	//请求队列
	sprintf(ramdisk.gendisk->disk_name,RAMDISK_NAME); //设备名字
	set_capacity(ramdisk.gendisk,RAMDISK_SIZE/512); //设备容量(单位为扇区)
	//分配一个gendisk(磁盘设备),gendisk初始化完成后就可以使用add_disk函数将gendisk添加到内核中,也就是向内核添加一个磁盘设备。
	add_disk(ramdisk.gendisk);
	
	return 0;
blk_init_fail:
	put_disk(ramdisk.gendisk);
gendisk_alloc_fail:
	unregister_blkdev(ramdisk.major,RAMDISK_NAME);
register_blkdev_fail:
	kfree(ramdisk.ramdiskbuf); //释放内存
ram_fail:
	return ret;
}
//驱动出口函数
static void __exit ramdisk_exit(void)
{
	//释放gendisk
	printk("ramdisk exit \r\n");
	del_gendisk(ramdisk.gendisk);
	put_disk(ramdisk.gendisk);
	
	//清除请求队列
	blk_cleanup_queue(ramdisk.queue);
	
	//注销块设备
	unregister_blkdev(ramdisk.major);
	
	//释放内存
	kfree(ramdisk.ramdiskbuf);
}

(1)kzalloc 函数详解
(2)注册函数

int register_blkdev(unsigned int major,const char *name);

major:主设备号
name:块设备名字
如果参数major在1~255之间额话表示自己定义主设备号,那么返回0,表示注册成功,返回负值表示注册失败;
如果参数major为0表示由系统自动分配主设备号,那么返回值就是系统分配的主设备号(1~255),如果返回负值那么就表示注册失败。
(3)初始化请求队列
我们首先需要申请一个request_queue,然后在初始化gendisk的时候将这个request_queue地址赋值给gendisk的queue成员变量,使用blk_init_queue函数来完成request_queue的申请与初始化,函数原型:

request_queue *blk_init_queue(request_fn_proc *rfn,spinlock_t *lock)

rfn:请求处理函数指针,每个request_queue都要有一个请求处理函数,请求处理函数request_fn_proc原型如下:

void (request_fn_proc)(struct request_queue *q)

请求处理函数需要驱动编写人员自行实现,具体的块设备读写操作就在此函数中完成。
lock:自选锁指针,需要驱动编写人员定义一个自旋锁,然后传递进来,请求队列会使用这个自旋锁。
返回值:如果为NULL的话表示失败,成功的话就返回申请到的request_queue的地址。
(4)设置块设备容量大小
使用set_capacity函数设置本块设备容量大小,注意这里的大小是扇区数,不是字节数,一个扇区是521字节。

块设备操作函数

//打开块设备
int ramdisk_open(struct block_device *dev, fmode_t mode)
{
	printk("ramdisk open\r\n");
	return 0;
}
//释放块设备
void ramdisk_release(struct gendisk *disk, fmode_t mode)
{
	printk("ramdisk release\r\n");
}
//获取磁盘信息
int ramdisk_getgeo(struct block_device *dev,struct hd_geometry *geo)
{
	//相对于机械硬盘的概念
	geo->head = 2; //磁头
	geo->cylinders = 32; //柱面
	geo->sector = RAMDISK_SIZE/(2 * 32 *512); //磁道上的扇区数量
	return 0}

static struct block_device_operations ramdisk_fops = 
{
	.owner = THIS_MODULES,
	.open   = ramdisk_open,
	.release = ramdisk_release,
	.getgeo = ramdisk_getgeo, 
};

(1)硬盘基本知识(磁头、磁道、扇区、柱面)

磁盘信息保存在geo结构体中,此结构体为hd_geometry类型

struct hd_geometry
{
	unsigned char heads; //磁头
	unsigned char sector; //一个磁道上的扇区数量
	unsigned short cylinders; //柱面
	unsigned long start;  
}

存储容量 = 磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数

请求处理函数

//处理传输过程
static void ramdisk_transfer(struct request *req)
{
	//blk_rq_pos获取到的是扇区地址(操作的块设备的扇区地址),左移9位转换为字节地址
	unsigned long start = blk_rq_pos(req) << 9;
	//获取请求要操作的数据长度
	unsigned long len = blk_rq_cur_bytes(req); //大小
	//bio中的数据缓冲区
	//读:从磁盘读取到的数据存放到buffer中
	//写:buffer保存着要写入磁盘的数据

	void *buffer = bio_data(req->bio); //bio_data()获取请求中的bio保存的数据
 	//rq_data_dir(函数判断当前是读还是写
 	if(rq_data_dir(req) == READ) //读数据 
	{
		memcpy(buffer,ramdisk.ramdiskbuf + start, len)
	}
	else if (rq_data_dir(req) == WRITE) //写数据
	{
		memcpy(ramdisk.ramdiskbuf + start,buf,len)
	}
}
//请求处理函数
void ramdisk_request_fn(struct request_queue *q)
{
	int err = 0;
	struct request *req;
	//循环处理请求队列中的每个请求
	req = blk_fetch_request(q); //获取请求队列中的第一个请求
	while(req != NULL)
	{
		//针对请求做具体的传输处理
		ramdisk_transfer(req);
		//判断是否为最后一个请求,如果不是的话,就获取下一个请求
		//循环处理完请求队列中的所有请求。
		if(!__blk_end_request_cur(req,err))
		{
			req = blk_fetch_request(q);
		}
	}
}

请求处理函数的重要内容就是完成从块设备中读取数据,或者向块设备中写入数据。ramdisk_request_fn函数就是请求处理函数,此函数只有一个参数q,为request_queue结构体指针类型,也就是请求队列,因此ramdisk_request_fn函数的主要工作就是依次处理请求队列中的所有请求。
blk_fetch_request函数获取的就是队列中的第一个请求,如果请求不为空的话,就调用ramdisk_transfer函数进行对请求做进一步的处理,然后就是while循环依次处理完请求队列中的每个请求。
__blk_end_request_cur函数检查是否为最后一个请求,如果不是的话就继续获取下一个,直至整个请求队列处理完成。
几个概念的理解:
(1)请求处理函数:就是处理请求的函数;
(2)请求队列:装有请求的队列;
(3)请求:请求就是使用调度算法对多个bio进行了排序,以便提高读写性能;
(4)bio:装有要处理的数据。

完整代码及其测试

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define RAMDISK_SIZE	(2 * 1024 * 1024) 	/* 容量大小为2MB */
#define RAMDISK_NAME	"ramdisk"			/* 名字 */
#define RADMISK_MINOR	3					/* 表示有三个磁盘分区!不是次设备号为3! */

/* ramdisk设备结构体 */
struct ramdisk_dev{
	int major;					/* 主设备号 */
	unsigned char *ramdiskbuf;	/* ramdisk内存空间,用于模拟块设备 */
	spinlock_t lock;			/* 自旋锁 */
	struct gendisk *gendisk; 	/* gendisk */
	struct request_queue *queue;/* 请求队列 */

};

struct ramdisk_dev ramdisk;		/* ramdisk设备 */

/*
 * @description		: 打开块设备
 * @param - dev 	: 块设备
 * @param - mode 	: 打开模式
 * @return 			: 0 成功;其他 失败
 */
int ramdisk_open(struct block_device *dev, fmode_t mode)
{
	printk("ramdisk open\r\n");
	return 0;
}

/*
 * @description		: 释放块设备
 * @param - disk 	: gendisk
 * @param - mode 	: 模式
 * @return 			: 0 成功;其他 失败
 */
void ramdisk_release(struct gendisk *disk, fmode_t mode)
{
	printk("ramdisk release\r\n");
}

/*
 * @description		: 获取磁盘信息
 * @param - dev 	: 块设备
 * @param - geo 	: 模式
 * @return 			: 0 成功;其他 失败
 */
int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)
{
	/* 这是相对于机械硬盘的概念 */
	geo->heads = 2;			/* 磁头 */
	geo->cylinders = 32;	/* 柱面 */
	geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */
	return 0;
}

/* 
 * 块设备操作函数 
 */
static struct block_device_operations ramdisk_fops =
{
	.owner	 = THIS_MODULE,
	.open	 = ramdisk_open,
	.release = ramdisk_release,
	.getgeo  = ramdisk_getgeo,
};

/*
 * @description	: 处理传输过程
 * @param-req 	: 请求
 * @return 		: 无
 */
static void ramdisk_transfer(struct request *req)
{	
	unsigned long start = blk_rq_pos(req) << 9;  	/* blk_rq_pos获取到的是扇区地址,左移9位转换为字节地址 */
	unsigned long len  = blk_rq_cur_bytes(req);		/* 大小   */

	/* bio中的数据缓冲区
	 * 读:从磁盘读取到的数据存放到buffer中
	 * 写:buffer保存这要写入磁盘的数据
	 */
	void *buffer = bio_data(req->bio);		
	
	if(rq_data_dir(req) == READ) 		/* 读数据 */	
		memcpy(buffer, ramdisk.ramdiskbuf + start, len);
	else if(rq_data_dir(req) == WRITE) 	/* 写数据 */
		memcpy(ramdisk.ramdiskbuf + start, buffer, len);
}

/*
 * @description	: 请求处理函数
 * @param-q 	: 请求队列
 * @return 		: 无
 */
void ramdisk_request_fn(struct request_queue *q)
{
	int err = 0;
	struct request *req;

	/* 循环处理请求队列中的每个请求 */
	req = blk_fetch_request(q);
	while(req != NULL) {

		/* 针对请求做具体的传输处理 */
		ramdisk_transfer(req);

		/* 判断是否为最后一个请求,如果不是的话就获取下一个请求
		 * 循环处理完请求队列中的所有请求。
		 */
		if (!__blk_end_request_cur(req, err))
			req = blk_fetch_request(q);	
	}
}


/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ramdisk_init(void)
{
	int ret = 0;
	printk("ramdisk init\r\n");

	/* 1、申请用于ramdisk内存 */
	ramdisk.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL);
	if(ramdisk.ramdiskbuf == NULL) {
		ret = -EINVAL;
		goto ram_fail;
	}

	/* 2、初始化自旋锁 */
	spin_lock_init(&ramdisk.lock);

	/* 3、注册块设备 */
	ramdisk.major = register_blkdev(0, RAMDISK_NAME); /* 由系统自动分配主设备号 */
	if(ramdisk.major < 0) {
		goto register_blkdev_fail;
	}  
	printk("ramdisk major = %d\r\n", ramdisk.major);

	/* 4、分配并初始化gendisk */
	ramdisk.gendisk = alloc_disk(RADMISK_MINOR);
	if(!ramdisk.gendisk) {
		ret = -EINVAL;
		goto gendisk_alloc_fail;
	}

	/* 5、分配并初始化请求队列 */
	ramdisk.queue = blk_init_queue(ramdisk_request_fn, &ramdisk.lock);
	if(!ramdisk.queue) {
		ret = EINVAL;
		goto blk_init_fail;
	}

	/* 6、添加(注册)disk */
	ramdisk.gendisk->major = ramdisk.major;		/* 主设备号 */
	ramdisk.gendisk->first_minor = 0;			/* 第一个次设备号(起始次设备号) */
	ramdisk.gendisk->fops = &ramdisk_fops; 		/* 操作函数 */
	ramdisk.gendisk->private_data = &ramdisk;	/* 私有数据 */
	ramdisk.gendisk->queue = ramdisk.queue;		/* 请求队列 */
	sprintf(ramdisk.gendisk->disk_name, RAMDISK_NAME); /* 名字 */
	set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512);	/* 设备容量(单位为扇区) */
	add_disk(ramdisk.gendisk);

	return 0;

blk_init_fail:
	put_disk(ramdisk.gendisk);
	//del_gendisk(ramdisk.gendisk);
gendisk_alloc_fail:
	unregister_blkdev(ramdisk.major, RAMDISK_NAME);
register_blkdev_fail:
	kfree(ramdisk.ramdiskbuf); /* 释放内存 */
ram_fail:
	return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ramdisk_exit(void)
{
	printk("ramdisk exit\r\n");
	/* 释放gendisk */
	del_gendisk(ramdisk.gendisk);
	put_disk(ramdisk.gendisk);

	/* 清除请求队列 */
	blk_cleanup_queue(ramdisk.queue);

	/* 注销块设备 */
	unregister_blkdev(ramdisk.major, RAMDISK_NAME);

	/* 释放内存 */
	kfree(ramdisk.ramdiskbuf); 
}

module_init(ramdisk_init);
module_exit(ramdisk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

测试结果
1.查看我们创建的ramdisk分区
可以看出来,ramdisk已经识别出来了,大小为2MB,但是同时也提示/dev/ramdisk没有分区表,因为我们还没有格式化/dev/ramdisk
在这里插入图片描述
2.格式化/dev/ramdisk
使用mkfs.vfat命令格式化/dev/ramdisk,将其格式化成vfat,输入如下命令mkfs.vfat /dev/ramdisk
3.格式化完成后就可以挂载/dev/ramdisk来访问了,挂载点可以定义。mount /dev/ramdisk /tmp
挂载成功以后就可以通过/tmp来访问了ramdisk这个磁盘了,进入/tmp目录中,可以通过vi命令新建一个txt文件来测试磁盘访问是否正常。
4.检查
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用请求队列实验 的相关文章

  • 无法从 jenkins 作为后台进程运行 nohup 命令

    更新 根据下面的讨论 我编辑了我的答案以获得更准确的描述 我正在尝试从詹金斯运行 nohup 命令 完整的命令是 nohup java jar home jar server process 0 35 jar prod gt gt var
  • 使用 sed 更新 xml 属性(Windows + cygwin 和 Linux)?

    我需要使用 sed 命令对 xml 文件进行更新 但我在这方面遇到了麻烦 它需要在 Windows 使用 cygwin 和 Linux 上运行 XML 具有以下元素
  • 所有平台上的java

    如果您想用 java 为 Windows Mac 和 Linux 编写桌面应用程序 那么所有这些代码都相同吗 您只需更改 GUI 即可使 Windows 应用程序更像 Windows 等等 如果不深入细节 它是如何工作的 Java 的卖点之
  • nslookup 报告“无法解析 '(null)': 名称无法解析”,尽管它成功解析了 DNS 名称

    我在 ubuntu 上 并且正在运行 docker 默认桥接网络 我有 Zookeeper kafka 的容器化版本 以及我编写的与 kafka 对话的应用程序 I do a docker exec it
  • 尝试安装 LESS 时出现“请尝试以 root/管理员身份再次运行此命令”错误

    我正在尝试在我的计算机上安装 LESS 并且已经安装了节点 但是 当我输入 node install g less 时 出现以下错误 并且不知道该怎么办 FPaulMAC bin paul npm install g less npm ER
  • PHP 从命令行启动 gui 程序,但 apache 不启动

    首先 我阅读了有类似问题的人的一些帖子 但所有答案都没有超出导出 DISPLAY 0 0 和 xauth cookies 这是我的问题 提前感谢您的宝贵时间 我开发了一个小库 它使用 OpenGL 和 GLSL 渲染货架 过去几天我将它包装
  • 如何将目录及其子目录中的所有 PDF 文件复制到一个位置?

    如何全部复制PDF文件从目录及其子目录到单个目录 实际上还有更多的文件 并且深度有些任意 假设四个目录的最大深度是公平的 我想这些文件需要重命名 如果a pdf例如 位于多个目录中 因为我会adding https ebooks stack
  • 在 Linux 上使用多处理时,TKinter 窗口不会出现

    我想生成另一个进程来异步显示错误消息 同时应用程序的其余部分继续 我正在使用multiprocessingPython 2 6 中的模块来创建进程 我试图用以下命令显示窗口TKinter 这段代码在Windows上运行良好 但在Linux上
  • 使用 sh 运行 bash 脚本

    我有 bash 脚本 它需要 bash 另一个人尝试运行它 sh script name sh 它失败了 因为 sh 是他的发行版中 dash 的符号链接 ls la bin sh lrwxrwxrwx 1 root root 4 Aug
  • ubuntu:升级软件(cmake)-版本消歧(本地编译)[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我的机器上安装了 cmake 2 8 0 来自 ubuntu 软件包 二进制文件放置在 usr bin cmake 中 我需要将 cmake 版本至少
  • 将 jar 作为 Linux 服务运行 - init.d 脚本在启动应用程序时卡住

    我目前正在致力于在 Linux VM 上实现一个可运行的 jar 作为后台服务 我已经使用了找到的例子here https gist github com shirish4you 5089019作为工作的基础 并将 start 方法修改为
  • 多处理:仅使用物理核心?

    我有一个函数foo它消耗大量内存 我想并行运行多个实例 假设我有一个有 4 个物理核心的 CPU 每个核心有两个逻辑核心 我的系统有足够的内存来容纳 4 个实例foo并行但不是 8 个 此外 由于这 8 个核心中的 4 个是逻辑核心 我也不
  • 删除 Git 存储库,但保留所有文件

    在我使用 Linux 的过程中的某个时刻 我决定将我的主目录中的所有内容都放入源代码管理中是个好主意 我不是在问这是否是一个好主意 我是在问如何撤销它 删除存储库的原因是我最近安装了 Oh My Zsh 而且我非常喜欢它 问题是我的主目录有
  • 如何减去两个 gettimeofday 实例?

    我想减去两个 gettimeofday 实例 并以毫秒为单位给出答案 这个想法是 static struct timeval tv gettimeofday tv NULL static struct timeval tv2 gettime
  • Linux - 从第二个选项卡获取文本

    假设我们有这样的文件 一些文本11 一些文本12 一些文本13 一些文本21 一些文本22 一些文本23 文本由制表符分隔 我们知道第 1 列中的一些文本 但希望从第 2 列中获取文本 我知道我可以通过以下方式获取线路 grep somet
  • 在两次之间每分钟执行一次 Cronjob

    我需要在 crontab 中每分钟运行一个 bash 脚本8 45am and 9 50am每天的 Code 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 8 home pull sh gt ho
  • 我如何知道 C 程序的可执行文件是在前台还是后台运行?

    在我的 C 程序中 我想知道我的可执行文件是否像这样在前台运行 a out 或者像这样 a out 如果你是前台工作 getpgrp tcgetpgrp STDOUT FILENO or STDIN FILENO or STDERR FIL
  • 快速像素绘图库

    我的应用程序以每像素的方式生成 动画 因此我需要有效地绘制它们 我尝试过不同的策略 库 但结果并不令人满意 尤其是在更高分辨率的情况下 这是我尝试过的 SDL 好的 但是慢 OpenGL 像素操作效率低下 xlib 更好 但仍然太慢 svg
  • 如何在c linux中收听特定接口上的广播?

    我目前可以通过执行以下操作来收听我编写的简单广播服务器 仅广播 hello int fd socket PF INET SOCK DGRAM 0 struct sockaddr in addr memset addr 0 sizeof ad
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost

随机推荐

  • 修改Fedora 25与Windows 10的默认启动顺序

    首先贴出Fedora25下 boot grub2 grub cfg的内容 DO NOT EDIT THIS FILE It is automatically generated by grub2 mkconfig using templat
  • 看完这篇 教你玩转渗透测试靶机vulnhub——DC6

    Vulnhub靶机DC6渗透测试详解 Vulnhub靶机介绍 Vulnhub靶机下载 Vulnhub靶机安装 Vulnhub靶机漏洞详解 信息收集 暴力破解 漏洞发现 漏洞利用 nmap脚本提权 获取flag Vulnhub靶机渗透总结 V
  • Unity显示被遮挡的模型

    具体显示为这个效果 同事在网上找了一个受光的材质 Shader Custom RoleShader Properties Color Color Color 1 1 1 1 MainTex Albedo RGB 2D white Gloss
  • 一文彻底搞懂 MYSQL分库分表方案

    MYSQL分库分表方案 垂直分表 把一部分表字段放入一张表 另一部分放入其他的表 按照表字段的使用频次分门别类的划分 例如 在商品列表查询时 列表中只是展示部分字段 同时这个列表查询比详情信息查询更加高频 并不需要把所有字段都展示 我们可以
  • 可信执行环境(TEE):深入探讨安全计算的未来

    摘要 本文将详细介绍可信执行环境 TEE 的概念 原理和功能 我们将讨论TEE的应用场景 以及如何使用TEE来保护敏感数据和代码的安全 此外 我们还将探讨TEE的挑战和未来发展 1 引言 随着计算设备的普及和云计算技术的快速发展 如何保护数
  • python b 'string'

    str literals a sequence of Unicode characters UTF 16 or UTF 32 depending on how Python was compiled bytes b literals a s
  • 软件测试之自动化测试

    目录 1 什么是自动化测试 2 selenium java环境搭建 3 熟悉selenium的API 定位元素 添加等待 打印信息 浏览器的相关操作 键盘组合键用法 鼠标事件 特殊场景 定位一组元素 多层框架定位 下拉框处理 弹窗处理 上传
  • MATLAB算法实战应用案例精讲-【深度学习】归一化

    目录 归一化基础知识点 1 什么是归一化 2 为什么要归一化
  • Linux中部署软件时提示空间不足的应急方案

    1 df h 查看剩余空间 2 我们看到 home 下面的空间有很多 注 如果 home 下的控件时充足 且可以分配出去多余的空间 可以继续往下看 如自己 不够用或不够往外分配 可以外接新硬盘 3 在根号下执行 cp r home home
  • 【深度学习标注数据处理】imgaug Augment Polygons 对标注图片和 polygon 的数据增强

    对于本地化进行图像的增强 大家都是非常好操作的 但是 对于标注信息一起增强 还是稍微有一些难度的 麻烦很多 我是遇到一个数据集非常少的任务 只有40张图 就直接标记了去训练 发现几乎不拟合 当然这里使用的是yolo v8 而不是UNet 于
  • Gson的使用

    1 添加Gson 库 右键app open module settings dependncies com goolel code gson gson 2 2 4 2 对象转Json 保存至文件 使用Misc中的方法 Misc gson s
  • 3.2 图像分类

    文章目录 LeNet 小图像 LeNet在手写数字识别上的应用 LeNet在眼疾识别数据集iChallenge PM上的应用 数据集准备 查看数据集图片 定义数据读取器 启动训练 AlexNet 大图像 VGG 深度 GoogLeNet 深
  • 基于变分模态分解和麻雀算法优化长短期记忆网络的多维时间序列预测,VMD-SSA-LSTM多维时间序列预测。MATLAB代码(含LSTM、VMD-LSTM、VMD-SSA-LSTM三个模型的对比)

    clc clear all close all VMD SSA LSTM多维时间序列预测 tic load data mat load vmd data mat load LSTM mat disp disp VMD SSA LSTM预测
  • sqli-labs/Less-18

    这一关和前面的所有关卡都不一样 我们试一试先成功登录进去看看 结果除了iD地址之外还有一个信息回显了 那就是user agent所以我们抓包试一试 抓包后再user agent注入试试看 我尝试了许多注入方法 发现大部分方法都不能看出我注入
  • jdk、jre环境变量配置

    1 jdk和jre的区别 jdk Java 开发工具包 jre Java 的运行环境 只需这么记就可以了 想深入了解得自行查询相关资料 2 jdk是包含jre的 所以只需下载jdk 官方网址 https www oracle com cn
  • QT样式翻译

    Qt4 7文档翻译 Qt样式单参考 Qt Style Sheets Reference 转载于 http 2845385 blog 51cto com 2835385 1080560 0 tsina 1 14777 397232819ff9
  • 深入理解warp shuffle

    warp shuffle 相关函数学习 shfl up sync 0xffffffff lane val i 是CUDA函数之一 用于在线程束内的线程之间交换数据 其中 0xffffffff是掩码参数 指示线程束内所有线程都参与数据交换 一
  • C++线程处理函数的返回值

    引言 关于线程处理函数 常见的可能是返回值为void类型 那线程处理函数是否能定义自己想要的返回值类型呢 这里请看下面的说明 C 线程返回值 应用环境 1 传统的方式获取线程返回值 2 使用C Promise和future方式 3 prom
  • 服务器怎么和网站接入,网站服务器的带宽怎么接入呢?

    zzzooo 当下许多企业为了将事务上云 开端租借效劳器建立网站 可是因为各个运营商机房种类都不相同 带宽线路也有区别 许多用户不清楚网站效劳器的带宽要怎样接入 今天小编就和我们聊一聊当下商场几种比较抢手的几种带宽接入方式 一 BGP多线带
  • 使用请求队列实验

    关于块设备架构就讲解这些 接下来我们使用开发板上的 RAM 模拟一段块设备 也就是ramdisk 然后编写块设备驱动 首先是传统的使用请求队列的时候 也就是针对机械硬盘的时候如何编写驱动 先分段分析一下驱动代码 1 重要的数据结构及宏定义