瑞芯微RK3568与君正X2600e平台Linux系统CS创世SD NAND应用全解析与驱动架构详解

前言

今天就瑞芯微平台和北京君正平台下的linux系统中关于CS创世 SD NAND的使用做一些经验的分享,如有不正,请批评指正;

采用的开发板是RK3568和x2600e,ubuntu版本是20.04,交叉编译工具链是aarch64-linux-gnu-mips-linux-gnu-

下面将从五个板块来进行介绍,分别是操作CS创世 SD NAND的常用命令、SD底层协议简要介绍、对CS创世 SD NAND进行读写操作的三大方式、SD的驱动框架介绍以及CS创世 SD NAND启动,前三个板块没有瑞芯微和君正平台之分,只要是跑linux系统,差别不大,第四块以RK平台为例,第五块同时以君正平台和RK平台为例。

一:操作CS创世 SD NAND的常用命令

1.查看SD设备

使用lsblkfdisk -l命令可以查看到sd设备节点,一般为/dev/mmcblkX/dev/sdX,eg:/dev/mmcblk0;

2.挂载分区

mount 设备分区 挂载点 eg:mount /dev/mmcblk0p1 /mnt/sdcard

3.卸载分区

umount 设备分区/挂载点 eg:umount /dev/mmcblk0p1 或 umount /mnt/sdcard

4.分区管理

fdisk 设备节点eg:fdisk /dev/mmcblk0。进入交互页面后常用命令:

p 打印分区表
n 创建新分区
d 删除分区
t 更改分区类型
w 将更改写入磁盘并退出
q 不保存更改退出
m 显示帮助菜单
l 列出已知的分区类型
v 验证分区表
g 创建新的空GPT分区表
o 创建新的空DOS分区表

在进行分区管理前请务必备份重要数据,因为在更改生效后会丢失原来数据;

5.格式化分区

格式化为FAT32:mkfs.vfat 设备分区;eg: mkfs.vfat /dev/mmcblk0p1

格式化为ext4:mkfs.ext4 设备分区;eg:mkfs.ext4 /dev/mmcblk0p2

6.简单读写

cp,cat,echo等命令。eg:

复制文件:cp /usr/data/1.txt /mnt/sdcard/2.txt

查看文件内容:cat /mnt/sdcard/test.c

写文件:echo "测试内容" > /mnt/sdcard/test.txt

二:SD底层协议简要介绍

SD驱动框架的核心在于初始化流程的精准实现。当卡插入卡槽自动上电后,首先执行CMD0命令进行软复位,强制进入空闲模式。紧接着发送CMD8命令验证电压匹配,此时SDHC/SDSC卡会返回包含电压范围与校验值的R7响应,而SD1.X/MMC卡则无响应。对于无CMD8响应的情况,需通过ACMD41(需先发送CMD55激活应用命令)进一步判断:若仍无响应则判定为MMC卡,需改用CMD1命令激活;若有R1响应则确认为SD1.X卡。

对于支持CMD8的SD卡,需再次执行带HCS=1参数的ACMD41命令,通过解析返回的R3响应中的OCR寄存器完成最终类型鉴别。当OCR寄存器的BUSY位有效时,CCS位为1表示SDHC卡(容量≥2GB),CCS位为0则为SDSC卡(容量<2GB)。完成卡类型确认后,所有卡种进入通用初始化流程:通过CMD2获取128位CID标识符,CMD3分配相对地址(RCA),最终通过CMD7完成卡选择进入传输状态。

技术实现需特别注意:CMD8的电压匹配必须严格遵循3.3V规范;MMC卡识别需准确区分CMD1与ACMD41的响应差异;OCR寄存器解析必须等待BUSY位有效;建议增加协议规定的最大3次超时重试机制。本流程适用于SD2.0协议下的各类存储设备,具体平台实现需结合RK3568与X2600e的硬件特性调整时序参数。

发送CMD2获取CID寄存器的值,SD卡会回复R2响应,再发送CMD3,SD卡获得相对地址,流程图如下图所示:

然后是数据传输模式,初始化完成后进行数据传输就相对简单了,只需要发送对应的命令即可,流程图如下:

下面附上常用命令以及响应的图:

