Linux驱动编程 - seq_open、single_open使用方法
目录
前言:
一、seq_xxx
1、seq_xxx 函数介绍
1.1 seq_open
1.2 seq_read
1.3 seq_lseek
1.4 seq_release
1.5 格式化输出函数
2、seq_open 实例
二、single_xxx 函数
1、single_xxx 函数介绍
1.1 single_open
1.2 single_start
1.3 single_next
1.4 single_stop
1.5 single_release
2、single_open 实例
三、更精简的方法 DEFINE_SHOW_ATTRIBUTE
前言:
内核态和用户态数据交互有很多方法,比如驱动中的 copy_to_user、copy_from_user。再比如驱动中添加属性文件,cat、echo调用节点属性文件的xx_show函数或xx_store函数。
本文介绍 seq_file 的用法,为了更简单和方便,内核提供了single_xxx系列接口,它是对seq_file的进一步封装。
相比较于之前的方法其优势可以向应用层导出比较多的数据,例如大于1个PAGE_SIZE,同时驱动文件中我们不用关注复杂的缓冲区管理,这些内核自己处理,属于比较成熟的内核接口。
seq_file机制提供了一种标准化的例程,使得顺序文件的处理变得简单高效。顺序文件通常用于遍历一些数据项并创建文件内容,其访问模式是逐个访问读取数据项。seq_file机制通过struct seq_operations结构定义了一系列操作函数,包括开始、停止、显示和获取下一个操作,从而简化了文件处理过程
测试示例依赖debugfs,内核配置打开debugfs:
CONFIG_DEBUG_FS=y
如果/sys/kernel/debug是空的,执行以下命令挂载debugfs文件系统:
$ mount -t debugfs none /sys/kernel/debug
一、seq_xxx
seq_file只是在普通的文件read中加入了内核缓冲的功能,从而实现顺序多次遍历,读取大数据量的简单接口。
/* 路径:include/linux/seq_file.h */
struct seq_file {char *buf; //在seq_open中分配,大小为4KBsize_t size; //4096size_t from; //struct file从seq_file中向用户态缓冲区拷贝时相对于buf的偏移地址size_t count; //可以拷贝到用户态的字符数目size_t pad_until;loff_t index; //从内核态向seq_file的内核态缓冲区buf中拷贝时start、next的处理的下标pos数值,即用户自定义遍历iterloff_t read_pos; //当前已拷贝到用户态的数据量大小,即struct file中拷贝到用户态的数据量u64 version;struct mutex lock;const struct seq_operations *op; //操作集,包含seq_start,seq_next,set_show,seq_stop函数int poll_event;const struct file *file;void *private;
};struct seq_operations {void * (*start) (struct seq_file *m, loff_t *pos);void (*stop) (struct seq_file *m, void *v);void * (*next) (struct seq_file *m, void *v, loff_t *pos);int (*show) (struct seq_file *m, void *v);
};
- start:用于指定seq_file文件的读开始位置,返回实际读开始位置,如果指定的位置超过文件末尾,应当返回NULL;
- stop:用于在读完seq_file文件后调用,它类似于文件操作close,用于做一些必要的清理,如释放内存等;
- next:用于把seq_file文件的当前读位置移动到下一个读位置,返回实际的下一个读位置,如果已经到达文件末尾,返回NULL;
- show:用于格式化输出,如果成功返回0,否则返回出错码;
1、seq_xxx 函数介绍
内核自带函数 seq_open、seq_read、seq_lseek、seq_release等函数
1.1 seq_open
功能:用于把struct seq_operations结构与seq_file文件关联起来
/*
参数:*file:指向要操作的文件指针*op:指向一个seq_operations结构体,包含start、show等
返回值:0:成功, 非0:失败
*/
int seq_open(struct file *file, const struct seq_operations *op)
{struct seq_file *p;WARN_ON(file->private_data);p = kmem_cache_zalloc(seq_file_cache, GFP_KERNEL);if (!p)return -ENOMEM;file->private_data = p;mutex_init(&p->lock);p->op = op;// No refcounting: the lifetime of 'p' is constrained// to the lifetime of the file.p->file = file;/** Wrappers around seq_open(e.g. swaps_open) need to be* aware of this. If they set f_version themselves, they* should call seq_open first and then set f_version.*/file->f_version = 0;/** seq_files support lseek() and pread(). They do not implement* write() at all, but we clear FMODE_PWRITE here for historical* reasons.** If a client of seq_files a) implements file.write() and b) wishes to* support pwrite() then that client will need to implement its own* file.open() which calls seq_open() and then sets FMODE_PWRITE.*/file->f_mode &= ~FMODE_PWRITE;return 0;
}
1.2 seq_read
功能:用于从内核缓冲区中读取数据并将其输出到用户空间
/*
参数:*file:指向要操作的文件指针*buf:用户空间的缓冲区地址,用于存放读取的数据size:用户请求读取的字节数*ppos:文件描述符的当前位置,指示读取的起始位置
返回值:读取的字节数,失败返回负数错误码
*/
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{struct seq_file *m = file->private_data;size_t copied = 0;size_t n;void *p;int err = 0;mutex_lock(&m->lock);/** seq_file->op->..m_start/m_stop/m_next may do special actions* or optimisations based on the file->f_version, so we want to* pass the file->f_version to those methods.** seq_file->version is just copy of f_version, and seq_file* methods can treat it simply as file version.* It is copied in first and copied out after all operations.* It is convenient to have it as part of structure to avoid the* need of passing another argument to all the seq_file methods.*/m->version = file->f_version;/** if request is to read from zero offset, reset iterator to first* record as it might have been already advanced by previous requests*/if (*ppos == 0) {m->index = 0;m->version = 0;m->count = 0;}/* Don't assume *ppos is where we left it */if (unlikely(*ppos != m->read_pos)) {while ((err = traverse(m, *ppos)) == -EAGAIN);if (err) {/* With prejudice... */m->read_pos = 0;m->version = 0;m->index = 0;m->count = 0;goto Done;} else {m->read_pos = *ppos;}}/* grab buffer if we didn't have one */if (!m->buf) {m->buf = seq_buf_alloc(m->size = PAGE_SIZE);if (!m->buf)goto Enomem;}/* if not empty - flush it first */if (m->count) {n = min(m->count, size);err = copy_to_user(buf, m->buf + m->from, n);if (err)goto Efault;m->count -= n;m->from += n;size -= n;buf += n;copied += n;if (!size)goto Done;}/* we need at least one record in buffer */m->from = 0;p = m->op->start(m, &m->index);while (1) {err = PTR_ERR(p);if (!p || IS_ERR(p))break;err = m->op->show(m, p);if (err < 0)break;if (unlikely(err))m->count = 0;if (unlikely(!m->count)) {p = m->op->next(m, p, &m->index);continue;}if (m->count < m->size)goto Fill;m->op->stop(m, p);kvfree(m->buf);m->count = 0;m->buf = seq_buf_alloc(m->size <<= 1);if (!m->buf)goto Enomem;m->version = 0;p = m->op->start(m, &m->index);}m->op->stop(m, p);m->count = 0;goto Done;
Fill:/* they want more? let's try to get some more */while (1) {size_t offs = m->count;loff_t pos = m->index;p = m->op->next(m, p, &m->index);if (pos == m->index)/* Buggy ->next function */m->index++;if (!p || IS_ERR(p)) {err = PTR_ERR(p);break;}if (m->count >= size)break;err = m->op->show(m, p);if (seq_has_overflowed(m) || err) {m->count = offs;if (likely(err <= 0))break;}}m->op->stop(m, p);n = min(m->count, size);err = copy_to_user(buf, m->buf, n);if (err)goto Efault;copied += n;m->count -= n;m->from = n;
Done:if (!copied)copied = err;else {*ppos += copied;m->read_pos += copied;}file->f_version = m->version;mutex_unlock(&m->lock);return copied;
Enomem:err = -ENOMEM;goto Done;
Efault:err = -EFAULT;goto Done;
}
1.3 seq_lseek
功能:重新定位文件内的读/写文件偏移量
/*
参数:*file:指向要操作的文件指针offset:偏移量,表示从文件开头向后移动的字节数whence:起始位置,可以是SEEK_SET(文件开头)、SEEK_CUR(当前位置)或SEEK_END(文件末尾)
返回值:文件位置,失败返回-1
*/
loff_t seq_lseek(struct file *file, loff_t offset, int whence)
{struct seq_file *m = file->private_data;loff_t retval = -EINVAL;mutex_lock(&m->lock);m->version = file->f_version;switch (whence) {case SEEK_CUR:offset += file->f_pos;/* fall through */case SEEK_SET:if (offset < 0)break;retval = offset;if (offset != m->read_pos) {while ((retval = traverse(m, offset)) == -EAGAIN);if (retval) {/* with extreme prejudice... */file->f_pos = 0;m->read_pos = 0;m->version = 0;m->index = 0;m->count = 0;} else {m->read_pos = offset;retval = file->f_pos = offset;}} else {file->f_pos = offset;}}file->f_version = m->version;mutex_unlock(&m->lock);return retval;
}
1.4 seq_release
功能:释放seq_file结构体资源
int seq_release(struct inode *inode, struct file *file)
{struct seq_file *m = file->private_data;kvfree(m->buf);kmem_cache_free(seq_file_cache, m);return 0;
}
1.5 格式化输出函数
/*函数seq_putc用于把一个字符输出到seq_file文件*/
int seq_putc(struct seq_file *m, char c);
/*函数seq_puts则用于把一个字符串输出到seq_file文件*/
int seq_puts(struct seq_file *m, const char *s);
/*函数seq_escape类似于seq_puts,只是,它将把第一个字符串参数中出现的包含在第二个字符串参数中的字符按照八进制形式输出,也即对这些字符进行转义处理*/
int seq_escape(struct seq_file *, const char *, const char *);
/*函数seq_printf是最常用的输出函数,它用于把给定参数按照给定的格式输出到seq_file文件*/
int seq_printf(struct seq_file *, const char *, ...)__attribute__ ((format(printf,2,3)));
/*函数seq_path则用于输出文件名,字符串参数提供需要转义的文件名字符,它主要供文件系统使用*/
int seq_path(struct seq_file *, struct vfsmount *, struct dentry *, char *);
2、seq_open 实例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>static void *my_seq_start(struct seq_file *p, loff_t *pos)
{if (*pos >= 1)return NULL;return 1;
}static void *my_seq_next(struct seq_file *p, void *v, loff_t *pos)
{++(*pos);return NULL;
}static void my_seq_stop(struct seq_file *p, void *v)
{return;
}static int my_seq_show(struct seq_file *p, void *v)
{seq_printf(p, "hello word\n");return 0;
}static const struct seq_operations my_seq_ops= {.start = my_seq_start,.next = my_seq_next,.stop = my_seq_stop,.show = my_seq_show,
};static int my_seq_open(struct inode *inode, struct file *file)
{return seq_open(file, &my_seq_ops); //内核自带的 seq_open 设置 seq_operations
}static const struct file_operations my_debug_fops = {.owner = THIS_MODULE,.open = my_seq_open,.read = seq_read, //内核自带的 seq_read.llseek = seq_lseek, //内核自带的 seq_lseek.release = seq_release, //内核自带的 seq_release
};static struct dentry *seq_file_demo_dir = NULL;
int demo_seq_init(void)
{/* 生成 /sys/kernel/debug/demo_seq 文件 */seq_file_demo_dir = debugfs_create_file("demo_seq", 0444, NULL,NULL, &my_debug_fops);return 0;
}static void __exit demo_seq_exit(void)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);if (seq_file_demo_dir)debugfs_remove(seq_file_demo_dir);
}module_init(demo_seq_init);
module_exit(demo_seq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("dongao");
运行结果:
$ insmod ./demo_seq_driver.ko
$ cat /sys/kernel/debug/demo_seq
hello word
也可以应用程序 open 打开 /sys/kernel/debug/demo_seq ,然后read,如下:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"int main(int argc, char *argv[])
{int fd = -1;int ret = -1;char buf[1024] = {0};fd = open("/sys/kernel/debug/demo_seq", O_RDONLY);if(fd < 0){printf("open failed!\r\n");return -1;}lseek(fd, 0, SEEK_SET);ret = read(fd, buf, sizeof(buf) - 1);if(ret < 0){printf("read Failed!\r\n");close(fd);return -1;}printf("%s", buf); //打印输出 "hello word"ret = close(fd); /* 关闭文件 */if(ret < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}
二、single_xxx 函数
相比于seq_open()函数,内核还提供了一个更加精简的single_open()函数,只需要实现xx_show函数就行。
1、single_xxx 函数介绍
1.1 single_open
single_start、single_next、single_stop是内核提供的函数。single_open中实现了设置struct seq_operations,并调用了 seq_open。
/* fs/seq_file.c */
int single_open(struct file *file, int (*show)(struct seq_file *, void *),void *data)
{struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL_ACCOUNT);int res = -ENOMEM;if (op) {op->start = single_start;op->next = single_next;op->stop = single_stop;op->show = show;res = seq_open(file, op);if (!res)((struct seq_file *)file->private_data)->private = data;elsekfree(op);}return res;
}
1.2 single_start
static void *single_start(struct seq_file *p, loff_t *pos)
{return NULL + (*pos == 0);
}
1.3 single_next
static void *single_next(struct seq_file *p, void *v, loff_t *pos)
{++*pos;return NULL;
}
1.4 single_stop
static void single_stop(struct seq_file *p, void *v)
{
}
1.5 single_release
int single_release(struct inode *inode, struct file *file)
{const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;int res = seq_release(inode, file);kfree(op);return res;
}
2、single_open 实例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>static int my_seq_show(struct seq_file *m, void *v)
{seq_printf(m, "test single_open\n");return 0;
}static int my_seq_open(struct inode *inode, struct file *file)
{return single_open(file, &my_seq_show, NULL); //设置 xxx_show
}static const struct file_operations my_debug_fops = {.owner = THIS_MODULE,.open = my_seq_open,.read = seq_read, //内核自带的 seq_read.llseek = seq_lseek, //内核自带的 seq_lseek.release = seq_release, //内核自带的 seq_release
};static struct dentry *seq_file_demo_dir = NULL;
int demo_seq_init(void)
{/* 生成 /sys/kernel/debug/demo_seq 文件 */seq_file_demo_dir = debugfs_create_file("demo_seq", 0444, NULL,NULL, &my_debug_fops);return 0;
}static void __exit demo_seq_exit(void)
{printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);if (seq_file_demo_dir)debugfs_remove(seq_file_demo_dir);
}module_init(demo_seq_init);
module_exit(demo_seq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("dongao");
生成 /sys/kernel/debug/demo_seq 文件,测试方法同上。
三、更精简的方法 DEFINE_SHOW_ATTRIBUTE
#define DEFINE_SHOW_ATTRIBUTE(__name) \
static int __name ## _open(struct inode *inode, struct file *file) \
{ \return single_open(file, __name ## _show, inode->i_private); \
} \\
static const struct file_operations __name ## _fops = { \.owner = THIS_MODULE, \.open = __name ## _open, \.read = seq_read, \.llseek = seq_lseek, \.release = single_release, \
}
实例如下
#include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>static struct dentry *seq_file_demo_dir;static int my_seq_show(struct seq_file *seq, void *v)
{seq_printf(seq, "Hello World\n");return 0;
}
DEFINE_SHOW_ATTRIBUTE(my_seq);static int __init demo_seq_init(void)
{/* 生成 /sys/kernel/debug/demo_seq 文件 */seq_file_demo_dir = debugfs_create_file("demo_seq", 0444, NULL,NULL, &my_seq_fops);return 0;
}static void __exit demo_seq_exit(void)
{if (seq_file_demo_dir)debugfs_remove(seq_file_demo_dir);
}module_init(demo_seq_init);
module_exit(demo_seq_exit);
MODULE_LICENSE("GPL");
测试方法同上,不再赘述。
相关文章:
Linux驱动编程 - seq_open、single_open使用方法
目录 前言: 一、seq_xxx 1、seq_xxx 函数介绍 1.1 seq_open 1.2 seq_read 1.3 seq_lseek 1.4 seq_release 1.5 格式化输出函数 2、seq_open 实例 二、single_xxx 函数 1、single_xxx 函数介绍 1.1 single_open 1.2 single_start 1.3 single_next 1.4 single_stop…...
N列股票收盘价为起点的马科维茨(Markowitz)均值—方差理论
1. 数据准备与收益率计算 输入数据: 假设你有一个矩阵,每一列代表一只股票的历史收盘价序列。每一行对应一个时间点的收盘价。 计算收益率: 马科维茨理论要求使用资产的收益率而非价格。常用的收益率计算方法有对数收益率或简单收益率。 2.…...
【嵌入式学习2】函数
目录 ## 函数 ## 函数分类 ## 函数定义 1、无参数无返回值 2、有参数无返回值 3、有参数有返回值 ## 函数声明 ## 局部变量和全局变量 ## 多文件编程 如何避免把同一个头文件 include 多次,或者头文件嵌套包含? 命令行编译文件 头文件包含的…...
模式搜索+扩散模型:FlowMo重构图像Token化的技术革命
图像Token化作为现代生成式AI系统的核心技术,长期面临对抗性训练不稳定、潜在空间冗余等挑战。斯坦福大学李飞飞与吴佳俊团队提出的FlowMo(Flow towards Modes)创新性地融合模式搜索与扩散模型,在多个关键维度突破传统方法局限&am…...
mac brew 安装的php@7.4 打开redis扩展
1. 找到php7.4的pecl目录 一般在这个位置 cd /usr/local/Cellar/php7.4/7.4.33_8/pecl/20190902 ls 一下 有个 redis.so 于是 直接去php.ini编辑了 php.ini的路径 vim /usr/local/etc/php/7.4/php.ini 把938行添加进去 然后重启一下 php7.4 brew services restart ph…...
OSPF多区域通信
作业要求: 1、多区域0SPF area 0、area10、are20 2、AR5、AR6作为stub区,使用环回接口与Pc1进行通信 第一步:为各端口配置IP地址 AR1: <Huawei>sys [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 5.5.5.1 24 [Huawei-GigabitEther…...
C++模板编程与元编程面试题及参考答案(精选100道题)
目录 解释 C++ 模板的实例化过程,显式实例化与隐式实例化的区别 模板函数在不同翻译单元中的 ODR(单一定义规则)问题 模板参数推导失败的可能场景及解决方法 模板函数中 auto 返回类型的推导规则 如何限制模板函数仅接受特定类型的参数?(非 C++20 概念场景) 函数模板…...
括弧匹配检验(信息学奥赛一本通-1354)
【题目描述】 假设表达式中允许包含两种括号:圆括号和方括号,其嵌套的顺序随意,如([ ]())或[([ ][ ࿳…...
三、重学C++—C语言内存管理
上一章节: 二、重学C—C语言核心-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/146191640?spm1001.2014.3001.5502 本章节代码: cPart2 CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/tree/…...
算法题(105):小猫爬山
审题: 本题需要我们找出将n个小猫放在有限重的缆车上运下山所需的最小缆车数 时间复杂度分析:本题的数据量小于等于18,所以我们在做好剪枝的前提下可以使用深度优先搜索解题 思路: 方法一:dfs 搜索策略:将小…...
C语言-适配器模式详解与实践
文章目录 C语言适配器模式详解与实践1. 什么是适配器模式?2. 为什么需要适配器模式?3. 实际应用场景4. 代码实现4.1 UML 关系图4.2 头文件 (sensor_adapter.h)4.3 实现文件 (sensor_adapter.c)4.4 使用示例 (main.c) 5. 代码分析5.1 关键设计点5.2 实现特…...
线程的pthread_create、pthread_join、pthread_exit、pthread_detach函数
线程的创建(pthread_create) pthread_t tid;//本质是unsigned long类型,打印时得到的是该线程的虚拟地址int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg ); pthread_t *thre…...
测试专项4:AI算法测试在测试行业中,该如何定位自己自述
这岗位到底干啥的? 打个比方: 你就像AI模型的“质检员产品经理风险顾问”三合一。 质检员: 别人造了个AI模型(比如人脸识别系统),你不能光看它实验室成绩好,得把它丢到现实里折腾:…...
QT-LINUX-Bluetooth蓝牙开发
BlueToothAPI QT-BlueToothApi Qt Bluetooth 6.8.2 官方提供的蓝牙API不支持linux。 D-Bus的API实现蓝牙 确保系统中安装了 BlueZ(版本需≥5.56),并且 Qt 已正确安装并配置了 D-Bus 支持。 默默看了下自己的版本.....D-BUS的API也不支持。 在 D-Bus 中,org 目录是 D-Bus…...
【经验总结】AUTOSAR架构下NvMBlock无效问题分析
目录 前言 正文 1.问题描述 2.问题原因 3.深入分析 3.1NvM_InvalidateNvBlock分析 3.2NvBlock无效后NvM_ReadBlock行为分析 3.3NvBlock无效后NvM_WriteBlock行为分析 4.总结 前言 最近在做所有NvMBlock测试的时候,发现一个NvMBlock始终无法测试成功(写入Block值 --&…...
STM32 的tf卡驱动
基于STM32的TF卡驱动的基本实现步骤和相关代码示例,主要使用SPI接口来与TF卡进行通信。 硬件连接 将TF卡的SPI接口与STM32的SPI引脚连接,通常需要连接SCK(时钟)、MOSI(主出从入)、MISO(主入从出)和CS(片选)引脚。 软件实现 初始化SPI 配置SPI的工作模式、时钟频率…...
stress-ng命令详解
stress-ng 是一款功能强大的 Linux 系统压力测试工具,能够模拟多种复杂负载场景,覆盖 CPU、内存、磁盘 I/O、进程调度等核心资源,帮助开发者验证系统在高负载下的稳定性与性能表现。以下是其核心功能、参数解析及实战案例。 一、工具简介与安…...
【C语言系列】数据在内存中存储
数据在内存中存储 一、整数在内存中的存储二、大小端字节序和字节序判断2.1什么是大小端?2.2练习2.2.1练习12.2.2练习22.2.3练习32.2.4练习42.2.5练习52.2.6练习6 三、浮点数在内存中的存储3.1练习3.2浮点数的存储3.2.1 浮点数存的过程3.2.2 浮点数取的过程 3.3题目…...
【中文翻译】第12章-The Algorithmic Foundations of Differential Privacy
由于GitHub项目仅翻译到前5章,我们从第6章开始通过大语言模型翻译,并导出markdown格式。 大模型难免存在错漏,请读者指正。 教材原文地址:https://www.cis.upenn.edu/~aaroth/Papers/privacybook.pdf 12 其他模型 到目前为止&…...
图解模糊推理过程(超详细步骤)
我们前面已经讨论了三角形、梯形、高斯型、S型、Z型、Π型6种隶属函数,下一步进入模糊推理阶段。 有关六种隶属函数的特点在“Pi型隶属函数(Π-shaped Membership Function)的详细介绍及python示例”都有详细讲解:https://lzm07.b…...
datawhale组队学习-大语言模型-task5:主流模型架构及新型架构
目录 5.3 主流架构 5.3.1 编码器-解码器架构 5.3.2 因果解码器架构 5.3.3 前缀解码器架构 5.4 长上下文模型 5.4.1 扩展位置编码 5.4.2 调整上下文窗口 5.4.3 长文本数据 5.5 新型模型架构 5.5.1 参数化状态空间模型 5.5.2 状态空间模型变种 5.3 主流架构 在预训…...
为什么后端路由需要携带 /api 作为前缀?前端如何设置基础路径 /api?
一、为什么后端路由需要携带 /api 作为前缀? 1. 区分 API 端点与其他路由 在 Web 应用程序中,后端不仅需要处理 API 请求,还可能需要处理静态资源(如 HTML、CSS、JS 文件)或其他服务(如 WebSocket&#x…...
C++ 关系运算符重载和算术运算符重载的例子,运算符重载必须以operator开头
在C中,运算符重载允许为用户定义的类型(类或结构体)赋予某些内置运算符的功能。下面是一个关于关系运算符重载()和算术运算符重载()的简单例子。 示例:复数类的运算符重载 将创建一…...
建造者模式 (Builder Pattern)
建造者模式 (Builder Pattern) 是一种创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 一、基础 1.1 意图 将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 1.2 适用场景 当创建复杂对象的算法应该…...
MCU vs SoC
MCU(Microcontroller Unit,单片机)和SoC(System on Chip,片上系统)是两种不同的芯片类型,尽管它们都实现了高度集成,但在设计目标、功能复杂性和应用场景上存在显著差异。以下是两者…...
RAG 架构地基工程-Retrieval 模块的系统设计分享
目录 一、知识注入的关键前奏——RAG 系统中的检索综述 (一)模块定位:连接语言模型与知识世界的桥梁 (二)核心任务:四大关键问题的协调解法 (三)系统特征:性能、精度…...
(C语言)习题练习 sizeof 和 strlen
sizeof 上习题,不知道大家发现与上一张的习题在哪里不一样嘛? int main() {char arr[] "abcdef";printf("%zd\n", sizeof(arr));printf("%zd\n", sizeof(arr 0));printf("%zd\n", sizeof(*arr));printf(&…...
Unity Animation的其中一种运用方式
Animation是Unity的旧的动画系统,先说目的,其使用是为了在UI中播放动效,并且在动效播放结束后接自定义事件而设计的 设计的关键点在于,这个脚本不是通过Animation直接播放动画片段,而是通过修改AnimationState的nor…...
湖北楚大夫
品牌出海已成为众多企业拓展业务、提升竞争力的关键战略。楚大夫(chudafu.com)作为一家专注于品牌出海、海外网络营销推广以及外贸独立站搭建的公司,凭借其专业、高效、创新的服务模式,致力于成为中国企业走向国际市场的坚实后盾与得力伙伴。楚大夫通过综…...
框架的CVE漏洞利用 php类 java类 手工操作和自动化操作蓝队分析漏洞利用的流量特征
前言 php重要框架和基本的识别特征 php的主要是 tp框架 和 laravel 当然还有 yii等 tp的主要特征 1\报错信息: 2、图标 3、请求头 Laravel特征 1、报错信息 2、请求头 php框架CVE利用 lavarvel 工具 https://github.com/zhzyker/CVE-2021-3129 https://git…...
