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

Linux之 SPI 驱动框架- spi-mem 框架

一、框架变更的历程

1.1 旧框架图

1.2 新框架图

那么问题来了, 为什么要开发新的 SPI 存储器接口?

有了这个新的框架, SPI NOR 和SPI NAND 都可以基于相同的SPI控制器驱动进行支持了。m25p80 驱动将被修改成,使用spi-mem 接口,取代具有局限性的spi_flash_read() 接口。目前, 我们仍然有专用的SPI NOR控制器,但是最终是移除他们,并将他们移植为 drivers/spi/ 下 的普通SPI控制器驱动。

spi-mem framework

        linux 中spi-mem 的核心代码在 drivers/spi/spi-mem.c , 该框架提供给spi 存储控制器驱动的api 。

注:4.x版本内核后,spi-nand就已经使用spi-mem framework了。但是spi-nor直到5.x版本才使用spi-mem驱动。具体版本未作考证,所以选取了教新得到5.14.9版本内核来进行代码分析。

二、重要的数据结构

2.1 struct spi_mem
  spi-mem本质是一个spi总线从设备驱动,使用struct spi_mem来描述一个spi存储设备。

struct spi_mem {struct spi_device *spi;void *drvpriv;const char *name;
};

  • spi:底层的spi device,可以看出spi_mem是对spi_device的简单封装。
  • drvpriv:spi_mem_driver的私有数据
  • name:该spi-mem的名字
     

2.2 

struct spi_mem_op

  该结构体表示一次对spi存储器的操作。提供给上层存储器驱动使用。

struct spi_mem_op {struct {u8 nbytes;u8 buswidth;u8 dtr : 1;u16 opcode;} cmd;struct {u8 nbytes;u8 buswidth;u8 dtr : 1;u64 val;} addr;struct {u8 nbytes;u8 buswidth;u8 dtr : 1;} dummy;struct {u8 buswidth;u8 dtr : 1;enum spi_mem_data_dir dir;unsigned int nbytes;union {void *in;const void *out;} buf;} data;
};

通常spi存储器的操作,包括opcode(cmd)、addr、dummy、data。注意,buswidth代表single、dual、quad传输。

2.3 spi_controller_mem_ops

故名意思,提供给spi_controller注册使用的回调函数集。一个希望优化SPI存储器操作的spi控制器,都可以实现该回调函数集。

struct spi_controller_mem_ops {int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);bool (*supports_op)(struct spi_mem *mem,const struct spi_mem_op *op);int (*exec_op)(struct spi_mem *mem,const struct spi_mem_op *op);const char *(*get_name)(struct spi_mem *mem);int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,u64 offs, size_t len, void *buf);ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,u64 offs, size_t len, const void *buf);int (*poll_status)(struct spi_mem *mem,const struct spi_mem_op *op,u16 mask, u16 match,unsigned long initial_delay_us,unsigned long polling_rate_us,unsigned long timeout_ms);
};
  • adjust_op_size:调整存储器操作的数据传输大小,以符合对齐要求和最大FIFO大小的约束。用于校正单次spi存储器传输数据长度。如单次要求读取1024字节,但是控制器只支持单次512字节传输,那么在此回调中,就需要将spi_mem_op->data.nbytes限制到512字节。spi存储器的core层,会自动将分包后,后续数据的读取地址增加。如果回调中没实现,则使用spi-mem驱动框架中默认的校正接口。代码如下:
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{struct spi_controller *ctlr = mem->spi->controller;size_t len;if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)return ctlr->mem_ops->adjust_op_size(mem, op);if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;if (len > spi_max_transfer_size(mem->spi))return -EINVAL;op->data.nbytes = min3((size_t)op->data.nbytes,spi_max_transfer_size(mem->spi),spi_max_message_size(mem->spi) -len);if (!op->data.nbytes)return -EINVAL;}return 0;
}
  • supports_op:spi-nor、spi-nand通常支持多种模式,单线、四线、各个模式的cmd(opcode)各不相同,在驱动初始化的时候,需要通过support_op,确认控制器是否支持该命令。只有flash和控制器都能支持的传输模式,flash才能正常工作。通常情况下,不需要实现该函数,使用spi-mem默认的即可满足需求。代码如下:
bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
{if (spi_mem_check_op(op))return false;return spi_mem_internal_supports_op(mem, op);
}static bool spi_mem_internal_supports_op(struct spi_mem *mem,const struct spi_mem_op *op)
{struct spi_controller *ctlr = mem->spi->controller;if (ctlr->mem_ops && ctlr->mem_ops->supports_op)return ctlr->mem_ops->supports_op(mem, op);return spi_mem_default_supports_op(mem, op);
}
  • exec_op:执行存储器操作,即实现如何发送、接受一次flash的操作。不实现该回调函数,spi-mem会用默认的传输模式,即使用传统spi_message、spi_transfer方式,一次spi存储器操作,如果包含cmd、addr、dummy、data,那么单次传输需要四个spi_transfer,效率十分低下。所以,不管是支持quad模式的spi控制器、还是普通spi控制器,如果有外接spi存储器的需求,且使用spi-mem驱动框架,都建议在驱动中实现spi_controller_mem_ops回调。

Linux支持的默认的exec_op 的操作函数如下:

路径:drivers/spi/spi-dw-core.c  的函数 dw_spi_exec_mem_op()->dw_spi_write_then_read()

  • get_name: 自定义 struct spi_mem->name, 这个name 通常会传递给mtd->name, 可以通过这个来兼容不同spi 存储器的mtdparts, 不过必须要注意的是如果这个name 是动态分配的内存,则应该调用devm_xxx()相关的接口, 因为没有提供free_name 的接口。
  • dirmap_create: 创建一个直接映射的描述符,用来通过访问memory来访问存储器,当控制器能做到将spi 存储器映射到cpu 的地址空间时,可以实现这个。此接口由spi_mem_dirmap_create()函数调用。
  • dirmap_destroy:销毁dirmap_create所创建的描述符, 此接口会由spi_mem_dirmap_destroy 调用。
  • dirmap_read: 直接从memory 读取spi 存储器的数据, 由 spi_mem_dirmap_read调用。
  • dirmap_write: 直接往memory 写数据来写spi存储器的内容, 由spi_mem_dirmap_write调用。

注意:当spi_controller_mem_ops 没有实现时, core 层将通过创建多个SPI 传输组成的SPI消息,来添加对该特性的通用支持,就像以前通用SPI NOR 控制器驱动程序所做的那样。

        对于支持直接读写内存来读写flash 的控制器来说,需要对 struct spi_mem_dirmap_desc 这个的结果体进行操作:

  • mem: 该描述符所属的spi_mem 设备
  • info: 在创建描述符所需要的信息,下面会说明。
  • nordirmap: 如果spi controller 没有实现mem_ops->dirmap_create 回调函数,则设置为1;或者在调用mem_ops->dirmap_create是出错(超出映射的内存区域)时 设置为1;此值为1 时,所有跟spi_mem_dirmap_read/write()相关的函数,就会使用spi_mem_exec_op 函数来操作flash;
  • priv: 指向controller 的私有数据结构

2.4  struct spi_mem_driver

在spi存储器的设备驱动中,应该声明自己为struct spi_mem_driver:

struct spi_mem_driver {struct spi_driver spidrv;int (*probe)(struct spi_mem *mem);int (*remove)(struct spi_mem *mem);void (*shutdown)(struct spi_mem *mem);
};

该结构体集成自struct spi_driver ,spi存储器的设备驱动需要实现proberemove函数,他们传入的参数是一个spi_mem对象。

三、举 spi-nor core 的注册为例

源码路径: drivers/mtd/spi-nor/core.c

这是linux 中 通用的spi-nor 的驱动程序,我们简单看一下它是如何使用spi-mem 框架对spi 存储器进行操作的。

以spi_nor_write 为例

spi_nor_write

                -》spi_nor_write_data // 此函数返回实际写入的字节数,如果少于上一步请求写入的字节数,就会循环执行这步,直到所有请求的字节数写完。

                        -》spi_nor_spimem_write_data

                                ->if(nor->dirmap.wdesc) 执行spi_mem_dirmap_write 函数// 在spi_nor_driver 驱动中的spi_nor_probe函数中, 是有创建wdesc和rdesc的, 不过在创建过程中会根据实际情况给desc->nodirmap赋值

                                        -》如果desc->nodirmap 为真,则执行spi_mem_no_dirmap_write 函数

                                        -》否则就调用spi_controller->mem_ops->dirmap_write 回调函数来写flash

                                ->else 执行 spi_nor_spimem_exec_op 函数 // 如果其他的spi mem设备端驱动里没有创建描述符,则直接执行spi_nor_spimem_exec_op