最后提醒几个需要注意的点:

  • 第一是上电之后有一个时间段叫“供电上升时间”,这个是电压上升到操作的总线电平以及等到能发送第一条命令的时间,这个时间需要在1ms,74个时钟周期以及供电上升时间这三者中取最大值,其实这点在驱动源码中也有体现,在mmc_power_up函数(drivers/mmc/core/core.c)如下图所示,同时在协议的6.4.1章节也有说明,这个时间如果没有等待而直接开始发送命令进行初始化,可能也能通过初始化,但是在后面的数据传输阶段就有概率会出现问题了,如果没有注意到这个供电上升时间,那么其实出现了异常是很难定位问题的;

  • 第二是SD卡的两种数据包格式,分别是常规数据和宽位数据,常规数据是指普通的8bit字节数据,发送规则是先发低字节再发高字节,每个字节是先发高位后发低位;而宽位数据一般指SD卡存储寄存器,规则是先发高位,后发低位, 这点在解析SD卡寄存器时需要格外注意,否则就会发现解析的数据明显不符规范;

三:对CS创世 SD NAND进行读写操作的三大方式

CS创世 SD NAND作为一种存储设备,不外乎就是读和写,同时这也是最重要的,熟悉读写方式对于使用CS创世 SD NAND开发非常有帮助,因此在这一块会详细介绍;

1. 使用dd命令

  1. 使用前先挂载分区的文件系统(常用于写入普通文件)
sudo dd if=输入文件 of=输出文件 bs=块大小 status=状态信息 其他选项参数··· 

eg:

sudo dd if=/mnt/sdcard/test.doc of=backup.doc bs=1M count=1
//将/mnt/sdcard/test.doc的前1M字节写入backup.doc;
  1. 使用前不挂载分区的文件系统(常用于写入镜像文件,备份或擦除整个CS创世 SD NAND等)
sudo dd if=输入设备 of=输出设备 bs=块大小 status=状态信息 其他选项参数

eg:

sudo dd if=sd_back_up.img of=/dev/mmcblk0 bs=4M status=progress
//将sd_back_up.img以4MB的块大小写入/dev/mmcblk0,同时显示进度和速度
sudo dd if=/dev/mmcblk1p1 of=/root/zboot.img bs=4M status=progress
//将mmcblk1p1设备中的所有数据读取到/root/zboot.img文件下

如果要对读取的数据进行限制,只读取部分数据,那么使用参数skip或者count

sudo dd if=/dev/sdX of=/path/to/output.img bs=4M skip=10 status=progress      
//skip:跳过前10个块再开始读取数据
sudo dd if=/dev/sdX of=/path/to/output.img bs=4M count=20 status=progress
//count:只读取20个块的数据

也可以结合skip和count,实现从特定位置读取指定数量的数据;

注意:若使用前不挂载分区,那么在写入之前请备份CS创世 SD NAND内重要数据,因为不挂载是绕过文件系统对原始存储设备直接操作,会完全忽略文件系统结构,很可能会覆盖已有数据,甚至可能会损坏文件系统,严重的话只能重新格式化;

如果是先挂载了分区,那么dd命令会通过文件系统进行操作,相对安全;

2. 使用块设备的标准接口

通过文件io或标准io进行读写,无论是挂载文件系统还是不挂载文件系统,都能使用文件io或标准io对CS创世 SD NAND进行操作,区别是前者通过文件系统较为安全且效率稍低而后者直接操作硬件存储设备效率更高同时对于数据的写入需要更加注意防止覆盖重要数据,下面分别提供两个示例;

第一个示例:使用标准io(fopen,fwrite,fread,fseek,fclose等)在已挂载文件系统的CS创世 SD NAND上进行读写,总所周知,在linux里流传着一句话,那就是“一切皆文件”,在这种情况下操作挂载点的文件其实与操作一般文件基本无异;示例实现的功能是在SD卡挂载目录/mnt/sdcard/目录下创建test_data.txt文件,然后再往里面写入4次0~255,然后再将数据读取出来验证是否写入成功并打印测试结果;

编译命令是aarch64-linux-gnu-gcc test2.c -o test2,编译成功后使用sftp root@ip将test2发送至开发板运行测试,运行结果以及程序见下面两张图;

