当前位置: 首页 > news >正文

block层:8. deadline调度器

deadline

源码基于5.10

0. 私有数据

struct deadline_data {/** run time data*//** requests (deadline_rq s) are present on both sort_list and fifo_list*/struct rb_root sort_list[2];struct list_head fifo_list[2];/** next in sort order. read, write or both are NULL*/struct request *next_rq[2];unsigned int batching;		/* number of sequential requests made */unsigned int starved;		/* times reads have starved writes *//** settings that change how the i/o scheduler behaves*/int fifo_expire[2];int fifo_batch;int writes_starved;int front_merges;spinlock_t lock;spinlock_t zone_lock;struct list_head dispatch;
};

1. 函数表

static struct elevator_type mq_deadline = {.ops = {.insert_requests	= dd_insert_requests,.dispatch_request	= dd_dispatch_request,.prepare_request	= dd_prepare_request,.finish_request		= dd_finish_request,.next_request		= elv_rb_latter_request,.former_request		= elv_rb_former_request,.bio_merge		= dd_bio_merge,.request_merge		= dd_request_merge,.requests_merged	= dd_merged_requests,.request_merged		= dd_request_merged,.has_work		= dd_has_work,.init_sched		= dd_init_queue,.exit_sched		= dd_exit_queue,},#ifdef CONFIG_BLK_DEBUG_FS.queue_debugfs_attrs = deadline_queue_debugfs_attrs,
#endif.elevator_attrs = deadline_attrs,.elevator_name = "mq-deadline",.elevator_alias = "deadline",// 支持zone设备顺序写.elevator_features = ELEVATOR_F_ZBD_SEQ_WRITE,.elevator_owner = THIS_MODULE,
};

1.1. 属性

#define DD_ATTR(name) \__ATTR(name, 0644, deadline_##name##_show, deadline_##name##_store)static struct elv_fs_entry deadline_attrs[] = {DD_ATTR(read_expire), // 提交读取之前最长时间,默认HZ/2(半秒)DD_ATTR(write_expire), // 写入的最大起时时间,默认5*HZ(5秒)DD_ATTR(writes_starved), // 最大的读取次数会导致写饥饿,默认是2次DD_ATTR(front_merges), // 是否开启前向合并,0/1,默认开DD_ATTR(fifo_batch), // 顺序请求批量最大值,默认16__ATTR_NULL
};

2. 初始化及退出

2.1. 初始化

static int dd_init_queue(struct request_queue *q, struct elevator_type *e)
{struct deadline_data *dd;struct elevator_queue *eq;// 分配一个queue对象eq = elevator_alloc(q, e);if (!eq)return -ENOMEM;// 分配deadline对象dd = kzalloc_node(sizeof(*dd), GFP_KERNEL, q->node);if (!dd) {kobject_put(&eq->kobj);return -ENOMEM;}// 与eq建立关联eq->elevator_data = dd;// 各种初始化INIT_LIST_HEAD(&dd->fifo_list[READ]);INIT_LIST_HEAD(&dd->fifo_list[WRITE]);dd->sort_list[READ] = RB_ROOT;dd->sort_list[WRITE] = RB_ROOT;dd->fifo_expire[READ] = read_expire;dd->fifo_expire[WRITE] = write_expire;// writes_starved,默认为0dd->writes_starved = writes_starved;// 默认打开前向合并dd->front_merges = 1;// fifo里最大的请求数,默认16dd->fifo_batch = fifo_batch;spin_lock_init(&dd->lock);spin_lock_init(&dd->zone_lock);INIT_LIST_HEAD(&dd->dispatch);// 设置到请求队列里q->elevator = eq;return 0;
}struct elevator_queue *elevator_alloc(struct request_queue *q,struct elevator_type *e)
{struct elevator_queue *eq;// 分配一个对象eq = kzalloc_node(sizeof(*eq), GFP_KERNEL, q->node);if (unlikely(!eq))return NULL;// 关联到调度器的函数表eq->type = e;// 一些基本初始化kobject_init(&eq->kobj, &elv_ktype);mutex_init(&eq->sysfs_lock);hash_init(eq->hash);return eq;
}

2.2. 退出

static void dd_exit_queue(struct elevator_queue *e)
{struct deadline_data *dd = e->elevator_data;// 这2个列表必须为空BUG_ON(!list_empty(&dd->fifo_list[READ]));BUG_ON(!list_empty(&dd->fifo_list[WRITE]));// 直接释放ddkfree(dd);
}

3. 合并