当没有创建描述符时:

spi_nor_spimem_exec_op()-》spi_mem_exec_op()

可以看出,当spi_controller 中mem_ops 有实现时,就会调用mem_ops->exec_op 回调;否则就会通过创建由多个spi_transfer 组成的spi_message,调用spi_sync函数来支持通用spi 的传输。

四、spi mem控制器的编写步骤

通过deepseek 写的驱动框架:

#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/dma-mapping.h>/** 定义 DIRMAP 相关参数*/
#define DIRMAP_READ_BUF_SIZE    (4 * 1024)  // 读缓冲区大小
#define DIRMAP_WRITE_BUF_SIZE   (1 * 1024)  // 写缓冲区大小/** 控制器私有数据结构*/
struct my_spi_controller {struct spi_controller *ctlr;/* dirmap 相关资源 */void __iomem *read_buf;dma_addr_t read_buf_dma;void __iomem *write_buf;dma_addr_t write_buf_dma;bool use_dma;
};/** SPI 存储器控制器操作结构体实现(包含 dirmap)*/
static const struct spi_controller_mem_ops my_spi_mem_controller_ops = {.adjust_op_size = my_adjust_op_size,.supports_op = my_supports_op,.exec_op = my_exec_op,.get_name = my_get_name,.dirmap_create = my_dirmap_create,.dirmap_read = my_dirmap_read,.dirmap_write = my_dirmap_write,
};/** 创建 dirmap 映射*/
static int my_dirmap_create(struct spi_mem_dirmap_desc *desc)
{struct spi_controller *ctlr = desc->mem->spi->controller;struct my_spi_controller *my_ctlr = spi_controller_get_devdata(ctlr);/* 检查是否支持请求的映射类型 */if ((desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN && !my_ctlr->read_buf) ||(desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT && !my_ctlr->write_buf))return -ENOTSUPP;/* 检查操作是否支持 */if (!my_supports_op(desc->mem, &desc->info.op_tmpl))return -ENOTSUPP;return 0;
}/** dirmap 读取操作*/
static ssize_t my_dirmap_read(struct spi_mem_dirmap_desc *desc,u64 offs, size_t len, void *buf)
{struct spi_controller *ctlr = desc->mem->spi->controller;struct my_spi_controller *my_ctlr = spi_controller_get_devdata(ctlr);struct spi_mem_op op = desc->info.op_tmpl;size_t chunk_len, remaining = len;ssize_t ret = 0;u8 *buf_ptr = buf;/* 设置地址 */op.addr.val = desc->info.offset + offs;while (remaining) {chunk_len = min_t(size_t, remaining, DIRMAP_READ_BUF_SIZE);op.data.nbytes = chunk_len;if (my_ctlr->use_dma && my_ctlr->read_buf_dma) {/* DMA 模式读取 */op.data.buf.in = my_ctlr->read_buf;if (my_exec_op(desc->mem, &op) < 0) {ret = -EIO;break;}memcpy(buf_ptr, my_ctlr->read_buf, chunk_len);} else {/* 直接模式读取 */op.data.buf.in = buf_ptr;if (my_exec_op(desc->mem, &op) < 0) {ret = -EIO;break;}}buf_ptr += chunk_len;remaining -= chunk_len;ret += chunk_len;}return ret;
}/** dirmap 写入操作*/
static ssize_t my_dirmap_write(struct spi_mem_dirmap_desc *desc,u64 offs, size_t len, const void *buf)
{struct spi_controller *ctlr = desc->mem->spi->controller;struct my_spi_controller *my_ctlr = spi_controller_get_devdata(ctlr);struct spi_mem_op op = desc->info.op_tmpl;size_t chunk_len, remaining = len;ssize_t ret = 0;const u8 *buf_ptr = buf;/* 设置地址 */op.addr.val = desc->info.offset + offs;while (remaining) {chunk_len = min_t(size_t, remaining, DIRMAP_WRITE_BUF_SIZE);op.data.nbytes = chunk_len;if (my_ctlr->use_dma && my_ctlr->write_buf_dma) {/* DMA 模式写入 */memcpy(my_ctlr->write_buf, buf_ptr, chunk_len);op.data.buf.out = my_ctlr->write_buf;} else {/* 直接模式写入 */op.data.buf.out = buf_ptr;}if (my_exec_op(desc->mem, &op) < 0) {ret = -EIO;break;}buf_ptr += chunk_len;remaining -= chunk_len;ret += chunk_len;}return ret;
}/** SPI 控制器探测函数(增强版,含 dirmap 初始化)*/
static int my_spi_controller_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct spi_controller *ctlr;struct my_spi_controller *my_ctlr;int ret;/* 分配控制器结构 */ctlr = spi_alloc_controller(dev, sizeof(*my_ctlr));if (!ctlr)return -ENOMEM;my_ctlr = spi_controller_get_devdata(ctlr);my_ctlr->ctlr = ctlr;/* 初始化 dirmap 资源 */my_ctlr->use_dma = false;/* 分配 DMA 缓冲区用于读取 */my_ctlr->read_buf = dmam_alloc_coherent(dev, DIRMAP_READ_BUF_SIZE,&my_ctlr->read_buf_dma, GFP_KERNEL);if (!my_ctlr->read_buf)dev_warn(dev, "Failed to allocate DMA read buffer, falling back to PIO\n");/* 分配 DMA 缓冲区用于写入 */my_ctlr->write_buf = dmam_alloc_coherent(dev, DIRMAP_WRITE_BUF_SIZE,&my_ctlr->write_buf_dma, GFP_KERNEL);if (!my_ctlr->write_buf)dev_warn(dev, "Failed to allocate DMA write buffer, falling back to PIO\n");my_ctlr->use_dma = my_ctlr->read_buf && my_ctlr->write_buf;/* 初始化控制器参数 */ctlr->mem_ops = &my_spi_mem_controller_ops;ctlr->bus_num = -1; /* 动态分配总线号 */ctlr->num_chipselect = 1; /* 假设支持1个片选 */ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;ctlr->bits_per_word_mask = SPI_BPW_MASK(8);/* 设置传输限制 */ctlr->max_transfer_size = 4096; /* 最大传输大小 */ctlr->max_address_width = 3;    /* 支持24位地址 *//* 支持 dirmap */ctlr->mem_ops = &my_spi_mem_controller_ops;/* 注册SPI控制器 */ret = devm_spi_register_controller(dev, ctlr);if (ret) {dev_err(dev, "Failed to register SPI controller: %d\n", ret);spi_controller_put(ctlr);return ret;}return 0;
}/** SPI 控制器移除函数(增强版,含 dirmap 资源清理)*/
static int my_spi_controller_remove(struct platform_device *pdev)
{struct spi_controller *ctlr = platform_get_drvdata(pdev);struct my_spi_controller *my_ctlr = spi_controller_get_devdata(ctlr);/* DMA 缓冲区由 devm 管理,无需显式释放 */return 0;
}/* 其余函数(my_adjust_op_size, my_supports_op, my_exec_op, my_get_name)保持不变 *//** 平台驱动结构体*/
static struct platform_driver my_spi_controller_driver = {.probe = my_spi_controller_probe,.remove = my_spi_controller_remove,.driver = {.name = "my-spi-controller",.owner = THIS_MODULE,},
};module_platform_driver(my_spi_controller_driver);MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Enhanced SPI Memory Controller Driver with Dirmap Support");
MODULE_LICENSE("GPL");

