linux驱动编程 - kfifo先进先出队列

简介:

        kfifo是Linux Kernel里面的一个 FIFO(先进先出)数据结构,它采用环形循环队列的数据结构来实现,提供一个无边界的字节流服务,并且使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证kfifo的线程安全。
        FIFO主要用于缓冲速度不匹配的通信。

下面图解kfifo工作过程:

蓝色表示kfifo剩余空间,红色表示kfifo已占用空间

1)空的kfifo

2)put数据到buffer后

3)从buffer中get数据后

4)当此时put到buffer中的数据长度超出in到末尾长度时,则将剩下的移到头部去

注意,kfifo如果只有一个写入者,一个读取者,是不需要锁的。但是多对一的情况,多的那方需要上锁。如:多个写入者,一个读取者,需对写入者上锁。 反之,如果有多个读取者,一个写入者,需对读取者上锁。

一、kfifo常用函数介绍

Linux内核中的路径:lib/kfifo.c、include/linux/kfifo.h

头文件:#include <linux/kfifo.h>

常用函数 / 宏功能
DECLARE_KFIFO_PTR(fifo, type)定义一个名字为fifo,element类型为type,其数据需要kfifo_alloc动态分配
DECLARE_KFIFO(fifo, type, size)定义一个名字为fifo,element类型为type,element个数为size,其数据静态存储在结构体中,size需为常数且为2的整数次方
INIT_KFIFO(fifo)初始化DECLARE_KFIFO接口定义的fifo
DEFINE_KFIFO(fifo, type, size)定义并初始化fifo
kfifo_initialized(fifo)fifo是否初始化
kfifo_recsize(fifo)返回fifo的recsize
kfifo_size(fifo)返回fifo的size
kfifo_reset(fifo)将in和out置0,注意:需要上锁
kfifo_reset_out(fifo)设置out=in,由于只修改out,因此在读者上下文,且只有一个读者时,是安全的。否则需要上锁。
kfifo_len(fifo)返回fifo的总size
kfifo_is_empty(fifo)fifo是否为空 (in == out)
kfifo_is_full(fifo)fifo是否满
kfifo_avail(fifo)获取队列的空闲空间长度
kfifo_skip(fifo)跳过一个element
kfifo_peek_len(fifo)获取下一个element的字节长度。
kfifo_alloc(fifo, size, gfp_mask)为指针式FIFO分配空间并初始化,成功返回0,错误则返回负数错误码
kfifo_free(fifo)释放kfifo_alloc分配的内存
kfifo_init(fifo, buffer, size)用户自己申请缓存,然后传递给fifo进行初始化,成功返回0,错误则返回负数错误码
kfifo_put(fifo, val)这是一个宏,将val赋值给一个FIFO type类型的临时变量,然后将临时变量入队。存放一个element,如果成功返回入队的elements个数。如果FIFO满,则返回0。
kfifo_get(fifo, val)

val是一个指针,内部将val赋值给一个ptr指针类型的临时变量,并拷贝sizeof(*ptr)长度到val的地址。拷贝一个element。
如果FIFO为空,返回0,否则返回拷贝的element数。

kfifo_peek(fifo, val)和kfifo_get相同,除了不更新out外。
kfifo_in(fifo, but, n)入队n个elemnts。返回工程入队的elements数。
kfifo_in(fifo, buf, n, lock)加锁入队。加锁方式为spin_lock_irqsave
kfifo_out(fifo, buf, n)出队n个elements,返回成功拷贝的elements数
kfifo_out_spinlocked(fifo, buf, n, lock)加锁出队。加锁方式位spin_lock_irqsave
kfifo_from_user(fifo, from, len, copied)

复制用户空间的数据到kfifo

最多拷贝len个字节,参考record FIFO和非record FIFO的对应底层接口。
kfifo_to_user(fifo, to, len, copied)

复制kfifo中的数据到用户空间

最多拷贝len个字节到用户空间,参考record FIFO和非record FIFO的对应底层接口。
kfifo_out_peek(fifo, buf, n)peek n个elements的数据,但是内部out不动,返回拷贝的elements个数