static bool dd_bio_merge(struct request_queue *q, struct bio *bio,unsigned int nr_segs)
{struct deadline_data *dd = q->elevator->elevator_data;struct request *free = NULL;bool ret;spin_lock(&dd->lock);// 尝试合并, free 会带回被合并的请求ret = blk_mq_sched_try_merge(q, bio, nr_segs, &free);spin_unlock(&dd->lock);// 释放被合并的请求if (free)blk_mq_free_request(free);return ret;
}static int dd_request_merge(struct request_queue *q, struct request **rq,struct bio *bio)
{struct deadline_data *dd = q->elevator->elevator_data;// 结束的扇区sector_t sector = bio_end_sector(bio);struct request *__rq;// 不允许前向合并if (!dd->front_merges)return ELEVATOR_NO_MERGE;// 根据数据方向,找到起点为bio结束扇区的rq__rq = elv_rb_find(&dd->sort_list[bio_data_dir(bio)], sector);if (__rq) {// 找到一个rqBUG_ON(sector != blk_rq_pos(__rq));// 是否可以合并if (elv_bio_merge_ok(__rq, bio)) {*rq = __rq;// 判断丢弃合并if (blk_discard_mergable(__rq))return ELEVATOR_DISCARD_MERGE;// 返回前向合并return ELEVATOR_FRONT_MERGE;}}return ELEVATOR_NO_MERGE;
}static void dd_merged_requests(struct request_queue *q, struct request *req,struct request *next)
{if (!list_empty(&req->queuelist) && !list_empty(&next->queuelist)) {// next的过期时间比req短if (time_before((unsigned long)next->fifo_time,(unsigned long)req->fifo_time)) {// 把req移到next后面list_move(&req->queuelist, &next->queuelist);// 并设置成next的到期时间req->fifo_time = next->fifo_time;}}// 删除next请求deadline_remove_request(q, next);
}static void deadline_remove_request(struct request_queue *q, struct request *rq)
{struct deadline_data *dd = q->elevator->elevator_data;// 删除请求从sort列表里list_del_init(&rq->queuelist);// 如果在红黑树上,则删除它if (!RB_EMPTY_NODE(&rq->rb_node))deadline_del_rq_rb(dd, rq);// 从哈希表里删除elv_rqhash_del(q, rq);// 置空最后合并的请求,如果它等于rqif (q->last_merge == rq)q->last_merge = NULL;
}

4. 插入请求

static void dd_insert_requests(struct blk_mq_hw_ctx *hctx,struct list_head *list, bool at_head)
{struct request_queue *q = hctx->queue;struct deadline_data *dd = q->elevator->elevator_data;spin_lock(&dd->lock);// list里放的是待插入的请求while (!list_empty(list)) {struct request *rq;rq = list_first_entry(list, struct request, queuelist);// 先从list里删除list_del_init(&rq->queuelist);// 插入请求dd_insert_request(hctx, rq, at_head);// 增加调度器插入的数量atomic_inc(&hctx->elevator_queued);}spin_unlock(&dd->lock);
}static void dd_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,bool at_head)
{struct request_queue *q = hctx->queue;struct deadline_data *dd = q->elevator->elevator_data;const int data_dir = rq_data_dir(rq);// zone设备解锁blk_req_zone_write_unlock(rq);// 先尝试合并,如果可以合并则直接返回if (blk_mq_sched_try_insert_merge(q, rq))return;// 这里面只打印了insert的trace日志blk_mq_sched_request_inserted(rq);// 插入队前 || 是直通请求if (at_head || blk_rq_is_passthrough(rq)) {// 插在派发队列的前面或后面if (at_head)list_add(&rq->queuelist, &dd->dispatch);elselist_add_tail(&rq->queuelist, &dd->dispatch);} else {// 普通插入// 把请求加入对应读写方向的红黑树,这个红黑树按照扇区起点排序deadline_add_rq_rb(dd, rq);// 如果该请求是可以合并的// 能走到这里表示在上面合并时没有合并if (rq_mergeable(rq)) {// 则把它加到哈希表里,key是扇区请求数量elv_rqhash_add(q, rq);// 如果last_merge没值,则把rq设为它if (!q->last_merge)q->last_merge = rq;}// 设置过期时间rq->fifo_time = jiffies + dd->fifo_expire[data_dir];// 加到fifo_list里list_add_tail(&rq->queuelist, &dd->fifo_list[data_dir]);}
}static void
deadline_add_rq_rb(struct deadline_data *dd, struct request *rq)
{// 根据请求方向,获取根节点struct rb_root *root = deadline_rb_root(dd, rq);// 把请求加到红黑树里elv_rb_add(root, rq);
}static inline struct rb_root *
deadline_rb_root(struct deadline_data *dd, struct request *rq)
{// 根据请求方向,获取根节点return &dd->sort_list[rq_data_dir(rq)];
}void elv_rb_add(struct rb_root *root, struct request *rq)
{struct rb_node **p = &root->rb_node;struct rb_node *parent = NULL;struct request *__rq;// 按照请求扇区的起点,找到需要插入的位置while (*p) {parent = *p;__rq = rb_entry(parent, struct request, rb_node);if (blk_rq_pos(rq) < blk_rq_pos(__rq))p = &(*p)->rb_left;else if (blk_rq_pos(rq) >= blk_rq_pos(__rq))p = &(*p)->rb_right;}// 插入结点rb_link_node(&rq->rb_node, parent, p);// 插入颜色?rb_insert_color(&rq->rb_node, root);
}