在用户代码中,可以这样使用 dirmap 功能:

struct spi_mem_dirmap_desc *desc;
struct spi_mem_dirmap_info info = {.offset = 0,            // 映射的起始地址.length = SZ_1M,        // 映射的长度.op_tmpl = SPI_MEM_OP(  // 操作模板SPI_MEM_OP_CMD(0x03, 1),    // READ 命令SPI_MEM_OP_ADDR(3, 0, 1),   // 3字节地址SPI_MEM_OP_NO_DUMMY,SPI_MEM_OP_DATA_IN(0, NULL, 1) // 数据输入)
};// 创建直接映射
desc = spi_mem_dirmap_create(mem, &info);// 使用直接映射读取数据
spi_mem_dirmap_read(desc, 0, len, buf);// 销毁直接映射
spi_mem_dirmap_destroy(desc);

相关文章:

Linux之 SPI 驱动框架- spi-mem 框架

一、框架变更的历程 1.1 旧框架图 1.2 新框架图 那么问题来了&#xff0c; 为什么要开发新的 SPI 存储器接口&#xff1f; 有了这个新的框架&#xff0c; SPI NOR 和SPI NAND 都可以基于相同的SPI控制器驱动进行支持了。m25p80 驱动将被修改成&#xff0c;使用spi-mem 接口&a…...

