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

linux 内核 红黑树接口说明

红黑树(rbtree)在linux内核中使用非常广泛,cfs调度任务管理,vma管理等。本文不会涉及关于红黑树插入和删除时的各种case的详细描述,感兴趣的读者可以查阅其他资料。本文主要聚焦于linux内核中经典rbtree和augment-rbtree操作接口的说明。

1、基本概念

二叉树:每个结点最多2棵子树,无其它限制了。

二叉查找树(二叉排序树/二叉搜索树):首先它是二叉树,左子树上所有结点的值小于它根结点的值,右子树上所有结点的值大于它根结点的值(递归定义).

二叉平衡树:也称为平衡二叉树,它是"平衡二叉搜索树"的简称。首先它是"二叉搜索树",其次它是平衡的,即它的每一个结点的左子树的高度和右子树的高度差至多为1。

红黑树性质:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。除二叉查找树强制一般要求以外,对于任何有效的红黑树增加了如下的额外要求:

性质1. 节点是红色或黑色。

性质2. 根是黑色。

性质3. 所有叶子都是黑色(叶子是NULL节点)。

性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

 * red-black trees properties:  http://en.wikipedia.org/wiki/Rbtree

 *

 *  1) A node is either red or black

 *  2) The root is black

 *  3) All leaves (NULL) are black

 *  4) Both children of every red node are black

 *  5) Every simple path from root to leaves contains the same number of black nodes.

 *

 *  4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two

 *  consecutive red nodes in a path and every red node is therefore followed by

 *  a black. So if B is the number of black nodes on every simple path (as per

 *  5), then the longest possible path due to 4 is 2B.

 *

 *  We shall indicate color with case, where black nodes are uppercase(大写字母) and red nodes will be lowercase(小写字母).

 *  Unknown color nodes shall be drawn as red within parentheses and have some accompanying text comment.

linux内核中的红黑树分为两类,一类是经典的红黑树,用于存放key/value键值对,另一类是增强型红黑树(VMA是内核中典型的augment-rbtree)。

增强型rbtree是一种在每个节点中存储了“一些”额外数据的rbtree,其中节点N的额外数据必须是根为N的子树中所有节点内容的函数。

这些数据可用于为rbtree增加一些新功能。增强rbtree是建立在基本rbtree基础设施之上的可选功能。

需要此特性的rbtree用户在插入和删除节点时必须使用用户提供的增强回调调用增强函数。

注意内核红黑树的实现将部分工作留给了用户来实现:用户需要编写自己的树搜索和插入函数调用所提供的rbtree函数,锁也留给rbtree代码的用户。

2、数据结构

/*linux内核中,rbtree作为通用数据结构类似链表是嵌入到用户数据结构内部,在用户数据结构中存放自己的数据*/
struct rb_node {/*父节点,由于struct rb_node是long对齐,所以其地址低3-0bit或7-0bit未使用,低2位被用来作为颜色标志使用*/unsigned long  __rb_parent_color;struct rb_node *rb_right; /*右子树*/struct rb_node *rb_left;  /*左子树*/
} __attribute__((aligned(sizeof(long))));
/* The alignment might seem pointless, but allegedly CRIS needs it */注意,struct rb_node为long字节对齐,其地址最少也是4字节对齐,所以其成员__rb_parent_color用于存放其parent的地址,同时低2bit可以存放自身的----颜色属性。/*根节点*/
struct rb_root {struct rb_node *rb_node;
};/*节点颜色,默认插入节点为红色*/
#define	RB_RED		0
#define	RB_BLACK		1/*父节点地址, &~3 去掉颜色标志位*/
#define rb_parent(r)   		((struct rb_node *)((r)->__rb_parent_color & ~3))#define RB_ROOT		(struct rb_root) { NULL, }
#define RB_ROOT_CACHED 	(struct rb_root_cached) { {NULL, }, NULL }#define __rb_parent(pc)    ((struct rb_node *)(pc & ~3))/*pc节点的颜色*/
#define __rb_color(pc)     ((pc) & 1)
#define __rb_is_black(pc)  __rb_color(pc)
#define __rb_is_red(pc)    (!__rb_color(pc))/*rb->__rb_parent_color的颜色*/
#define rb_color(rb)       __rb_color((rb)->__rb_parent_color)
#define rb_is_red(rb)      __rb_is_red((rb)->__rb_parent_color)
#define rb_is_black(rb)    __rb_is_black((rb)->__rb_parent_color)/*返回内嵌struct rb_node的数据结构*/
#define	rb_entry(ptr, type, member) container_of(ptr, type, member)#define RB_EMPTY_ROOT(root)  (READ_ONCE((root)->rb_node) == NULL)/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
#define RB_EMPTY_NODE(node)  \((node)->__rb_parent_color == (unsigned long)(node))/*注意,这里是赋值操作*/
#define RB_CLEAR_NODE(node)  \((node)->__rb_parent_color = (unsigned long)(node))