5. 派发

static bool dd_has_work(struct blk_mq_hw_ctx *hctx)
{struct deadline_data *dd = hctx->queue->elevator->elevator_data;// 没有入队的直接返回if (!atomic_read(&hctx->elevator_queued))return false;// 这3个队列有1个不空就表示有任务return !list_empty_careful(&dd->dispatch) ||!list_empty_careful(&dd->fifo_list[0]) ||!list_empty_careful(&dd->fifo_list[1]);
}static struct request *dd_dispatch_request(struct blk_mq_hw_ctx *hctx)
{struct deadline_data *dd = hctx->queue->elevator->elevator_data;struct request *rq;spin_lock(&dd->lock);// 取出一个待派发的请求rq = __dd_dispatch_request(dd);spin_unlock(&dd->lock);// 如果取出一个,则减少调度器的计数if (rq)atomic_dec(&rq->mq_hctx->elevator_queued);return rq;
}static struct request *__dd_dispatch_request(struct deadline_data *dd)
{struct request *rq, *next_rq;bool reads, writes;int data_dir;// 派发列表不为空,则直接取出一个请求if (!list_empty(&dd->dispatch)) {rq = list_first_entry(&dd->dispatch, struct request, queuelist);list_del_init(&rq->queuelist);goto done;}// 读写里是否有请求reads = !list_empty(&dd->fifo_list[READ]);writes = !list_empty(&dd->fifo_list[WRITE]);// 获取下一个写请求rq = deadline_next_request(dd, WRITE);// 如果没获取到,则获取下一个读请求if (!rq)rq = deadline_next_request(dd, READ);// 有请求 && 发出的连续请求数量 < fifo的最大批量, 则派发if (rq && dd->batching < dd->fifo_batch)/* we have a next request are still entitled to batch */goto dispatch_request;/** 走到这儿表示rq为空或者batching已经到达限制*/// 有读的请求if (reads) {// 怎么会为空?BUG_ON(RB_EMPTY_ROOT(&dd->sort_list[READ]));// 如果有写请求 && 写饥饿超过了限制值if (deadline_fifo_request(dd, WRITE) &&(dd->starved++ >= dd->writes_starved))// 派发写请求goto dispatch_writes;// 走到这儿表示写不饥饿// 数据方向为读data_dir = READ;// 找一个请求派发goto dispatch_find_request;}// 走到这儿表示没有读请求,或者写饥饿if (writes) {
dispatch_writes:BUG_ON(RB_EMPTY_ROOT(&dd->sort_list[WRITE]));// 重置饥饿值dd->starved = 0;// 方向为写data_dir = WRITE;// 找一个请求派发goto dispatch_find_request;}// 走到这儿表示即没有写,与没有读return NULL;dispatch_find_request:// 根据操作方向找一个请求next_rq = deadline_next_request(dd, data_dir);// 在data_dir上有请求过期 || 没有下一个请求if (deadline_check_fifo(dd, data_dir) || !next_rq) {// 重新取一个rqrq = deadline_fifo_request(dd, data_dir);} else {// 没有过期的请求,继续派发next_rqrq = next_rq;}/** 对于zoned设备来说,如果我们只有写请求入队,它们不能被派发,rq将是NULL*/if (!rq)return NULL;// batch重置dd->batching = 0;dispatch_request:dd->batching++;// 从各种列表里删除rq请求, 并设置next请求deadline_move_request(dd, rq);
done:// 如果是zone设备需要加锁blk_req_zone_write_lock(rq);// 标记请求已开始rq->rq_flags |= RQF_STARTED;return rq;
}static struct request *
deadline_next_request(struct deadline_data *dd, int data_dir)
{struct request *rq;unsigned long flags;// 只处理读写if (WARN_ON_ONCE(data_dir != READ && data_dir != WRITE))return NULL;// 获取下一个请求rq = dd->next_rq[data_dir];// 下一请求为空if (!rq)return NULL;// 请求是读 || 是写但是不是zone设备if (data_dir == READ || !blk_queue_is_zoned(rq->q))return rq;// 处理zone设备.todo: zone设备相关看面再看spin_lock_irqsave(&dd->zone_lock, flags);while (rq) {if (blk_req_can_dispatch_to_zone(rq))break;rq = deadline_latter_request(rq);}spin_unlock_irqrestore(&dd->zone_lock, flags);return rq;
}static struct request *
deadline_fifo_request(struct deadline_data *dd, int data_dir)
{struct request *rq;unsigned long flags;// 只处理读或写if (WARN_ON_ONCE(data_dir != READ && data_dir != WRITE))return NULL;// 对应的列表为空if (list_empty(&dd->fifo_list[data_dir]))return NULL;// 取出第1个元素rq = rq_entry_fifo(dd->fifo_list[data_dir].next);// 方向是读 || 方向是写但是不是zoned请求if (data_dir == READ || !blk_queue_is_zoned(rq->q))return rq;// 走到这儿表示方向是写的zone设备. todo: zone设备后面再看spin_lock_irqsave(&dd->zone_lock, flags);list_for_each_entry(rq, &dd->fifo_list[WRITE], queuelist) {if (blk_req_can_dispatch_to_zone(rq))goto out;}rq = NULL;
out:spin_unlock_irqrestore(&dd->zone_lock, flags);return rq;
}static inline int deadline_check_fifo(struct deadline_data *dd, int ddir)
{// 获取下一个请求struct request *rq = rq_entry_fifo(dd->fifo_list[ddir].next);// 如果rq达到了过期时间返回1if (time_after_eq(jiffies, (unsigned long)rq->fifo_time))return 1;return 0;
}static void
deadline_move_request(struct deadline_data *dd, struct request *rq)
{const int data_dir = rq_data_dir(rq);// 先把读写全置空dd->next_rq[READ] = NULL;dd->next_rq[WRITE] = NULL;// 在红黑树上获取rq的下一个节点,根据扇区号dd->next_rq[data_dir] = deadline_latter_request(rq);// 从排序列表和fifo列表,哈希表里删除此请求deadline_remove_request(rq->q, rq);
}static inline struct request *
deadline_latter_request(struct request *rq)
{// 获取下一个结点struct rb_node *node = rb_next(&rq->rb_node);if (node)return rb_entry_rq(node);return NULL;
}