振动分析 - 献个宝

1.一个自制的振动能量分析工具 这个分析工具似乎真的定位到了故障的具体位置。 1.1对一组实验室虚拟信号的分析结果: 1.2 对现场真实数据的分析结果 依照边频带的调制,和边频的缝隙宽度,基本定位到问题。 追加几份待看的文档: 齿轮结构的频谱特征 - 知乎使用 FFT 获得…...

从脑电图和大脑记录中学习稳健的深度视觉表征

从脑电图和大脑记录中学习稳健的深度视觉表征 印度&#xff0c;印度&#xff0c;印度&#xff0c;印度大脑实验室&#xff0c;印度 例如&#xff0c;达拉普&#xff0c;克普拉萨德&#xff0c;山&#xff0c;山&#xff0c;新的。ac .在 摘要 解码人类大脑一直是新机器人科学家…...

【论文阅读】——D^3-Human: Dynamic Disentangled Digital Human from Monocular Vi

文章目录 摘要1 引言2 相关工作3 方法3.1 HmSDF 表示3.2 区域聚合3.3. 变形场3.4. 遮挡感知可微分渲染3.5 训练3.5.1 训练策略3.5.2 重建损失3.5.3 正则化限制 4. 实验4.1 定量评估4.2 定性评价4.3 消融研究4.4 应用程序 5 结论 摘要 我们介绍 D 3 D^{3} D3人&#xff0c;一种…...

高分辨率北半球多年冻土数据集(2000-2016)

关键数据集分类&#xff1a;冰冻圈数据集时间分辨率&#xff1a;10 year < x < 100 year空间分辨率&#xff1a;1km - 10km共享方式&#xff1a;开放获取数据大小&#xff1a;339.79 MB数据时间范围&#xff1a;2000-01-01 — 2016-12-31元数据更新时间&#xff1a;2022-…...

Prompt Tuning:轻量级大模型微调全攻略

Prompt Tuning(提示调优)步骤金额流程 传统的 Prompt Tuning(提示调优) 是一种轻量级的大模型微调技术,核心是通过优化连续的提示向量(而非模型参数)来适配特定任务。 一、核心步骤概述 准备任务与数据 明确任务类型(如分类、问答等),准备输入文本和目标标签。加载…...

【VBA 字典的引用和调用方法】

如何引用字典对象。在VBA中&#xff0c;字典不是内置的&#xff0c;所以需要引用Microsoft Scripting Runtime库。 在 VBA 中使用 Dictionary&#xff08;字典&#xff09;对象可以方便地存储键值对&#xff08;Key-Item&#xff09;数据&#xff0c;以下是引用方法和常用参数介…...

基于开源AI智能名片链动2+1模式S2B2C商城小程序的管理与运营策略研究

摘要&#xff1a;本文通过分析开源AI智能名片链动21模式S2B2C商城小程序的技术架构与商业逻辑&#xff0c;探讨其在企业管理与运营中的实践价值。结合案例研究&#xff0c;论证该模式如何通过清晰的目标设定、动态反馈机制和资源整合能力&#xff0c;提升团队执行力与客户粘性。…...

储能电站:风光储一体化能源中心数字孪生

在 “双碳” 目标引领下&#xff0c;我国能源产业加速向清洁低碳、绿色化转型&#xff0c;风能、太阳能等可再生能源的开发利用成为关键。然而&#xff0c;风能和太阳能的波动性、间歇性与随机性&#xff0c;给大规模接入电网带来挑战。储能技术的兴起&#xff0c;为解决这一难…...