第二个示例:使用文件io(open,write,read,lseek,close)对未挂载文件系统的CS创世 SD NAND直接操作;实现的功能是从CS创世 SD NAND的第1000块开始写入512字节数据,数据内容为0255,0255,写入后再把数据读取出来比较是否成功并打印结果;

编译命令是:aarch64-linux-gnu-gcc test.c -o test,编译成功后使用sftp root@ip将test2发送至开发板运行测试,运行结果和程序也如下面两张图所示

3. 使用ioctl产生系统调用

陷入内核进行处理,前两种方式都容易实现,而且不需要关注底层发送的命令,而ioctl可以对CS创世 SD NAND进行精细的命令控制来约束NAND的行为,并且在ioctl使用过程中还有一些需要注意的点,如果想要对CS创世 SD NAND进行最为底层的命令操作,那么ioctl必定是是首选,因此着重介绍这种方式;

使用ioctl来对CS创世 SD NAND发送命令,其中最为重要的就是填充struct mmc_ioc_cmd结构体,结构体的详细定义位于kernel/include/uapi/linux/mmc/ioctl.h,其中关于flags即命令标志位掩码的定义位于/include/linux/mmc/core.h文件中的struct mmc_command结构体中,下面对struct mmc_ioc_cmd结构体的各成员作详细介绍,如下图所示:

介绍完struct mmc_ioc_cmd结构体后细心的小伙伴会发现了这里面几乎所有的参数都好填充,唯独flags,命令标志位掩码的定义在core.h中,如下图所示:

那么在编写ioctl的程序时,填充flags需要用到core.h里面的内容,总所周知,linux里面的隔离是很严重的,在应用层调用内核层的定义,这是不允许的,编译会报错,之前刚开发的时候因为这个编译报错还折腾了不少时间,最终的解决办法就是把这些标志位的宏定义复制到自己写的应用程序中,这样就没有报错了,下面提供一份程序

程序较长,文件名是init.c(见附件),编译命令是:aarch64-linux-gnu-gcc init.c -o init,编译成功后使用sftp root@ip(若有adb功能也可使用adb push)将可执行程序init发送至开发板进行运行测试,文件中包含ioctl使用的详细解释,下面三张图是运行结果截图(前两张)以及使用逻辑分析仪抓取的命令(第三张图),波形文件名是:init.TLW,见附件,两者都与程序一致,运行过程中没有报错,这就是使用ioctl向CS创世 SD NAND发送底层命令的详细过程;

关于ioctl的使用,有下面几点需要格外注意,这些都是小编亲身走过的坑,所谓前人栽树,后人乘凉, 也算方便大家“乘凉”了哈哈;

第一是如果进行的是写操作,例如发送CMD24,CMD25等命令,那么struct mmc_ioc_cmd结构体的write_flag必须要赋值为非零值,如果是0值,那么很容易导致发送命令失败,得到“errno=110 (Connection timed out)”的错误信息,这个错误信息相对常见,意为连接超时,分析函数调用链可知:在__mmc_blk_ioctl_cmd(drivers/mmc/core/block.c)函数中有

根据write_flag的值会进一步增加MMC_DATA_WRITE或MMC_DATA_READ的标志再传到驱动中,因此发送写命令时请务必给write_flag赋非零值;

第二是在SD卡初始化完成后,开始数据传输前,需要发送ACMD6定义数据总线的宽度,分析源码可知:在mmc_sd_init_card(drivers/mmc/core/sd.c)函数中有如下图所示部分

SD和主机通常都是同时支持4位数据总线宽度的,在不修改SD驱动的情况下,主机控制器是4位总线宽度,所以如果不发送ACMD6,那么卡会保持默认的1位总线宽度,此时主机和NAND的总线宽度不一致,就会出现“errno=84 (Invalid or incomplete multibyte or wide character)”的错误信息,含义是无效或不完整的多字节或宽字符,解决办法有两种,一种是修改驱动源码,将主机控制器的总线宽度固定为1位,此时可以不发送ACMD6,主机和 CS创世 SD NAND都使用1位数据总线通信,第二种是发送ACMD6通知卡改变总线宽度为4位,两者都用4位总线传输数据,ACMD6的参数说明如下图,很明显要第二种方式更简单,因此如果遇到errno=84的错误码,检查一下主机和CS创世 SD NAND的总线宽度是否一致;