1、结构体定义

1.1 struct __kfifo 结构体

struct __kfifo {
	unsigned int	in;           //入队指针,指向下一个元素可被插入的位置
	unsigned int	out;          //出队指针,指向即将出队的第一个元素
	unsigned int	mask;         //向上扩展成2的幂queue_size-1
	unsigned int	esize;        //每个元素的大小,单位为字节
	void		*data;            //存储数据的缓冲区
};

下图可以直观的表示各结构体成员之间的关系:

1.2 struct kfifo 结构体


#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
	union { \
		struct __kfifo	kfifo; \
		datatype	*type; \
		const datatype	*const_type; \
		char		(*rectype)[recsize]; \
		ptrtype		*ptr; \
		ptrtype const	*ptr_const; \
	}

#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \
{ \
	__STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
	type		buf[0]; \
}

struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);

kfifo结构体展开后格式如下:

struct kfifo
{
    union
    {
        struct __kfifo    kfifo;            //__kfifo是kfifo的成员
        unsigned char        *type;
        const unsigned char  *const_type;
        char                 (*rectype)[0];
        void                 *ptr;
        void const           *ptr_const;  
    };
    unsigned char buf[0];
}

kfifo怎么和其它字段是联合的?其它字段读写岂不是会覆盖kfifo的内容。其实这又是内核的一个技巧,其它字段不会读写数据,只是编译器用来获取相关信息 。

比如:

获取recsize:

#define kfifo_recsize(fifo)     (sizeof(*(fifo)->rectype))
通过kfifo_alloc分配buf存储空间时,获取块的大小

__kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : 

2、初始化kfifo

声明kfifo有2种方式:

  • DECLARE_KFIFO_PTR 配合 kfifo_alloc 用于动态申请kfifo;
  • DECLARE_KFIFO 用于静态定义kfifo;
功能相似方法

DECLARE_KFIFO_PTR(fifo, type)

参数:

fifo:要定义的kfifo的名字

type:元素的类型

宏定义一个kfifo指针对象,会设置type buf[]数组的大小为0,因此需配合 kfifo_alloc  动态分配buf的存储空间struct kfifo fifo;

DECLARE_KFIFO(fifo, type, size)

参数:

fifo:要定义的kfifo的名字

type:元素的类型

size:kfifo可容纳的元素个数,必须是2的幂

静态声明一个kfifo对象,设置type buf[] 大小为size、类型为 type 的数组DEFINE_KFIFO

笔者常用到动态申请方式,因此主要介绍动态申请方式。

动态申请除了用 DECLARE_KFIFO_PTR,还能用 struct kfifo 创建结构体,如:

struct kfifo fifo;        #可替代 DECLARE_KFIFO_PTR(fifo, unsigned char)

这种方式可替代 DECLARE_KFIFO_PTR(fifo, unsigned char),它们都用到 __STRUCT_KFIFO_PTR,仅传入的第3个参数不同。

/* struct kfifo结构体定义 */
struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);


/* DECLARE_KFIFO_PTR宏定义 */
#define STRUCT_KFIFO_PTR(type) \
    struct __STRUCT_KFIFO_PTR(type, 0, type)

#define DECLARE_KFIFO_PTR(fifo, type)   STRUCT_KFIFO_PTR(type) fifo

2.1 动态申请

方法一:

struct kfifo fifo = {0};                                //定义一个 struct kfifo 变量

kfifo_alloc(&fifo, 4096, GFP_KERNEL);  //使用 kfifo_alloc 动态申请内存空间,大小为4096

方法二:

DECLARE_KFIFO_PTR(fifo, unsigned char);        //申请

INIT_KFIFO(fifo);                                                    //初始化 

kfifo_alloc(&fifo, 4096, GFP_KERNEL);   //使用 kfifo_alloc 动态申请内存空间,大小为4096

注意:动态分配最后需要调用 kfifo_free 释放

2.2 静态定义

方法一:

DECLARE_KFIFO(fifo, char, 512);        //静态申明,type buf[] 大小为512,类型为char

INIT_KFIFO(fifo);                                   //初始化fifo结构

方法二:

DEFINE_KFIFO(fifo, char, 512)             //同上

3、入队、出队

3.1 kfifo_in

功能:buf中len长度数据写入到fifo中

返回值:实际写入长度

unsigned int __kfifo_in(struct __kfifo *fifo,
		const void *buf, unsigned int len)
{
	unsigned int l;

	l = kfifo_unused(fifo);        //判断kfifo还有多少剩余空间
	if (len > l)
		len = l;

	kfifo_copy_in(fifo, buf, len, fifo->in);    //将数据拷贝到kfifo中
	fifo->in += len;               //设置写入数量+len
	return len;
}

#define	kfifo_in(fifo, buf, n) \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	typeof(__tmp->ptr_const) __buf = (buf); \
	unsigned long __n = (n); \
	const size_t __recsize = sizeof(*__tmp->rectype); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	(__recsize) ?\
	__kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
	__kfifo_in(__kfifo, __buf, __n); \
})

3.2 kfifo_out

功能:从fifo中获取len长度数据到buf中

unsigned int __kfifo_out(struct __kfifo *fifo,
		void *buf, unsigned int len)
{
	len = __kfifo_out_peek(fifo, buf, len);    //fifo输出数据到buf
	fifo->out += len;                          //输出数量+len
	return len;
}

#define	kfifo_out(fifo, buf, n) \
__kfifo_uint_must_check_helper( \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	typeof(__tmp->ptr) __buf = (buf); \
	unsigned long __n = (n); \
	const size_t __recsize = sizeof(*__tmp->rectype); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	(__recsize) ?\
	__kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
	__kfifo_out(__kfifo, __buf, __n); \
}) \
)

4、动态申请、释放内存

4.1 kfifo_alloc

功能:动态申请kfifo内存

返回值:0-成功,其他-失败

int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
		size_t esize, gfp_t gfp_mask)
{
	/*
	 * round up to the next power of 2, since our 'let the indices
	 * wrap' technique works only in this case.
	 */
	size = roundup_pow_of_two(size);    //向上扩展为2的幂

	fifo->in = 0;
	fifo->out = 0;
	fifo->esize = esize;

	if (size < 2) {
		fifo->data = NULL;
		fifo->mask = 0;
		return -EINVAL;
	}

	fifo->data = kmalloc_array(esize, size, gfp_mask);    //动态申请内存

	if (!fifo->data) {
		fifo->mask = 0;
		return -ENOMEM;
	}
	fifo->mask = size - 1;

	return 0;
}

#define kfifo_alloc(fifo, size, gfp_mask) \
__kfifo_int_must_check_helper( \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	__is_kfifo_ptr(__tmp) ? \
	__kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
	-EINVAL; \
}) \
)

注意:保证缓冲区大小为2的次幂,若不是,会向上取整为2的次幂(很重要)

4.2 kfifo_free

功能:释放kfifo动态申请的内存

void __kfifo_free(struct __kfifo *fifo)
{
	kfree(fifo->data);        //释放内存
	fifo->in = 0;
	fifo->out = 0;
	fifo->esize = 0;
	fifo->data = NULL;
	fifo->mask = 0;
}

#define kfifo_free(fifo) \
({ \
	typeof((fifo) + 1) __tmp = (fifo); \
	struct __kfifo *__kfifo = &__tmp->kfifo; \
	if (__is_kfifo_ptr(__tmp)) \
		__kfifo_free(__kfifo); \
})

二、使用方法

使用kfifo的方式有两种,动态申请和静态定义。

3.1 动态申请

动态申请步骤如下:

① 包含头文件 #include <linux/kfifo.h>

② 定义一个 struct kfifo 变量;

③ 使用 kfifo_alloc 申请内存空间;

④ 分别使用 kfifo_in、kfifo_out 执行入队、出队的操作;

⑤ 不再使用kfifo时,使用 kfifo_free 释放申请的内存。