iOS 直播特殊礼物特效实现方案(Swift实现,超详细!)

特殊礼物特效是提升直播互动体验的关键功能&#xff0c;下面我将详细介绍如何在iOS应用中实现各种高级礼物特效。 基础特效类型 1.1 全屏动画特效 class FullScreenAnimationView: UIView {static func show(with gift: GiftModel, in view: UIView) {let effectView FullS…...

9. 现代循环神经网络

文章目录 9.1. 门控循环单元&#xff08;GRU&#xff09;9.1.1. 门控隐状态9.1.1.1. 重置门和更新门9.1.1.2. 候选隐状态9.1.1.3. 隐状态 9.1.2. 从零开始实现9.1.2.1. 初始化模型参数9.1.2.2. 定义模型 9.1.3. 简洁实现9.1.4. 小结 9.2. 长短期记忆网络&#xff08;LSTM&#…...

视频太大?用魔影工厂压缩并转MP4,画质不打折!

在日常生活中&#xff0c;我们常常需要将视频文件转换成不同的格式以适应各种设备或平台的播放需求。魔影工厂作为一款功能强大且操作简单的视频转换工具&#xff0c;深受用户喜爱。本文中简鹿办公将手把手教你如何使用魔影工厂将视频转换为MP4格式&#xff0c;并进行个性化设置…...

Python中tqdm进度条工具和enumerate函数的使用详解

tqdm进度条工具 tqdm 是 Python 中一个非常流行的 进度条显示工具库&#xff0c;常用于迭代操作的可视化&#xff0c;比如训练神经网络、批量数据处理等任务。 一、tqdm 是什么&#xff1f; tqdm 全称是 taqaddum&#xff08;阿拉伯语&#xff0c;意为“进展”&#xff09;&a…...

最宽温度范围文本格式PT1000分度表-200~850度及PT1000铂电阻温度传感器计算公式

常用PT铂电阻温度传感器 该图片来自网络&#xff0c;在此对图片作者表示感谢。 白色陶瓷面为测温面。 近距离图片。 常用的有PT100、PT500、PT1000&#xff0c;不常用的还有 PT50、PT200、PT10000等&#xff0c;PT代表铂电阻&#xff0c;后面的数字是零摄氏度时电阻值&#…...

基于Netty架构的充电桩系统设计:服务器运维如何更好保障稳定性?

Netty是一个异步事件驱动的网络应用框架&#xff0c;用于快速开发高性能、高可靠性的网络服务器和客户端。它本质上是NIO的封装和增强&#xff0c;主要针对TCP/IP协议下高性能网络通信场景。 本设计通过Netty的高性能网络通信能力&#xff0c;结合充电桩行业特性&#xff0c;实…...