第三是频率问题,如果使用ioctl发送cmd时,检查了命令和各项参数都没有错,也没犯上面两种错误,但是运行程序发现就是会报各种错,例如下图(运行的是示例程序init)

那么很大概率就是频率太高,CS创世 SD NAND接受不了,此时需要降频,如下图

降低频率后再次运行,运行结果见下图

只要多加注意以上三点,相信使用ioctl就没有什么大问题;

四:CS创世 SD NAND的驱动框架介绍

以瑞芯微平台的RK3568驱动源码为例

1. MMC/SD驱动在linux中的结构层次

通常在linux系统中,MMC/SD设备都是被抽象成块设备来处理,在kernel的顶层目录下的drivers/mmc目录下通常有三个文件夹分别是core、card和host,有些驱动会将core和card合并成一个core,例如RK3568就是只有core和host,这个驱动框架就是以RK平台的SD驱动来介绍的,下面解释三个文件夹的作用;

1.card层: 要把操作的数据以块设备的处理方式写到存储设备上或从存储设备上读取;因为CS创世 SD NAND属于块设备,那么必然要提供块设备的驱动程序,这部分就是解决了一个问题,即如何将你的CS创世 SD NAND实现为块设备的。

2.core 层:则是将数据以何种格式,何种方式在 MMC/SD主机控制器与MMC/SD卡的记 忆体(即块设备)之间进行传递,这种格式、方式被称之为规范或协议.

这部分完成了不同协议和规范的实现,抽离出不同SD主机控制器的共性,并为HOST 层的驱动提供了接口函数

3.host 层: 是这个文件夹属于 Linux 内核中 MMC/SD 子系统 的 硬件驱动层,直接负责与 MMC/SD 主机控制器(Host Controller) 的硬件交互。它的核心作用是向上提供统一的接口供 Core 层 调用(如发送命令、读写数据),向下为不同厂商的 Host 控制器提供驱动实现,将上层(Core 层)的协议请求转换为具体的寄存器操作、时钟控制、DMA 传输等硬件行为。

core层根据协议规范来构造各种命令,那么命令是怎么发送给CS创世 SD NAND呢?通过主机控制器。

主机控制器通过设置SD需要的gpio资源,注册中断资源,使能控制器等等,然后再向上面的核心层增加一个host,这样核心层就能调用具体的硬件操作函数来和SD卡通信了;

card和core是封装好的共性以及规范,通常是不需要修改的,而host层是直接与硬件打交道,需要控制底层寄存器的,不同的host控制器硬件资源也不一样,因此驱动CS创世 SD NAND,host层才是应该需要修改开发的,MMC/SD驱动在linux中的结构层次见下图:

2. SD驱动中核心的数据结构举例

(CS创世 SD NAND和SD卡在驱动中使用的数据结构和调用的函数是一致的,并且有不少数据结构命名或函数功能注释翻译过来用SD卡描述更贴切,因此下面描述用词使用SD卡代替CS创世 SD NAND,不再赘述)

1.struct mmc_host

功能:表示一个MMC/SD卡主机控制器,它是驱动程序和内核MMC子系统之间的主要接口;

重要成员:

const struct mmc_host_ops *ops:包含操作该主机的各种函数指针,包括发送命令,设置时钟和电源,请求操作等,用于和硬件交互
struct device class_dev:代表该主机控制器的设备对象,可用于设备模型的注册和管理
unsigned int f_min:主机控制器支持的最小时钟频率
unsigned int f_max:主机控制器支持的最大时钟频率,对于SD卡的操作频率非常重要
struct mmc_card *card:指向插入该主机的SD卡设备
struct mmc_ios ios:包含了当前IO的状态信息,例如时钟频率,电压范围,电源模式等
const struct mmc_bus_ops *bus_ops:指向struct mmc_bus_ops结构体,定义了和MMC卡通信的操作集,例如读写,卡检测等;
u32 ocr_avail:存储MMC主机可用的操作条件寄存器OCR的值,
u32 caps:表示MMC主机的能力和特性,通过不同的位来标记主机的各种模式;
  1. struct mmc_card