假设在初始状态下,所有列表全空, writes_starved=2, fifo_batch=3:

  1. 插入请求顺序如下:r1 r2 w1 w2 w3 r3 w4 r4 w5 w6 w7 r5 r6
  2. w1派发, next_rq[write]=w2,batching=1
  3. w2派发, next_rq[write]=w3,batching=2
  4. w3派发, next_rq[write]=w4,batching=3
  5. 触发fifo_batch限制, next_rq[read]是NULL,派发r1, next_rq[read]=r2,next_rq[write]=NULL,batching=1
  6. r2派发, next_rq[read]=r3,batching=2
  7. r3派发, next_rq[read]=r4,batching=3
  8. 触发writes_starved限制,派发写请求w4,next_rq[read]=NULL,next_rq[write]=w5

相关文章:

block层:8. deadline调度器

deadline 源码基于5.10 0. 私有数据 struct deadline_data {/** run time data*//** requests (deadline_rq s) are present on both sort_list and fifo_list*/struct rb_root sort_list[2];struct list_head fifo_list[2];/** next in sort order. read, write or both ar…...

DTO,VO,PO的意义与他们之间的转换

DTO&#xff08;Data Transfer Object&#xff09;&#xff1a;数据传输对象&#xff0c;这个概念来源于J2EE的设计模式&#xff0c;原来的目的是为了EJB的分布式应用提供粗粒度的数据实体&#xff0c;以减少分布式调用的次数&#xff0c;从而提高分布式调用的性能和降低网络负…...

Java 集合框架2

一、关于set接口的常用类 1.HashSet类 用来处理无序的单列数据&#xff0c;没有重复的元素,重复的元素算一个 i.构造方法 //HashSet类是set接口的子类&#xff0c;是无序的单列数据&#xff0c;没有重复的元素&#xff0c;重复的元素算一个 //HashSet的构造方法 //HashSet() …...

2024王道408数据结构P144 T16

2024王道408数据结构P144 T16 思考过程 首先看题目&#xff0c;要求我们把二叉树的叶子结点求出来并且用链表的方式存储&#xff0c;链接时用叶结点的右指针来存放单链表指针。我们很清楚可以看出来能用中序遍历递归的方式实现&#xff0c;因为第一个叶子结点在整棵树的最左下…...

【ARM Coresight 系列文章 22 -- linux frace 与 trace-cmd】

文章目录 ftrace 介绍trace-cmd 介绍trace-cmd 常用跟踪事件ftrace 与 trace-cmd 关系ftrace 编译依赖 ftrace 介绍 ftrace 是 Linux 内核中的一个跟踪工具&#xff0c;主要用于帮助开发者分析和调试内核的行为。ftrace 的名字来源于 “function tracer”&#xff0c;它最初是…...

MyBatis的一级缓存和二级缓存是怎么样的?

目录 1. 一级缓存 2. 一级缓存失效的几种情况 3. 二级缓存 4.二级缓存失效的情况 5. 二级缓存的相关配置 6. 缓存的查询顺序 MyBatis 的缓存共分为一级缓存和二级缓存。 1. 一级缓存 一级缓存是 SqlSession 级别的&#xff0c;通过同一个 SqlSession 查询到的数据会被缓…...

下载的文件被Windows 11 安全中心自动删除

今天从CSDN上下载了自己曾经上传的文件&#xff0c;但是浏览器下载完之后文件被Windows安全中心自动删除&#xff0c;说是带病毒。实际是没有病毒的&#xff0c;再说了即便有病毒也不应该直接删除啊&#xff0c;至少给用户一个保留或删除的选项。 研究了一番&#xff0c;可以暂…...

【Java List与数组】List<T>数组和数组List<T>的区别(124)

List数组&#xff1a;存储List的数组&#xff0c;即&#xff1a;数组中的元素是&#xff1a;List&#xff1b; 数组List&#xff1a;存储数组的List&#xff0c;即&#xff1a;List中的数据是类型的数组&#xff1b; 测试案例&#xff1a; import java.util.ArrayList; impor…...

Nuxt 菜鸟入门学习笔记四:静态资源

文章目录 public 目录assets 目录全局样式导入 Nuxt 官网地址&#xff1a; https://nuxt.com/ Nuxt 使用以下两个目录来处理 CSS、fonts 和图片等静态资源&#xff1a; public 目录 public 目录用作静态资产的公共服务器&#xff0c;可通过应用程序定义的 URL 公开获取。 换…...

C语言 - 结构体、结构体数组、结构体指针和结构体嵌套

结构体的意义 问题&#xff1a;学籍管理需要每个学生的下列数据&#xff1a;学号、姓名、性别、年龄、分数&#xff0c;请用 C 语言程序存储并处理一组学生的学籍。 单个学生学籍的数据结构&#xff1a; 学号&#xff08;num&#xff09;&#xff1a; int 型姓名&#xff08;…...

python安装playwright问题记录

python安装playwright这个时候,有得时候会https timeout 有的时候会 not found。 我最后使用的方法三&#xff0c;挺好用的。 PyPI The Python Package Index 可以尝试使用的方法 1. 更换pip源&#xff1a;使用国内的pip源可以提高下载速度并减少超时问题。例如&#xff0c…...

关于gRPC微服务利弊之谈

gRPC微服务架构包括以下几个主要组件&#xff1a; 服务定义&#xff1a;定义服务的接口和消息格式&#xff0c;使用Protocol Buffers或其他的消息格式进行描述。服务实现&#xff1a;实现定义的服务接口和消息处理逻辑。服务器端实现&#xff1a;在服务器端&#xff0c;需要实…...

【Terraform学习】使用 Terraform创建Lambda函数启动EC2(Terraform-AWS最佳实战学习)

本站以分享各种运维经验和运维所需要的技能为主 《python》&#xff1a;python零基础入门学习 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8》暂未更新 《docker学习》暂未更新 《ceph学习》ceph日常问题解…...

Mac软件删除方法?如何删除不会有残留

Mac电脑如果有太多无用的应用程序&#xff0c;很有可能会拖垮Mac系统的运行速度。因此&#xff0c;卸载电脑中无用的软件是优化Mac系统运行速度的最佳方式之一。Mac卸载应用程序的方式是和Windows有很大的区别&#xff0c;特别对于Mac新用户来说&#xff0c;如何无残留的卸载删…...

编程之道:【性能优化】提高软件效率的实际建议和避免常见陷阱

在今天的数字化世界中&#xff0c;软件性能是应用程序成功的关键之一。无论是网页加载速度、移动应用的响应时间还是后端服务器的处理速度&#xff0c;性能都直接影响着用户满意度。在追求高性能时&#xff0c;开发人员需要采取一系列实际建议&#xff0c;同时避免常见的陷阱。…...

VGG的结构:视觉几何组(Visual Geometry Group)

目录 1. VGG 的结构 2. VGG 的网络细节 3. VGG 的代码实现 1. VGG 的结构 牛津大学的视觉几何组&#xff08;Visual Geometry Group&#xff09;设计了 VGGNet(也称为 VGG)&#xff0c;一种经典的卷积神经网络 (CNN) 架构。在 2014 年 ILSVRC 分类任务中&#xff0c;VGG 取…...

VBA:按照Excel工作表中的名称列自动汇总多个工作薄中对应sheet中所需要的数据

需求如下&#xff1a; B列为产品名为合并单元格&#xff0c;C列为供应商名&#xff0c;G、H列为金额数据&#xff1b;数据源放在同一个文件夹内&#xff0c;B列产品名来源于工作薄名称中间的字符串&#xff0c;C列供应商名来源于工作薄中的sheet名&#xff1b;G、H列金额数据来…...

Mybatis1.9 批量删除

1.9 批量删除 1.9.1 编写接口方法1.9.2 编写SQL语句1.9.3 编写测试方法 如上图所示&#xff0c;用户可以选择多条数据&#xff0c;然后点击上面的 删除 按钮&#xff0c;就会删除数据库中对应的多行数据。 1.9.1 编写接口方法 在 BrandMapper 接口中定义删除多行数据的方法。…...

CUDA小白 - NPP(2) -图像处理-算数和逻辑操作(2)

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…...

python+redis实现布隆过滤器(含redis5.0版本以上和5.0以下版本的两份代码)

布隆过滤器是一种空间效率极高的概率数据结构&#xff0c;用于测试一个元素是否是集合的成员。如果布隆过滤器返回 False&#xff0c;则元素绝对不在集合中。如果返回 True&#xff0c;则元素可能在集合中&#xff0c;但也可能是一个误报。布隆过滤器利用了多个不同的哈希函数对…...

SpringBoot Thymeleaf iText7 生成 PDF(2023/08/29)

SpringBoot Thymeleaf iText7 生成 PDF&#xff08;2023/08/29&#xff09; 文章目录 SpringBoot Thymeleaf iText7 生成 PDF&#xff08;2023/08/29&#xff09;1. 前言2. 技术思路3. 实现过程4. 测试 1. 前言 近期在项目种遇到了实时生成复杂 PDF 的需求&#xff0c;经过一番…...

【核磁共振成像】并行采集MRI

目录 一、并行成像二、SENSE重建三、SMASH重建四、灵敏度校准五、AUTO-SMASH和VD-AUTO-SMASH六、GRAPPA重建七、SPACE RIP重建算法八、PILS重建算法九、PRUNO重建算法十、UNFOLD算法 一、并行成像 并行MR成像(pMRI):相位阵列接受线圈不但各有自己专用的接受通道&#xff0c;而且…...

深度图相关评测网站

文章目录 1 单目/Stereo相关测评网站介绍12 单目/Stereo相关测评网站介绍23 单目/Stereo相关测评网站介绍3 1 单目/Stereo相关测评网站介绍1 https://vision.middlebury.edu/stereo/eval3/ 2 单目/Stereo相关测评网站介绍2 http://www.cvlibs.net/datasets/kitti/eval_stereo…...

本地部署 CodeLlama 并在 VSCode 中使用 CodeLlama

本地部署 CodeLlama 并在 VSCode 中使用 CodeLlama 1. CodeLlama 是什么2. CodeLlama Github 地址3. 下载 CodeLlama 模型4. 部署 CodeLlama5. 在 VSCode 中使用 CodeLlama6. 使用WSGI启动服务7. 创建 start.sh 启动脚本 1. CodeLlama 是什么 Code Llama 是一个基于 Llama 2 的…...

Agilent33220A任意波形发生器

20MHz正弦波和方波脉冲、斜披、三角波&#xff0c;噪声和直流波形14-bit,50MSa/s&#xff0c;64K点任意波形AM、FM、PM、FSK和PWM凋制线性和对数扫描及脉冲串模式10mVpp至10Vpp幅苗范围图形化界面可以对信号设置进行可视化验证通过USB、GPIB和LAN连接 性能优异的各种函数的波形…...

springboot第37集:kafka,mqtt,Netty,nginx,CentOS,Webpack

image.png binzookeeper-server-start.shconfigzookeeper.properties.png image.png image.png 消费 image.png image.png image.png image.png image.png image.png image.png image.png image.png Netty的优点有很多&#xff1a; API使用简单&#xff0c;学习成本低。功能强大…...

NVIDIA DLI 深度学习基础 答案 领取证书

最后一节作业是水果分类的任务&#xff0c;一共6类&#xff0c;使用之前学习的知识在代码段上进行填空。 加载ImageNet预训练的基础模型 from tensorflow import kerasbase_model keras.applications.VGG16(weights"imagenet",input_shape(224, 224, 3),include_t…...

axios模拟表单提交

axios默认是application/json方式提交&#xff0c;controller接收的时候必须以RequestBody的方式接收&#xff0c;有时候不太方便。如果axios以application/x-www-form-urlencoded方式提交数据&#xff0c;controller接收的时候只要保证名字应对类型正确即可。 前端代码&#…...

智安网络|探索物联网架构:构建连接物体与数字世界的桥梁

物联网是指通过互联网将各种物理设备与传感器连接在一起&#xff0c;实现相互通信和数据交换的网络系统。物联网架构是实现这一连接的基础和框架&#xff0c;它允许物体与数字世界之间的互动和协作。 一、物联网架构的概述 物联网架构是一种分层结构&#xff0c;它将物联网系…...

胡歌深夜发文:我对不起好多人

胡歌的微博又上了热搜。 8月29日01:18分&#xff0c;胡歌微博发文称&#xff1a;“我尽量保持冷静&#xff0c;我对不起好多人&#xff0c;我希望对得起这短暂的一生”&#xff0c;并配了一张自己胡子拉碴的图&#xff0c;右眼的伤疤清晰可见。 不少网友留言称“哥你又喝多了吗…...

C++二级题

数字放大 #include<iostream> #include<string.h> #include<stdio.h> #include<iomanip> #include<cmath> #include<bits/stdc.h> int a[2000][2000]; int b[2000]; char c[2000]; long long n; using namespace std; int main() {cin>…...

NetApp AFF A900:适用于数据中心的超级产品

NetApp AFF A900&#xff1a;适用于数据中心的超级产品 AFF A 系列中的 AFF A900 高端 NVMe 闪存存储功能强大、安全可靠、具有故障恢复能力&#xff0c;提供您为任务关键型企业级应用程序提供动力并保持数据始终可用且安全所需的一切。 产品功能与特性 AFF A900&#xff1a…...

入海排污口水质自动监测系统,助力把好入河入海“闸门”

随着经济社会的不断发展&#xff0c;污水的排放强度不断加大&#xff0c;大量的污水排入河流、湖泊和海洋中&#xff0c;造成了水体污染&#xff0c;严重影响着我国的用水安全、公众健康、经济发展与社会稳定。入河入海排污口是污染物进入河流和海洋的最后关口&#xff0c;也是…...

AUTOSAR知识点 之 ECUM (一):基础知识梳理(概念部分)

目录 1、概述 2、ECUM的工作状态 2.1、Startup状态 2.2、UP状态 2.3、RUN状态 2.4、SLEEP状态...

leetcode分类刷题:哈希表(Hash Table)(二、数组交集问题)

1、当需要快速判断某元素是否出现在序列中时&#xff0c;就要用到哈希表了。 2、本文针对的总结题型为给定两个及多个数组&#xff0c;求解它们的交集。接下来&#xff0c;按照由浅入深层层递进的顺序总结以下几道题目。 3、以下题目需要共同注意的是&#xff1a;对于两个数组&…...

[Mac软件]Adobe After Effects 2023 v23.5 中文苹果电脑版(支持M1)

After Effects是动画图形和视觉效果的行业标准。由运动设计师、平面设计师和视频编辑用于创建复杂的动画图形和视觉上吸引人的视频。 创建动画图形 使用预设样式为文本和图形添加动画效果&#xff0c;或逐帧调整它们。编辑、添加深度、制作动画或转换为可编辑的路径&#xff…...

范德波尔方程详细介绍与Python实现(附说明)

引言: 在研究真空管放大器的过程中,写下了一个振动微分方程。当时人们并没有混沌或是对初始条件敏感的概念。不过,当混沌理论有一定发展后,人们重新回顾这个方程时发现它其实是个混沌方程。当时,范德波尔在 Nature 杂志报告了基于这个微分方程的霓虹灯实验,发现当驱动信号…...

常用的GPT插件

0.简介 随着chatgpt爆火&#xff0c;这玩意并不对国内用户开放&#xff0c;如果想要使用的话还要需要进行翻墙以及国外手机号才能进行注册。 对于国内来说有很多国内免费的方法&#xff0c;这里就整理一下&#xff0c;方便大家开发 1. 网站类型 下面的网站无需注册即可免费…...

智慧校园用电安全解决方案

随着科技的不断发展&#xff0c;智慧校园建设逐渐成为了教育行业的一大趋势。在这个过程中&#xff0c;电力系统作为校园基础设施的重要组成部分&#xff0c;其安全、稳定、高效的运行显得尤为重要。下面小编来为大家介绍下智慧校园用电安全解决方案吧! 一、智慧校园电力系统现…...

【教程】DGL中的子图分区函数partition_graph讲解

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 函数形式 函数作用 函数内容 函数入参 函数返参 使用示例 实际上官方的函数解释中就已经非常详细了。 函数形式 def partition_graph(g, graph_name, num_parts, out_path, num_hops1, part…...

关于layui table回显以及选择下一页时记住上一页数据的问题

代码如下 <div class"layui-form-item"><label class"layui-form-label">选择商品</label><div class"layui-input-inline"><input type"text" name"keyword" id"keyword" placehold…...

kafka消息系统实战

kafka是什么&#xff1f; 是一种高吞吐量的、分布式、发布、订阅、消息系统 1.导入maven坐标 <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>2.4.1</version></dependency&…...

Kafka3.0.0版本——Leader故障处理细节原理

目录 一、服务器信息二、服务器基本信息及相关概念2.1、服务器基本信息2.2、LEO的概念2.3、HW的概念 三、Leader故障处理细节 一、服务器信息 三台服务器 原始服务器名称原始服务器ip节点centos7虚拟机1192.168.136.27broker0centos7虚拟机2192.168.136.28broker1centos7虚拟机…...

BI系统框架模型

一 技术架构 二 数据源 主数据 &#xff1a;组织|岗位|人员|大区|三大主数据&#xff08;客户、物料、供应商&#xff09;财务主数据&#xff08;科目|成本中心|利润中心|资产&#xff09;|工作中心|工艺路线 业务数据 &#xff1a;线索|业务机会|合同|订单|采购|生产|发…...

双向交错CCM图腾柱无桥单相PFC学习仿真与实现(3)硬件功能实现

前言 前面介绍了双向交错CCM图腾柱的系统设计仿真实现&#xff0c;仿真很理想 双向交错CCM图腾柱无桥单相PFC学习仿真与实现&#xff08;1&#xff09;系统问题分解_卡洛斯伊的博客-CSDN博客 然后又介绍了SOG锁相环仿真实现的原理 双向交错CCM图腾柱无桥单相PFC学习仿真与实…...

微软用 18 万行 Rust 重写了 Windows 内核

微软正在使用 Rust 编程语言重写其核心 Windows 库。 5 月 11 日——Azure 首席技术官 Mark Russinovich 表示&#xff0c;最新的 Windows 11 Insider Preview 版本是第一个包含内存安全编程语言 Rust 的版本。 “如果你参加了 Win11 Insider 环&#xff0c;你将在 Windows 内…...

word 调整列表缩进

word 调整列表缩进的一种方法&#xff0c;在试了其他方法无效后&#xff0c;按下图所示顺序处理&#xff0c;编号和文字之间的空白就没那么大了。 即右键word上方样式->点击修改格式->定义新编号格式->字体->取消勾选 “……对齐到网格”->确定...

nginx学习

一、nginx常用版本 Nginx开源版&#xff1a; http://nginx.org/ nginx plus商业版本&#xff08;好像功能支持更多&#xff09; https://www.nginx.com/ openresty &#xff08;免费&#xff0c;用的也是这个&#xff09; https://openresty.org/cn/ Tengine https://tengine.…...

python+TensorFlow实现人脸识别智能小程序的项目(包含TensorFlow版本与Pytorch版本)(一)

pythonTensorFlow实现人脸识别智能小程序的项目&#xff08;包含TensorFlow版本与Pytorch版本&#xff09;&#xff08;一&#xff09; 一&#xff1a;TensorFlow基础知识内容部分&#xff08;简明扼要&#xff0c;快速适应&#xff09;1、下载Cifar10数据集&#xff0c;并进行…...

ChatGPT怎么用于政府和公共服务?

将ChatGPT用于政府和公共服务领域是一种创新的应用方式&#xff0c;可以改善政府与公众之间的互动&#xff0c;提升公共服务的效率和质量。ChatGPT作为一个自然语言处理模型&#xff0c;可以在政府信息传递、公共参与、服务支持等方面发挥积极作用。以下将详细探讨ChatGPT如何用…...