操作系统学习笔记第1章 操作系统概述(灰灰题库

1.单选题 用户发起系统服务请求时&#xff0c;处理器处于______。 A. 用户态 B. 核心态 C. 阻塞态 D. 挂起态 第 1 题 答案&#xff1a;A 解析&#xff1a;用户态下&#xff0c;用户程序只能执行非特权指令 。当用户发起系统服务请求&#xff08;通常通过系统调用&#xff09;时…...

后端开发实习生-抖音生活服务

职位描述 ByteIntern&#xff1a;面向2026届毕业生&#xff08;2025年9月-2026年8月期间毕业&#xff09;&#xff0c;为符合岗位要求的同学提供转正机会。 团队介绍&#xff1a;生活服务业务依托于抖音、抖音极速版等平台&#xff0c;致力于促进用户与本地服务的连接。过去一…...

机器学习算法-sklearn源起

scikit-learn&#xff08;简称 sklearn&#xff09;是 Python 中最流行的开源机器学习库之一&#xff0c;基于 NumPy、SciPy 和 Matplotlib 构建。它提供了丰富的机器学习算法和工具&#xff0c;适用于数据挖掘和数据分析任务。以下是其核心特点的简介&#xff1a; 1、sklearn主…...

Keepalived 在不同场景下的高可用方案设计与最佳实践

一、Keepalived 典型应用场景深度解析 1. Web 服务器集群&#xff1a;统一入口与故障容错 1.1 场景需求 核心目标&#xff1a;为多台 Web 服务器提供统一 VIP 入口&#xff0c;隐藏后端节点细节&#xff0c;实现故障透明切换。 挑战&#xff1a; 确保用户请求在主节点故障时…...

注册并创建一个微信小程序

目录 &#xff08;一&#xff09;前往微信公众平台&#xff0c;并注册一个微信小程序账号 &#xff08;二&#xff09;配置微信小程序 &#xff08;三&#xff09;创建微信小程序项目 1.流程 1.1获取小程序ID 1.2下载微信开发者工具 1.3安装微信开发者工具 2.创建项目…...

CentOS 10:启动telnet服务

参考&#xff0c; 鳥哥私房菜 - 第七章、網路安全與主機基本防護&#xff1a;限制埠口, 網路升級與 SELinux 7.3.3 埠口与服务的启动/关闭及开机时状态设定 我们知道系统的 Telnet 服务通常是以 super daemon 来控管的&#xff0c;请您启动您系统的 telnet 试看看。 1 要启动 …...

计算机网络——每一层的用到的设备及其作用

计算机网络基础 OSI参考模型TCP/IP协议族集线器&#xff08;Hub&#xff09;交换机&#xff08;Switch&#xff09;路由器&#xff08;Router&#xff09;功能特点无线路由器&#xff08;家庭宽带&#xff09;光猫功能 网关&#xff08;Gateway&#xff09;功能应用场景特点 IP…...

OpenLayers 加载鹰眼控件

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图控件是一些用来与地图进行简单交互的工具&#xff0c;地图库预先封装好&#xff0c;可以供开发者直接使用。OpenLayers具有大部分常用的控件&#x…...

Eigen与OpenCV矩阵操作全面对比:最大值、最小值、平均值

功能对比总表 功能Eigen 方法OpenCV 方法主要区别最大值mat.maxCoeff(&row, &col)cv::minMaxLoc(mat, NULL, &maxVal, NULL, &maxLoc)Eigen需要分开调用&#xff0c;OpenCV一次获取最小值mat.minCoeff(&row, &col)cv::minMaxLoc(mat, &minVal, NU…...

安全基础与协议分析

5.1 Web安全基础 5.1.1 Web安全基础概览&#xff08;一、二&#xff09; Web安全的核心目标是保护Web应用及其数据免受攻击&#xff0c;涵盖以下关键领域&#xff1a; 攻击面&#xff1a; 前端漏洞&#xff08;XSS、CSRF&#xff09;。 后端漏洞&#xff08;SQL注入、RCE&a…...

【Web前端】JavaScript入门与基础(一)

JavaScript简介 JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供的核心语法不算…...

第一课:医学影像研究的科学思维与问题提出

课程目标: 理解科学思维在医学影像研究中的核心地位。掌握从临床实践、文献回顾及技术进展中发现医学影像研究问题的方法。学习如何凝练、评估并清晰表述一个具有研究价值的医学影像科学问题。熟悉医学影像研究问题提出的伦理考量。课程大纲与核心内容: 引言 医学影像研究的…...

前端大文件上传性能优化实战:分片上传分析与实战

前端文件分片是大文件上传场景中的重要优化手段&#xff0c;其必要性和优势主要体现在以下几个方面&#xff1a; 一、必要性分析 1. 突破浏览器/服务器限制 浏览器限制&#xff1a;部分浏览器对单次上传文件大小有限制&#xff08;如早期IE限制4GB&#xff09; 服务器限制&a…...

数据的获取与读取篇---常见的数据格式JSON

文件格式 假如你有一份想分析的数据文件,获得文件后下一步就是用代码读取它。不同的文件格式有不同的读取方法。所以读取前了解文件格式也很重要。你可能见过非常多的文件格式,例如TXT、MP3、PDF、JPEG等等。 一般可以通过文件的后缀来分辨文件的格式,例如TXT格式,一般保存…...

【python代码】一些小实验

目录 1. 测试Resnet50 ONNX模型的推理速度 1. 测试Resnet50 ONNX模型的推理速度 ############################### # 导出resnet50 模型 # 测试onnx模型推理 cpu 和 GPU 的对比 ###############################import time import numpy as np import onnxruntime as ort im…...