示例:

#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/kfifo.h>

//定义fifo存储结构体
struct member {
    char name[32];
    char val;
};

//定义fifo最大保存的元素个数
#define FIFO_MEMBER_NUM     64

//定义kfifo
static struct kfifo stFifo;

static int __init kfifo_demo_init(void)
{
    int ret = 0;
    int i;

/* 1.申请fifo内存空间,空间大小最好为2的幂 */
    ret = kfifo_alloc(&stFifo, sizeof(struct member) * FIFO_MEMBER_NUM, GFP_KERNEL);
    if (ret) {
        printk(KERN_ERR "kfifo_alloc fail ret = %d\n", ret);
        return;
    }

/* 2.入队 */
    struct member stMember = {0}; 
    for (i = 0; i < FIFO_MEMBER_NUM; i++) {
        snprintf(stMember.name, 32, "name%d", i);
        stMember.val = i;
        ret = kfifo_in(&stFifo, &stMember, sizeof(struct member));
        if (!ret) {
            printk(KERN_ERR "kfifo_in fail, fifo is full\n");
        }
    }

/* 3.出队 */
    for  (i = 0; i < FIFO_MEMBER_NUM; i++) {
        ret = kfifo_out_peek(&stFifo, &stMember, sizeof(struct member));        //读,返回实际读到长度(不修改out)
        ret = kfifo_out(&stFifo, &stMember, sizeof(struct member));             //读,返回实际读到长度(修改out)
        if (ret) {
            printk(KERN_INFO "kfifo_out stMember: name = %s, val=%d\n", stMember.name, stMember.val);
        } else {
            printk(KERN_ERR "kfifo_out fail, fifo is empty\n");
        }
        
        if (kfifo_is_empty(&stFifo)) {        //判断fifo空
            printk(KERN_INFO "kfifo is empty!!!\n");
            break;
        }
    }

/* 4.释放 */
    kfifo_free(&stFifo);
}

static void __exit kfifo_demo_exit(void)
{
    kfifo_free(&stFifo);
}