功能:表示一个插入到MMC主机控制器的SD卡设备

重要成员:

struct mmc_host *host:指向SD卡所连接的主机控制器
unsigned int rca:相对卡地址,是SD卡的重要标识符,用于在总线上唯一标识该卡
unsigned int type:卡类型,例如MMC,SD,SDIO等,用于区分不同的设备类型
u32 ocr:操作条件寄存器,包含了SD卡的操作条件信息,例如支持的电压范围,电源模式等;
struct mmc_cid cid:包含卡的CID信息,例如制造商id,产品名称等;
  1. struct mmc_ios

功能:包含SD主机控制器的IO状态信息

重要成员:

unsigned int clock:当前的时钟频率,驱动可以根据需要调整此时钟频率,来满足不同操作的需求;
unsigned char power_mode:电源模式,例如MMC_POWER_OFF,MMC_POWER_UP,用于控制SD卡的电源状态
unsigned char bus_width:总线宽度,可以选择1线,4线,8线,根据SD卡的能力和操作需求进行调整
  1. struct mmc_host_ops

功能:包含了操作MMC主机控制器的一系列函数指针,是驱动程序与硬件交互的接口;

重要成员举例:

void (*request)(struct mmc_host *host, struct mmc_request *req):函数指针,用于将一个操作请求添加到主机控制器的请求队列中
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios):用于设置主机控制器的IO状态,例如调整时钟频率,电源模式和总线宽度等等;
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios):在需要切换SD卡的操作电压时调用此函数;
int (*get_ro)(struct mmc_host *host):检查MMC/SD卡是否被写保护了;
int (*get_cd)(struct mmc_host *host):检查SD卡的插入和拔出;
  1. struct mmc_request

功能:表示一个对SD卡的操作请求,通常包括命令,数据传输等信息;

重要成员:

struct mmc_command *cmd:指向要执行的命令;
struct mmc_data *data:指向要传输的数据对象,用于数据块的读写操作;

3. host层函数调用关系解析

这里主要做了两件事,其一是mmc_alloc_host分配一个mmc_host,其二是mmc_add_host添加一个mmc_host;

4. 卡的检测

我们用的SD卡只是一张卡,要操作卡还得通过主机控制器才行,因此会有struct mmc_card,struct mmc_host之分,截至这里再回忆一下 dw_mci_init_slot做的事情,大概就是准备一个 mmc_host 结构,然后添加一个主控制器设备到内核,最后又调用了一下 mmc_rescan 来检测是不是有卡插入了,有人会问如果此时卡没有插入呢,那么不就是白白调用一次mmc_rescan,那下次卡插入又是怎么检测到的呢,很明显,这种不能确定触发时间的完全未知的动作,肯定是需要通过中断机制来处理,检测到卡插入,触发中断,扫描卡,开始初始化等流程,检测到卡拔出触发中断,清理各类资源,释放空间;

5. 扫描流程

struct mmc_card 结构里面包含了一个 struct device 结构, mmc_alloc_card 不但申请了内存,而且还填充了 struct device 中的几个成员,尤其 card->dev.bus = &mmc_bus_type; 这一句要重点对待,mmc_bus_type的定义如下图

申请一个 mmc_card 结构,并简单初始化后, mmc_init_card 的使命就完成了,然后再调用 mmc_add_card 将这个 card 设备添加到内核。 mmc_add_card 其实很简单,就是调用 device_add 将 card->dev 添加到内核当中去;

device_add 里面,设备对应的总线会拿着你这个设备和挂在这个总线上的所有驱动程序去匹配( match ),此时会调用 match 函数(如下图),如果匹配到了就会调用总线的 probe 函数或驱动的 probe 函数;

所以match永远不会失败,匹配成功就会执行mmc_bus_probe(定义见下图)

追踪到这里,probe由调用了 drv->probe() ,这就需要知道drv的定义了,struct mmc_driver*drv=to_mmc_driver(dev->driver);match 函数总是返回 1 ,说明挂在这条总线上的 driver 都有可能跑到这里来了,事实的确也是这样的,不过好在挂在这条总线上的 driver 只有一个,定义见下图:

因此跳转到mmc_bllk_probe函数执行,函数定义如下:

mmc_blk_probe 是 MMC 块设备驱动的核心探测函数,负责将 MMC/SD 卡注册为块设备,使其能够被系统访问,看到这里已经把core,host目录下的文件都牵扯进来了,慢慢再捋一下就能看出从host到core的联系了;

6. 数据的读写

在驱动中,向SD卡发送cmd都是通过请求队列来完成的,所有的cmd都会被封装成请求,然后由内核的I/O调度器统一排序,合并或拆分,再下发给硬件驱动,这样避免了驱动直接操作硬件,而是通过标准接口解耦,增强了安全性和可维护性,所以分析cmd的发送首先要定位到request请求队列,关键函数调用流程如下图:

顺便附上上图中提到的两个ops操作集合的赋值:

分析到这里,整个SD驱动框架做的事情说简单一些其实就干了两件事,一是卡的扫描检测,而是数据的读写,不过这个过程中涉及到的数据结构和函数调用确实相对复杂,捋清楚驱动框架还是需要静下心来花费时间的;

五:CS创世 SD NAND启动

相同的板卡厂商一般CS创世 SD NAND启动的流程固定,大致讲一下流程,首先是要制作SD启动卡,即将启动镜像烧录进CS创世 SD NAND,一般板卡厂商都会有专门的制作工具,只需按照使用方法来制作启动卡即可,如果没有制作工具,那么一般使用dd命令将系统镜像写入卡内(写入时可能有地址参数要求,需要咨询板卡厂商的技术支持),然后是选择启动方式,一般是有几个boot引脚,启动时系统会根据boot引脚的电平组合来选择启动方式,有些开发板可能在选择CS创世 SD NAND启动时还需要配置其他内核参数,只需咨询对应的技术即可,最后就是验证系统是否能正常从SD启动,数据读写是否正常;

君正平台

开发板是x2600e,CS创世 SD NAND是采用CS256G-AOW;

1. 制作SD启动卡

君正的烧录工具支持将启动镜像烧录到CS创世 SD NAND,先编译准备好启动镜像,然后按照下图顺序进行制作启动卡;

2. 选择启动方式

查询芯片手册得知启动方式的选择依据如下图

再查看原理图如下:

可知目前的电平组合是001,即选择的是SFC@PD06_3.3V(默认从SPI NAND FLASH启动),现在需要改为从SD启动,那么就要将BOOT_SET0即PD14接低电平,使得电平组合为000,然后就是查看PCB,找到PD14的gpio,再修改硬件将其接地即可;

3. 验证SD启动

通过上图可知,系统成功从CS创世 SD NAND启动,然后使用一些简单的读写命令验证是否正常即可,一般而言只要能正常启动系统读写就没问题,因为启动过程中本身就已经包含了对SD进行读写。

瑞芯微平台

开发板是RK3568,CS创世 SD NAND采用CS256G-AOW,也采取相同步骤;

1. 制作SD启动卡

首先编译准备好镜像文件,并将其拷贝到windows端(可以将镜像放置共享目录下),然后打开瑞芯微的制作工具SDDiskTool,按照下图所示进行配置:

正常来说制作过程是2~3分钟,但是也遇到过十分钟左右的,这个可能因CS创世 SD NAND而异,但是只要没报错,就耐心等待制作完成;

2. 选择启动方式

由于咨询技术支持得到的答复是RK对于启动引导不开源,原理也是靠硬件选boot脚,然后在芯片手册上也没找到有关boot引脚配置的部分,所以说明在RK上我们不需要修改硬件,额外配置boot引脚来选择启动方式,后面在rockchip-common.h文件中找到了关于启动方式的优先顺序,如下图:

因此只要启动时在卡槽检测到CS创世 SD NAND,那么就会优先从SD启动;

3. 验证SD启动

从上面两张图中可以相互印证,系统成功从SD启动;

启动卡的分区情况如下图:

从mmcblk1p1至mmcblk1p6共6个分区,依次是uboot,misc,boot,recovery,backup和rootfs,前面5个分区一般存储特定数据,不建议用户将数据写入,而mmcblk1p6是根文件系统分区,存储所有系统文件、应用程序、用户数据等,进行用户操作时就是基于这个分区,因此进行读写测试或者存储用户数据建议在此分区进行。