3、接口说明

3.1、rbtree插入红黑树节点

3.1.1、经典rbtree插入红黑树节点

在将数据插入rbtree之前,需要用户实现查找函数,查找插入节点应该插入到rbtree root中的位置,建立链接后,才能将其插入到root中;

系统无法知道用户数据存放规则,将节点存放到rbtree中的位置的查找工作交给用户来处理。

通过rb_link_node(...)接口设置node要被插入到parent下面,建立位置链接关系
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,struct rb_node **rb_link)
{/*设置node__rb_parent_color的值,颜色属性为红色*/node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL;*rb_link = node;
}在树中插入数据包括首先搜索插入新节点的位置,然后插入节点并重新平衡(“重新上色”)树。
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{__rb_insert(node, root, dummy_rotate);
}
节点插入的工作交给__rb_insert来处理。下面是__rb_insert函数原型:
static __always_inline void __rb_insert(struct rb_node *node, struct rb_root *root,void (*augment_rotate)(struct rb_node *old, struct rb_node *new))其中augment_rotate函数指针传入旋转回调函数,经典红黑树中未使用,传入哑旋转回调函数dummy_rotate;经典红黑树只是存储节点之间的顺序关系,无其他"额外"信息,所以其struct rb_augment_callbacks 增强回调函数全部实现为空;
/** Non-augmented rbtree manipulation functions.(非增强红黑树操作功能函数)** We use dummy augmented callbacks here, and have the compiler optimize them* out of the rb_insert_color() and rb_erase() function definitions.*/static inline void dummy_propagate(struct rb_node *node, struct rb_node *stop) {}
static inline void dummy_copy(struct rb_node *old, struct rb_node *new) {}
static inline void dummy_rotate(struct rb_node *old, struct rb_node *new) {}static const struct rb_augment_callbacks dummy_callbacks = {dummy_propagate, dummy_copy, dummy_rotate
};/** Please note - only struct rb_augment_callbacks and the prototypes for* rb_insert_augmented() and rb_erase_augmented() are intended to be public.* The rest are implementation details you are not expected to depend on.** See Documentation/rbtree.txt for documentation and samples.*/struct rb_augment_callbacks {void (*propagate)(struct rb_node *node, struct rb_node *stop);void (*copy)(struct rb_node *old, struct rb_node *new);void (*rotate)(struct rb_node *old, struct rb_node *new);
};
对于augment-rbtree(增强红黑树)rb_augment_callbacks的定义可以通过下面的宏来实现;
/*这个宏定义的内容比较长,定义了augment回调函数接口以及对应的struct rb_augment_callbacks rbname 结构体*/
#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield,	\rbtype, rbaugmented, rbcompute)		\
static inline void							\
rbname ## _propagate(struct rb_node *rb, struct rb_node *stop)		\
{									\while (rb != stop) {						\rbstruct *node = rb_entry(rb, rbstruct, rbfield);	\rbtype augmented = rbcompute(node);			\if (node->rbaugmented == augmented)			\break;						\node->rbaugmented = augmented;				\rb = rb_parent(&node->rbfield);				\}								\
}									\
static inline void							\
rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new)		\
{									\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);		\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);		\new->rbaugmented = old->rbaugmented;				\
}									\
static void								\
rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new)	\
{									\rbstruct *old = rb_entry(rb_old, rbstruct, rbfield);		\rbstruct *new = rb_entry(rb_new, rbstruct, rbfield);		\new->rbaugmented = old->rbaugmented;				\old->rbaugmented = rbcompute(old);				\
}									\
rbstatic const struct rb_augment_callbacks rbname = {			\rbname ## _propagate, rbname ## _copy, rbname ## _rotate	\
};