module_init(kfifo_demo_init);
module_exit(kfifo_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("dong");

测试结果:

这种动态申请方式in、out都是以字节为单位。

3.2 静态定义

静态定义步骤如下:

① 包含头文件 #include <linux/kfifo.h>

② 使用宏 DECLARE_KFIFO 静态定义 fifo 变量;

③ 分别使用 kfifo_put、kfifo_get执行入队、出队的操作;

静态定义不需要申请和释放内存的步骤,出入队函数也更精简。

示例:

#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/kfifo.h>

//定义fifo存储结构体
struct member {
    char name[32];
    char val;
};

//定义fifo最大保存的元素个数,最好为2的幂
#define FIFO_MEMBER_NUM     64

//静态定义已经包含了缓存定义
DECLARE_KFIFO(stFifo, struct member, FIFO_MEMBER_NUM);

static int __init kfifo_demo_init(void)
{
    int ret = 0;
    int i;

/* 1.初始化  */
    INIT_KFIFO(stFifo);

/* 2.入队 */
    struct member stMember = {0}; 
    for (i = 0; i < FIFO_MEMBER_NUM; i++) {
        snprintf(stMember.name, 32, "name%d", i);
        stMember.val = i;
        ret = kfifo_put(&stFifo, stMember);  //注意这里的元素变量名而不是指针
        if (!ret) {
            printk(KERN_ERR "kfifo_put fail, fifo is full\n");
        }
    }

/* 3.出队 */
    for  (i = 0; i < FIFO_MEMBER_NUM; i++) {
        ret = kfifo_get(&stFifo, &stMember); //注意这里传入地址
        if (ret) {
            printk(KERN_INFO "kfifo_get stMember: name = %s, val=%d\n", stMember.name, stMember.val);
        } else {
            printk(KERN_ERR "kfifo_get fail, fifo is empty\n");
        }
        printk(KERN_INFO "kfifo: in = %d, out = %d\n", stFifo.kfifo.in, stFifo.kfifo.out);
        
        if (kfifo_is_empty(&stFifo)) {
            printk(KERN_INFO "kfifo is empty!!!\n");
            break;
        }
    }
}

static void __exit kfifo_demo_exit(void)
{
    return;
}

module_init(kfifo_demo_init);
module_exit(kfifo_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("dong");

测试结果: 

示例中静态定义的in、out是以结构体为单位,64次入队fifo中就会有64个结构体元素。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

黑马的ES课程中的不足

在我自己做项目使用ES的时候&#xff0c;发现了黑马没教的方法&#xff0c;以及一些它项目的小问题 搜索时的匹配方法 这个boolQuery().should 我的项目是通过文章的标题title和内容content来进行搜索 但是黑马它的项目只用了must 如果我们的title和content都用must&#x…

Arc for Windows 无法使用?一篇文章教会你!

&#x1f44b; 大家好&#xff0c;我是 Beast Cheng &#x1f4eb; 联系我&#xff1a;458290771qq.com &#x1f331; 接合作、推广…… 什么是Arc浏览器&#xff1f; Arc浏览器是The Browser Conpany使用Swift语言开发的一款浏览器&#xff0c;Arc浏览器由其漂亮的侧边栏闻名…

HTML5使用<mark>标签:高亮显示文本

1、<mark>标签的使用 mark 标签用于表示页面中需要突出显示或高亮的一段文本&#xff0c;这段文本对于当前用户具有参考作用。它通常在引用原文以引起读者注意时使用。<mark>标签的作用相当于使用一支荧光笔在打印的纸张上标出一些文字。它与强调不同&#xff0c;…

自闭症在生活中的典型表现

自闭症&#xff0c;这个看似遥远却又悄然存在于我们周围的疾病&#xff0c;其影响深远且复杂。在日常生活中&#xff0c;自闭症患者的典型表现往往让人印象深刻&#xff0c;这些表现不仅揭示了他们内心的世界&#xff0c;也提醒我们要以更加包容和理解的心态去面对他们。 首先…

UEC++ 虚幻5第三人称射击游戏(二)

UEC++ 虚幻5第三人称射击游戏(二) 派生榴弹类武器 新建一个继承自Weapon的子类作为派生榴弹类武器 将Weapon类中的Fire函数添加virtual关键字变为虚函数让榴弹类继承重写 在ProjectileWeapon中重写Fire函数,新建生成投射物的模版变量 Fire函数重写逻辑 代码//生成的投射物U…

MySQL中mycat与mha应用

目录 一.Mycat代理服务器 1.Mycat应用场景 2.mycat安装目录结构说明 3.Mycat的常用配置文件 4.Mycat日志 5.mycat 实现读写分离 二.MySQL高可用 1.原理过程 2.MHA软件 3.实现MHA 一.Mycat代理服务器 1.Mycat应用场景 Mycat适用的场景很丰富&#xff0c;以下是几个典型…

大模型的实践应用25-LLama3模型模型的架构原理,以及手把手教你搭建LLama3模型

大家好,我是微学AI,今天给大家介绍一下大模型的实践应用25-LLama3模型模型的架构原理,以及手把手教你搭建LLama3模型。LLaMA 3 是Meta公司开发的最新一代大规模语言模型,其架构在很大程度上继承了LLaMA 2的设计,但对某些关键组件进行了改进和优化。 文章目录 一、LLama3模…

Vue通过Key管理状态

Vue通过Key管理状态 Vue 默认按照“就地更新”的策略来更新&#xff0c;通过 v-for 渲染的元素列表。当数据项的顺序改变时&#xff0c;Vue 不会随之移动 DOM 元素的顺序&#xff0c;而是就地更新每个元素&#xff0c;确保它们在原本指定的索引位置上渲染。为了给 Vue 一个提示…

mupdf加载PDF显示中文乱码

现象 加载PDF显示乱码,提示非嵌入字体 non-embedded font using identity encoding调式 在pdf-font.c中加载字体 调试源码发现pdf文档的字体名字居然是GBK&#xff0c;估计又是哪个windows下写的pdf生成工具生成pdf 字体方法&#xff1a; static pdf_font_desc * load_cid…

STM32利用FreeRTOS实现4个led灯同时以不同的频率闪烁

在没有接触到FreeRTOS时&#xff0c;也没有想过同时叫两个或两个以上的led灯闪烁的想法&#xff0c;接触后&#xff0c;发现如果想叫两个灯同时以不同的频率闪烁&#xff0c;不能说是不可能&#xff0c;就算是做到了也要非常的麻烦。但是学习了FreeRTOS后&#xff0c;发现要想同…

Qt 网络编程实战

一.获取主机的网络信息 需要添加network模块 QT core gui network主要涉及的类分析 QHostInfo类 QHostInfo::localHostName() 获取本地的主机名QHostInfo::fromName(const QString &) 获取指定主机的主机信息 addresses接口 QNetworkInterface类 QNetworkInterfac…

Redis---9---集群(cluster)

将新增的6387节点&#xff08;空槽号&#xff09;作为master节点加入原集群 Redis—9—集群&#xff08;cluster&#xff09; 是什么 定义 ​ 由于数据量过大&#xff0c;单个Master复制集难以承担&#xff0c;因此需要对多个复制集进行集群&#xff0c;形成水平扩展每个复…

电脑f盘的数据回收站清空了能恢复吗

随着信息技术的飞速发展&#xff0c;电脑已成为我们日常生活和工作中不可或缺的设备。然而&#xff0c;数据的丢失或误删往往会给人们带来极大的困扰。尤其是当F盘的数据在回收站被清空后&#xff0c;许多人会陷入绝望&#xff0c;认为这些数据已无法挽回。但事实真的如此吗&am…

【C语言】自定义类型:联合和枚举

前言 前面我们学习了一种自定义类型&#xff0c;结构体&#xff0c;现在我们学习另外两种自定义类型&#xff0c;联合 和 枚举。 目录 一、联合体 1. 联合体类型的声明 2. 联合体的特点 3. 相同成员联合体和结构体对比 4. 联合体大小的计算 5. 用联合体判断当前机…

AI Earth应用—— 在线使用sentinel数据VV和VH波段进行水体提取分析(昆明抚仙湖、滇池为例)

AI Earth 本文的主要目的就是对水体进行提取,这里,具体的操作步骤很简单基本上是通过,首页的数据检索,选择需要研究的区域,然后选择工具箱种的水体提取分析即可,剩下的就交给阿里云去处理,结果如下: 这是我所选取的一景影像: 详情 卫星: Sentinel-1 级别: 1 …

利用redis数据库管理代理库爬取cosplay网站-cnblog

爬取cos猎人 数据库管理主要分为4个模块&#xff0c;代理获取模块&#xff0c;代理储存模块&#xff0c;代理测试模块&#xff0c;爬取模块 cos猎人已经倒闭&#xff0c;所以放出爬虫源码 api.py 为爬虫评分提供接口支持 import requests import concurrent.futures import …

dependencyManagement的作用、nacos的学习

使用SpringCloudAlibaba注意各组件的版本适配 SpringCloudAlibaba已经包含了适配的各组件&#xff08;nacos、MQ等&#xff09;的版本号&#xff0c;也是一个版本仲裁者&#xff0c;但是可能已经有了父项目Spring-Boot-Starter-Parent这个版本仲裁者&#xff0c;又不能加多个父…

Mongodb oplog的作用及如何评估和更改保留时间

作者介绍&#xff1a;老苏&#xff0c;10余年DBA工作运维经验&#xff0c;擅长Oracle、MySQL、PG数据库运维&#xff08;如安装迁移&#xff0c;性能优化、故障应急处理等&#xff09; 公众号&#xff1a;老苏畅谈运维 欢迎关注本人公众号&#xff0c;更多精彩与您分享。oplog …

硅纪元视角 | 国内首款鸿蒙人形机器人“夸父”开启应用新篇章

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…