六、结语

最后分享一些使用的软件:

串口工具:Windows下:MobaXterm;Ubuntu下:Minicom

分析内核源码:source insight 4

逻辑分析仪:ATK-Logic 或Acute TravelLogic Analyzer

除了软件,硬件上的逻辑分析仪,示波器,万用表也可辅助调试验证。

如果你想试一试请点击官网

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

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

相关文章

深入解析 Flink Function

RichFunctionFunction只是个标记接口public interface Function extends java.io.Serializable {}RichFunction 的核心语义是为用户定义的函数(UDF)提供生命周期管理和运行时上下文访问的能力。任何一个普通的 Flink Function 接口(例如 MapF…

JMeter —— 压力测试

目录 常用的性能指标 一、吞吐量类指标 二、响应时间类指标 三、资源利用率指标 JMeter 一、JMeter 简介 二.下载安装JMeter: 三.如何使用JMeter: 压力测试考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。压测都是为了系统…

Transformer在哪⾥做了权重共享?

1、什么是权值共享权重共享是指在模型的不同层之间复⽤相同的参数。这可以减少模型的总体参数数量,并使得模型在训练时更容易学习。2、在Transformer中的应用常见的做法是共享词嵌入层(embedding layer)和输出层(output layer&…

将 agents 连接到 Elasticsearch 使用模型上下文协议 - docker

我们在之前的文章 “将 agents 连接到 Elasticsearch 使用模型上下文协议” 及 “使用 MCP 将代理连接到 Elasticsearch 并对索引进行查询” 详述了如何使用 Elasticsearch MCP server 来和我们的 Elasticsearch 进行对话。细心的开发者可能已经注意到我们的 Elasticsearch MCP…

Shell 编程基础与实践要点梳理

目录 前言 一、认识 Shell 1.1 Shell 的定义与作用 1.2 Shell 解释器 二、Shell 脚本入门 2.1 编写 Shell 脚本 2.2 赋予执行权限与执行脚本 三、Shell 变量 3.1 变量定义与规则 3.2 变量使用与操作 3.3 变量类型 四、Shell 字符串 4.1 字符串定义方式 4.2 字符串…

Python自动化测试完整教程:pytest + selenium实战

目录 前言环境搭建pytest基础教程selenium基础教程pytest selenium实战项目页面对象模式(POM)测试报告生成持续集成配置最佳实践和进阶技巧总结 前言 自动化测试是现代软件开发中不可或缺的一环。Python作为一门简洁优雅的编程语言,配合pytest测试框架和seleniu…

APM 系列(一):Skywalking 与 Easyearch 集成

概述 SkyWalking 是一个开源的可观测性平台,用于收集、分析、聚合和可视化服务和云原生基础设施的数据。SkyWalking 提供了一种简单的方法,即使在云之间也能保持对分布式系统的清晰视图。它是一个现代的 APM,专门为云原生、基于容器的分布式…

使用 AD 帐户从 ASP.NET 8 容器登录 SQL Server 的 Kerberos Sidecar

我最近在做一个项目,需要将一个 ASP.NET 8 Web API 应用程序容器化,该应用程序需要与本地运行的 SQL Server 数据库进行通信。我们决定将 ASP.NET 8 容器定位到 Linux 系统,因此必须与运行在 Windows AD 域中的数据库进行通信。 问题 我们之前的设置是使用 IIS 在 Windows …

More Effective C++ 条款11:禁止异常流出析构函数之外

More Effective C 条款11:禁止异常流出析构函数之外核心思想 在C中,析构函数绝对不允许抛出异常。如果异常从析构函数中传播出去,可能会导致程序立即终止或未定义行为,特别是在栈展开过程中处理已有异常时。通过捕获并处理所有析构…

商超高峰客流统计误差↓75%!陌讯多模态融合算法在智慧零售的实战解析

原创声明:本文为原创技术解析,核心技术参数、架构设计及实战数据引用自 “陌讯技术白皮书”,技术方案与落地案例结合aishop.mosisson.com智慧零售数据联动场景展开,禁止未经授权的转载与商用。 一、行业痛点:智慧零售…

PyTorch实战(2)——使用PyTorch构建神经网络

PyTorch实战(2)——使用PyTorch构建神经网络0. 前言1. PyTorch 构建神经网络初体验1.1 使用 PyTorch 构建神经网络1.2 神经网络数据加载1.3 模型测试1.4 获取中间层的值2. 使用 Sequential 类构建神经网络3. PyTorch 模型的保存和加载3.1 模型保存所需组…

关于git的安装(windows)

1.git的介绍 Git 是一个分布式版本控制系统,由 Linus Torvalds 在 2005 年为 Linux 内核开发而创建。它能够高效地处理从小型到超大型项目的版本管理,具有以下特点: 分布式架构:每个开发者本地都有完整的仓库副本高效性能&#…

Java后端开发?接口封装器!

开发接口确实是Java后端开发中最核心、最可见的产出工作。“对入参校验、处理业务逻辑、返回格式处理”——精准地描述了一个API接口的核心处理流程。 但这只是冰山之上最直观的部分。一个专业、稳健、可扩展的后端系统,其复杂性和价值绝大部分隐藏在冰山之下。结合…

【沉浸式解决问题】NVIDIA 显示设置不可用。 您当前未使用连接到NVIDIA GPU 的显示器。

目录一、问题描述二、环境版本三、原因分析四、解决方案一、问题描述 在看一篇cuda安装的教程时,第一步是打开NVIDIA 控制面板,但是我打不开: NVIDIA 显示设置不可用。 您当前未使用连接到NVIDIA GPU 的显示器。 二、环境版本 设备&#xf…

牛客周赛 Round 106(小苯的方格覆盖/小苯的数字折叠/ 小苯的波浪加密器/小苯的数字变换/小苯的洞数组构造/ 小苯的数组计数)

A 小苯的方格覆盖思路&#xff1a;怎么摆第三行都是横放的2*1&#xff1b;故若n为奇数&#xff0c;总格子数3n为奇数&#xff0c;无法被2整除&#xff0c;直接排除。#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<iostream> #include<bits/stdc…

高并发内存池(16)-三层缓存的回收过程

高并发内存池&#xff08;16&#xff09;-三层缓存的回收过程 内存池的回收过程是内存管理系统的关键环节&#xff0c;它通过分层协作和智能合并机制&#xff0c;确保内存高效重复利用。以下是完整的回收流程解析&#xff1a;一、回收触发场景 ThreadCache回收&#xff1a;线程…

深入解析MyBatis Mapper接口工作原理

在Java持久层框架中&#xff0c;MyBatis以其灵活性和易用性赢得了广大开发者的青睐。作为MyBatis的核心概念之一&#xff0c;Mapper接口机制极大地简化了数据库操作代码的编写。本文将深入剖析MyBatis Mapper接口的工作原理&#xff0c;从基础概念到底层实现&#xff0c;帮助开…

疯狂星期四文案网第49天运营日记

网站运营第49天&#xff0c;点击观站&#xff1a; 疯狂星期四 crazy-thursday.com 全网最全的疯狂星期四文案网站 运营报告 今日访问量 常州苏州那些ip锲而不舍的扫了很多php的页面 今日搜索引擎收录情况 k页面比较严重了&#xff0c;哎。 我感觉不该做其他类型文案的 网…

从GPT-5发布来分析LLM大模型幻觉收敛(一)

GPT-5 号称在任何领域都有博士级别能力。在医疗健康领域&#xff0c;能够对专业的癌症诊断报告做通俗易懂的解读。对复杂的放射治疗方案决策&#xff0c;也能提供详细的分析报告&#xff0c;帮助病人权衡利弊。一位癌症患者的家属在发布会上表示&#xff0c;“ 真正鼓舞人心的是…

大模型安全概述、LlamaFirewall

资料搜集整理自网络。 概述 大模型爆火之后&#xff0c;衍生出大模型安全这一个比较新的领域。和之前的文章一样&#xff0c;本文有不少新颖的名词、概念、理论。 信通院、清华大学等多个单位联合发布的《大模型安全实践&#xff08;2024&#xff09;》&#xff0c;提出LLM安…