3.1.2、augment-rbtree(增强红黑树)插入红黑树节点

在插入时,用户必须更新通向插入节点的路径上的增强信息,然后像往常一样调用rb_link_node()和rb_augment_inserted(),而不是通常的rb_insert_color()调用。

如果rb_augment_inserts()重新平衡了rbtree,它将回调为用户提供的函数,以更新受影响子树上的增强信息。

/** Fixup the rbtree and update the augmented information when rebalancing.** On insertion, the user must update the augmented information on the path* leading to the inserted node, then call rb_link_node() as usual and* rb_augment_inserted() instead of the usual rb_insert_color() call.* If rb_augment_inserted() rebalances the rbtree, it will callback into* a user provided function to update the augmented information on the* affected subtrees.*/
static inline void rb_insert_augmented(struct rb_node *node, struct rb_root *root,const struct rb_augment_callbacks *augment)
{__rb_insert_augmented(node, root, augment->rotate);
}/** Augmented rbtree manipulation functions.** This instantiates the same __always_inline functions as in the non-augmented* case, but this time with user-defined callbacks.*/void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,void (*augment_rotate)(struct rb_node *old, struct rb_node *new))
{__rb_insert(node, root, augment_rotate);
}

和经典红黑树插入节点操作一样,最后的后都是留给__rb_insert来处理的。区别在于需要提供augmet->rotate的实现。

3.2、rbtree删除红黑树节点

3.2.1、经典rbtree删除红黑树节点

void rb_erase(struct rb_node *node, struct rb_root *root)
{struct rb_node *rebalance;rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);if (rebalance)____rb_erase_color(rebalance, root, dummy_rotate);
}/* Fast replacement of a single node without remove/rebalance/add/rebalance */
void rb_replace_node(struct rb_node *victim, struct rb_node *new,struct rb_root *root)
{struct rb_node *parent = rb_parent(victim);/* Set the surrounding nodes to point to the replacement */__rb_change_child(victim, new, parent, root);if (victim->rb_left)rb_set_parent(victim->rb_left, new);if (victim->rb_right)rb_set_parent(victim->rb_right, new);/* Copy the pointers/colour from the victim to the replacement */*new = *victim;
}

3.2.2、augment-rbtree(增强红黑树)删除红黑树节点

当擦除节点时,用户必须调用rb_erase_augmented()而不是rb_erase()。Rb_erase_augmented()回调用户提供的函数来更新受影响子树上的增强信息。

static __always_inline void rb_erase_augmented(struct rb_node *node, struct rb_root *root,const struct rb_augment_callbacks *augment)
{struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);if (rebalance)__rb_erase_color(rebalance, root, augment->rotate);
}	

3.3、rbtree节点遍历

/*如果rbtree中的节点是按顺存放的话,rb_first返回最小值节点*/

struct rb_node *rb_first(const struct rb_root *root)
{struct rb_node	*n;n = root->rb_node;if (!n)return NULL;while (n->rb_left)n = n->rb_left;return n;
}/*如果rbtree中的节点是按顺存放的话,rb_last返回最大值节点*/
struct rb_node *rb_last(const struct rb_root *root)
{struct rb_node	*n;n = root->rb_node;if (!n)return NULL;while (n->rb_right)n = n->rb_right;return n;
}/*如果rbtree中的节点是按顺存放的话,rb_next返回值比node节点值大的节点*/
struct rb_node *rb_next(const struct rb_node *node)
{struct rb_node *parent;if (RB_EMPTY_NODE(node))return NULL;/** If we have a right-hand child, go down and then left as far* as we can.*/if (node->rb_right) { /*node右子树上的值都比node大*/node = node->rb_right;while (node->rb_left) /*一直寻找左子树*/node=node->rb_left;return (struct rb_node *)node;}/** No right-hand children. Everything down and left is smaller than us,* so any 'next' node must be in the general direction of our parent.* Go up the tree; any time the ancestor is a right-hand child of its* parent, keep going up. First time it's a left-hand child of its* parent, said parent is our 'next' node.*//*node无右子树且node的parent存在:1、如果node为parent的左节点,则返回parent(parent比node大);2、node为其parent的右节点(parent比node小),则继续递归往上找(如果一直为右节点,表明node是以当前parent为root的这棵子树上的最大值),直到找到node为parent的左节点时返回其parent(parent比左子树所以节点都大);*/while ((parent = rb_parent(node)) && node == parent->rb_right)node = parent;return parent; /*这里返回的是parent*/
}/*如果rbtree中的节点是按顺存放的话,rb_next返回值比node节点值小的节点*/
struct rb_node *rb_prev(const struct rb_node *node)
{struct rb_node *parent;if (RB_EMPTY_NODE(node))return NULL;/** If we have a left-hand child, go down and then right as far* as we can.*/if (node->rb_left) {  /*node左子树上的值都比node小*/node = node->rb_left; while (node->rb_right) /*一直找右子树*/node=node->rb_right;return (struct rb_node *)node;}/** No left-hand children. Go up till we find an ancestor which* is a right-hand child of its parent.*//*node无左子树且node的parent存在:1、如果node为parent的右节点,则返回parent(parent比node小);	2、node为其parent的左节点(parent比node大),则继续递归往上找,(如果一直为左节点,表明node是以当前parent为root的这棵子树上的最小值),直到找到node为parent的右节点时返回其parent(parent比右子树所以节点都小);*/while ((parent = rb_parent(node)) && node == parent->rb_left)node = parent;return parent; /*这里返回的是parent*/
}

上面四个宏可以用于遍历红黑树中的节点:

for (node = rb_first(&mytree); node; node = rb_next(node)){

...
}

相关文章:

linux 内核 红黑树接口说明

红黑树(rbtree)在linux内核中使用非常广泛,cfs调度任务管理,vma管理等。本文不会涉及关于红黑树插入和删除时的各种case的详细描述,感兴趣的读者可以查阅其他资料。本文主要聚焦于linux内核中经典rbtree和augment-rbtree操作接口的说明。 1、基本概念 二叉树:每个…...

【ELK】filebeat 和logstash区别

Filebeat 和 Logstash 都是 Elastic Stack (也称为 ELK Stack) 的重要组件,用于日志数据的收集、处理和传输。它们有不同的功能和使用场景: Filebeat 角色: 轻量级日志收集器。功能: 从指定的日志文件中读取日志数据。可以从多个源(如文件、…...

CNN -1 神经网络-概述

CNN -1 神经网络-概述 一:芯片科技发展介绍了解1> 芯片科技发展趋势2> 芯片使用领域3> 芯片介绍1. 神经网络芯片2. 神经网络处理单元NPU(Neural Processing Unit)二:神经网络1> 什么是神经网络2> 神经元3> 人工神经网络三:卷积神经网络(CNN)入门讲解一…...

插片式远程IO模块:Profinet总线耦合器在STEP7配置

XD9000是Profinet总线耦合器,单个耦合器最多可扩展32个I/O模块!本文将深入探讨插片式远程IO模块的应用,并揭秘Profinet总线耦合器在STEP7配置过程中的技巧与注意事项。 STEP7-MicroWINSMART软件组态步骤: 1、按照下图指示安装GSD…...

python3读取shp数据

目录 1 介绍 1 介绍 需要tmp.shp文件和tmp.dbf文件,需要安装geopandas第三方库,python3代码如下, import geopandas as gpdshp_file_path "tmp.shp" shp_data gpd.read_file(shp_file_path) for index, row in shp_data.iterro…...

pytorch实现水果2分类(蓝莓,苹果)

1.数据集的路径,结构 dataset.py 目的: 输入:没有输入,路径是写死了的。 输出:返回的是一个对象,里面有self.data。self.data是一个列表,里面是(图片路径.jpg,标签&…...

Redis实践经验

优雅的Key结构 Key实践约定: 遵循基本格式:[业务名称]:[数据名]:id例:login:user:10长度步超过44字节(版本不同,上限不同)不包含特殊字符 优点: 可读性强避免key冲突方便管理节省内存&#x…...

分类题解清单

目录 简介MySQL题一、聚合函数二、排序和分组三、高级查询和连接四、子查询五、高级字符串函数 / 正则表达式 / 子句 算法题一、双指针二、滑动窗口三、模拟四、贪心五、矩阵六、排序七、链表八、设计九、前缀和十、哈希表十一、字符串十二、二叉树十三、二分查找十四、回溯十五…...

QUdpSocket 的bind函数详解

QUdpSocket 是 Qt 框架中用于处理 UDP 网络通信的类。bind 函数是此类中的一个重要方法,它用于将 QUdpSocket 对象绑定到一个特定的端口上,以便在该端口上接收 UDP 数据包。 函数原型 在 Qt 中,bind 函数的原型通常如下所示: b…...

[spring] Spring MVC - security(下)

[spring] Spring MVC - security(下) callback 一下,当前项目结构如下: 这里实现的功能是连接数据库,大范围和 [spring] rest api security 重合 数据库连接 - 明文密码 第一部分使用明文密码 设置数据库 主要就是…...

数据库数据恢复—SQL Server数据库由于存放空间不足报错的数据恢复案例

SQL Server数据库数据恢复环境: 某品牌服务器存储中有两组raid5磁盘阵列。操作系统层面跑着SQL Server数据库,SQL Server数据库存放在D盘分区中。 SQL Server数据库故障: 存放SQL Server数据库的D盘分区容量不足,管理员在E盘中生…...

spring security的demo

参考: https://juejin.cn/post/6844903502003568647 Spring Security 5.7.0弃用 WebSecurityConfigurerAdapter-CSDN博客 创建 Spring Security 配置类 WebSecurityConfigurerAdapter已被弃用 package com.cq.sc.security.config;import org.springframework.c…...

无需构建工具,快速上手Vue2 + ElementUI

无需构建工具,快速上手Vue2 ElementUI 在前端开发的世界中,Vue.js以其轻量级和易用性赢得了开发者的青睐。而Element UI,作为一个基于Vue 2.0的桌面端组件库,提供了丰富的界面组件,使得构建美观且功能丰富的应用变得…...

通信协议_Modbus协议简介

概念介绍 Modbus协议:一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De f…...

LabVIEW优化氢燃料电池

太阳能和风能的发展引入了许多新的能量储存方法。随着科技的发展,能源储存和需求平衡的方法也需要不断创新。智慧城市倡导放弃石化化合物,采用环境友好的发电和储能技术。氢气系统和储存链在绿色能源倡议中起着关键作用。然而,氢气密度低&…...

SpringCloudGateway

作用 统一管理,易于监控安全,限流:在网关层就过滤掉非法信息nginx外部网关,gateway内网nginx可以使用Lua或Kong来增强 概念 id:名称随意uri: 被代理的服务地址。id和uri必填,谓词和过滤器非必填谓词:可以…...

Wireshark 对 https 请求抓包并展示为明文

文章目录 1、目标2、环境准备3、Wireshark 基本使用4、操作步骤4.1、彻底关闭 Chrome 进程4.2、配置 SSLKEYLOGFILE [核心步骤]4.3、把文件路径配置到 Wireshark 指定位置4.4、在浏览器发起请求4.5、抓包配置4.6、过滤4.6.1、过滤域名 http.host contains "baidu.com4.6.2…...

如何在Ubuntu环境下使用加速器配置Docker环境

一、安装并打开加速器 这个要根据每个加速器的情况来安装并打开,一般是会开放一个代理端口,比如1087 二、安装Docker https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script 三、配置Docker使用加速器 3.1 修改配置…...

2.5 C#视觉程序开发实例1----CamManager实现模拟相机采集图片

2.5 C#视觉程序开发实例1----CamManager实现模拟相机采集图片 1 目标效果视频 CamManager 2 CamManager读取本地文件时序 3 BD_Vision_Utility添加代码 3.0 导入链接库 BD_OperatorSets.dllSystem.Windows.Forms.dllOpencvSharp 3.1 导入VisionParam中创建的文件Util_FileO…...

算法简介:什么是算法?——定义、历史与应用详解

引言 在现代计算机科学中,算法是一个核心概念。无论是编程还是数据分析,算法都扮演着至关重要的角色。在这篇博客中,我们将深入探讨算法的定义、历史背景以及它在计算机科学中的地位和实际应用。 什么是算法? 算法是解决特定问题…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

微信小程序云开发平台MySQL的连接方式

注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...