Linux 操作系统原理 — 内核协议栈收包/发包流程

news2025/6/21 15:36:47

目录

文章目录

  • 目录
  • 关键技术
    • NIC DMA
    • Kernel 中的 sk_buff 与 sk_buffer
    • Kernel 中的 Rx/Tx Ring
    • Buffer Descriptor Table
    • 中断收包机制
    • NAPI 收包机制
    • 多队列网卡
  • 内核协议栈收包/发包流程概览
  • 内核协议栈收包流程详解
    • 驱动程序层(数据链路层)
      • VLAN 协议族
      • Linux Bridge 子系统
    • 网络协议层(L3 子系统)
      • ARP 子系统
      • IP 子系统
    • 网络协议层(L4 子系统)
      • TCP 子系统
    • 协议接口层(BSD Socket 层)
    • 收包流程总览
  • 内核协议栈发包流程详解

关键技术

NIC DMA

DMA(Direct Memory Access,直接内存访问)是一种硬件实现的外设 I/O 技术。

在 DMA 技术出现之前,NIC 和 CPU 之间的 Frames(二层数据帧)收发依赖 CPU 先从 NIC Rx/Tx Queue 逐个 Copy 到 Kernel Space 内存空间,然后再从 Kernel Space 中 Copy 到 Application 的 User Space 中。每个 Frames 的读/写都需要单向的两次 CPU Copy,非常消耗资源。

DMA 技术出现后,NIC 增加了 DMA Controller 功能模块,并将 NIC Rx/Tx Queue 与 Main Memory 中的 ZONE_DMA 建立映射关系。当 Frames 进入 NIC Rx/Tx Queue 之后,DMA Controller 就会将这些 Frames 通过 DMA Copy 的方式存放到 ZONE_DMA 中,期间完全不需要 CPU 的参与。同时因为 Kernel Space 和 ZONE_DMA 是直接物理映射的关系,所以 Kernel Space 可以直接访问这些 Frames。

如此的,通过引入 DMA 技术,在 CPU 读/写报文的单向处理中,就减少了一次 CPU Copy 的工作负载。

在 32bit Linux 中,ZONE_DMA 默认只有 16MB;而在 64bit Linux 中,ZONE_DMA 默认可以有 4GB,得到了非常大的提升。

在这里插入图片描述

Kernel 中的 sk_buff 与 sk_buffer

sk_buff 结构体是 Kernel 定义的一个用于描述 Frame 的数据结构。而 sk_buffer 的本质就是从 ZOME_DMA 中划分给某一个 NIC 的用于存放 sk_buff 实例的内存空间,sk_buffer 会在 NIC Driver 初始化的流程中分配。

当 Frame 到达 NIC 后,DMA Controller 就会将 Frame 的数据 Copy 到 sk_buffer 中的 sk_buff 实例,以此来完成 Frame => sk_buff 数据格式的封装。在后续的流程中,sk_buff 还会被从 ZONE_DMA Copy 到 Kernel Socket Receive Buffer 中,等待 Application 接收。

struct igb_ring {
...
	union {
...
		/* RX */
		struct {
			struct sk_buff *skb;
			struct igb_rx_queue_stats rx_stats;
			struct u64_stats_sync rx_syncp;
		};
...
}

sk_buff 的结构体定义如下图所示,它包含了一个 Frame 的 Interface(网络接口)、Protocol(协议类型)、Headers(协议头)指针、Data(业务数据)指针等信息。

在这里插入图片描述

值得留意的是,在 Kernel 中,一个 Frame 的 Headers 和 Payload 可能是分开存放到不同内存块种的。有以下几点原因:

  1. 两者具有不同的特征和用途:Headers 包含了网络协议的元数据信息,而 Payload 包含了 Application 的业务信息。
  2. 两者具有不同的大小和格式:Headers 的格式通常是标准的,而且数据量比 Payload 小的多。
  3. 两者具有不同的处理逻辑:Headers 需要被快速识别、分析和校验,而 Payload 则需要被快速的传输和存储。

分开存储和处理的方式,可以有效提高网络传输的效率和可靠性。

同时,sk_buff 是一个双向链表数据结构,支持链表操作。

在这里插入图片描述

Kernel 中的 Rx/Tx Ring

sk_buffer 是 Kernel 用于实际存储 sk_buff 的内存空间,而 Rx/Tx Ring 则是 Kernel 用于管理和使用 sk_buffer 内存空间的数据结构。在很多材料中会将 sk_buffer 和 Ring 统称为 Ring Buffer,但实际上两者间有本质的区别。

Ring 是高性能数据包处理场景中常见的一种数据结构,本质是一个首尾相连的 Queue。相较于传统的 FIFO Queue 数据结构,Ring Queue 可以避免频繁的内存分配和数据复制,从而提高传输效率。此外还具有缓存友好、易于并行处理等优势。

Kernel 将 sk_buffer 内存空间设计成一个首尾相连的 Ring。

但需要注意的是,在高速网络环境中,如果 CPU/Driver 的处理能力低于 NIC 带宽,那么 sk_buffer 空间就会溢满,NIC Rx Queue 中 Frames 就会堆积,直到 NIC Rx Queue 也溢满后,NIC 就会开始丢包。所以 CPU/Driver 的报文处理能力,和 NIC 的带宽往往需要匹配得上。

在这里插入图片描述

另外还需要注意的是,Rx/Tx Ring 中存储的是 sk_buff 的 Descriptor,而不是 sk_buff 本身,本质是一个指针,指向存储 sk_buff 的 sk_buffer 物理地址。所以 Ring 上的一个节点,又称为一个 Packet Descriptor。

在这里插入图片描述

Packet Descriptor 有 Ready 和 Used 这 2 种状态。初始时 Descriptor 指向一个预先分配好且是空的 sk_buff 空间,处在 Ready 状态。当有 Frame 到达时,DMA Controller 从 Rx Ring 中按顺序找到下一个 Ready 的 Descriptor,将 Frame 的数据 Copy 到该 Descriptor 指向的 sk_buff 空间中,最后标记为 Used 状态。

在这里插入图片描述

Buffer Descriptor Table

Rx/Tx Ring 的具体实现为一张 Buffer Descriptor Table(BDT)。

BDT 是一个 Table 数据结构,拥有多个 Entries,每条 Entry 都对应了 Rx/Tx Ring 中的一个 Rx/Tx Desc,它们记录了存放 sk_buff 的入口地址、长度以及状态标志位。

在这里插入图片描述

  • 收包时:DMA Controller 搜索 Rx BDT,取出空闲的 DB Entry,将 Frame 存放到 Entry 指向的 sk_buff,修改 Entry 为 Ready。然后 DBT 指针下移一项。
  • 发包时:DMA Controller 搜索 Tx BDT,取出状态为 Ready 的 DB Entry 所指向的 sk_buff 并转化为 Frame 发送出去。然后 DBT 指针下移一项。

在这里插入图片描述

中断收包机制

  • 硬中断(HW Interrupt):是一种 NIC 和 CPU 之间的通信机制,由 NIC 发出,让 CPU 能够及时掌握 NIC 发生的事件,并视乎于中断的类型来决定是否放下当前任务,尽快处理紧急的事件,例如:Frames 的到达。硬件中断的本质是一个 IRQ(中断请求信号)电信号。Kernel 为每个 NIC 分配了一个专属的 HW IRQ Number,以此来区分发出硬中断的设备类型。IRQ Number 又会映射到 Kernel ISR(中断服务路由列表)中的一个中断处理程序(通常由 NIC Driver 提供)。

  • 软中断(SW Interrupt):同样具有由 NIC Driver 提供的软件中断处理程序,例如:收包处理函数。硬件中断处理程序只会处理关键性的、短时间内可以处理完的工作,剩余耗时较长工作,就交由软中断处理函数慢慢完成。

在这里插入图片描述

值得注意的是,硬中断是 Kernel 调度优先级最高的任务类型之一,会进行抢占式调度,所以硬中断通常都伴随着任务切换,即:将 CPU 当前的任务切换到中断处理程序的上下文。

一次中断处理,首先需要将 CPU 的状态寄存器数据保存到虚拟内存空间中的堆栈,然后运行中断服务程序,最后再将状态寄存器数据从堆栈中夹在到 CPU。整个过程需要至少 300 个 CPU 时钟周期。并且在多核处理器计算平台中,每个 Core 都有可能执行硬件中断处理程序,所以还存在着跨 Core 处理要面对的 Cache 一致性流量的问题。

可见,大量的中断处理,尤其是硬件中断处理会非常消耗 CPU 资源。

NAPI 收包机制

为了解决中断收包机制效率低下的问题,Kernel 提出了 NAPI(New API)收包机制。

NAPI(New API)是一种 “中断 + 轮训” 收包机制,在高速网络场景中,只需要一次中断后,再通过轮询的方式进行收包,避免了多次中断的情况。相较于传统的单一中断(硬中断 + 软中断)收包的方式效率更高。

在这里插入图片描述

NAPI 的工作流程如下:

  1. NIC Driver 在初始化流程中,就会注册 NAPI 收包机制所必须的 poll() 轮询函数到 ksoftirqd(软中断处理)内核线程。
  2. Frame 到达 NIC;
  3. DMA Controller 写入 sk_buff;
  4. NIC Controller 发起硬中断,进入 NIC Driver 的 napi_schedule() 硬中断处理函数,然后将一个 napi_struct 结构体加入到 poll_queue(NAPI 软中断队列)中。此时 NIC Controller 立即禁用了硬中断,开始切换到 NAPI “轮训“ 收包工作模式。
  5. 再进入 raise_softirq_irqoff(NET_RX_SOFTIRQ) 软中断处理程序,唤醒 NAPI 子系统,新建 ksoftirqd 内核线程。
  6. ksoftirqd 线程调用 NIC Driver 注册的 poll() 函数。
  7. poll() 调用 netif_receive_skb() 开始从 sk_buffer 内存空间中读取 sk_buff,并将它传递给 TCP/IP 协议栈进行处理。
  8. 直到一段时间内没有 Frame 到达为止,关闭 ksoftirqd 内核线程,NIC Controller 重新切换到 NAPI “中断” 收包工作模式。

在具体的实现中,poll() 会轮训检查 BDT Entries 的状态,如果发现当前 BDT 指针指向的 Entry Ready,则将该 Entry 对应的 sk_buff 取出来,并恢复该 Entry 的空闲状态。

可见,和传统方式相比,NAPI 一次中断就可以接收多个包,因此可以减少硬件中断的次数。

在这里插入图片描述

多队列网卡

在以往,一张 NIC 只会提供一组 Rx/Tx Queue,对应到一个 CPU 来进行处理。在多核时代,NIC 也相应的提供了 Multi-Queue 功能,可以将多个 Queue 通过硬中断绑定到不同的 CPU Cores 上处理。下面以 Intel 82575 为例。

  • 在硬件层面:它拥有 4 组 Rx/Tx Queue,它们的硬中断分别绑定到 4 个 CPU core 上,并通过 RSS(Receive Side Scaling)技术实现负载均衡。RSS 技术通过 HASH Packet Header IP 4-tuple(srcIP、srcPort、dstIP、dstPort),将同一条 Flow 总是送到相同的队列,从而避免了报文乱序问题。
    在这里插入图片描述

  • 在软件层面:Linux Kernel v2.6.21 开始支持多队列网卡特性。在 NIC Driver 初始化流程中,Kernel 获悉 net_device 所支持的 Rx/Tx Queue 得数量。然后结合 CPU Cores 的数量,通过 Sum=Min(NIC Queue, CPU Core) 公式计算得出应该被激活 Sum 个 Queue,并申请 Sum 个硬中断号,分配给激活的每个队列。

在这里插入图片描述

如上图所示,当某个硬件队列收到 Frames 时,就触发相应的硬中断,收到中断的 CPU Core 就中断处理任务下发给该 Core 的 NET_RX_SOFTIRQ 实例处理(每个 Core 都有一个 NET_RX_SOFTIRQ 实例)。

在 NET_RX_SOFTIRQ 中调用 NAPI 的收包接口,将 Frames 收到具有多个 netdev_queue 的 net_device 结构体中。

在这里插入图片描述

NIC 有 2 种常见的硬中断类型:

  1. MSI(Message Signaled Interrupts)是 PCI 规范的一个实现,可以突破 CPU 256 条 interrupt 的限制,使每个设备具有多个中断,多队列网卡驱动给每个硬件队列都申请了 MSI。

  2. MSI-X 中断是比较推荐的方式,尤其是对于支持多队列的网卡。因为每个 RX 队列有独立的 MSI-X 中断,因此可以被不同的 CPU 处理。处理中断的 CPU 也是随后处理这个包的 CPU。这样的话,从网卡硬件中断的层面就可以设置让收到的包被不同的 CPU 处理。

查看 Ethernet Controller 信息。

$ lspci -vvv

在这里插入图片描述

内核协议栈收包/发包流程概览

在这里插入图片描述

  • 收包流程
    在这里插入图片描述

  • 发包流程
    在这里插入图片描述

  • 收包流程
    在这里插入图片描述

  • 发包流程
    在这里插入图片描述

内核协议栈收包流程详解

驱动程序层(数据链路层)

在这里插入图片描述

  1. NIC Controller 接收到高低电信号,表示 Frame 到达。PHY 芯片首先将电信号转换为比特流,然后 MAC 芯片再将比特流转换为 Frame 格式并进行 CRC 校验,然后存入 Rx Queue。

  2. DMA Controller 将 NIC Rx Queue 中的 Frame Copy 到 Kernel 的 Rx Ring 中的一个 Rx Desc 指向的 sk_buff 空间。

  3. DMA Controller 更新相应的 BD Entry 状态为 Ready,并将 BDT 指针下移一项。

  4. NIC Controller 给 CPU 的相关引脚上触发一个电压变化,触发硬中断。每个 IRQ(硬中断请求)都对应一个 IRQ Number,CPU 执行 IRQ Number 对应的硬中断处理程序。硬中断处理程序判断是否开启 NAPI。

  5. 如果开启了 NAPI 模式,那么 NIC Driver 执行 napi_schedule() 硬中断处理函数,然后将一个 napi 结构体加入到 poll_queue(NAPI 软中断队列)中。此时 NIC Controller 立即禁用了硬中断,开始切换到 “NAPI 轮训“ 收包工作模式。

  6. 再进入 raise_softirq_irqoff() 软中断处理程序,唤醒 NAPI 子系统,新建 ksoftirqd 内核线程。

  7. ksoftirqd 线程调用 Net driver 注册的 linux/drivers/net/ethernet/intel/igb/igb_main.c poll() 函数。

/**
 *  igb_poll - NAPI Rx polling callback
 *  @napi: napi polling structure
 *  @budget: count of how many packets we should handle
 **/
static int igb_poll(struct napi_struct *napi, int budget)
{
...
	if (q_vector->tx.ring)
		clean_complete = igb_clean_tx_irq(q_vector, budget);

	if (q_vector->rx.ring) {
		int cleaned = igb_clean_rx_irq(q_vector, budget);
...
}
  1. poll() 调用 linux/net/core/dev.c netif_receive_skb() 开始从 sk_buffer 内存空间中收包,并将它传递给 TCP/IP 网络协议栈进行处理。
/**
 *	netif_receive_skb - process receive buffer from network
 *	@skb: buffer to process
 *
 *	netif_receive_skb() is the main receive data processing function.
 *	It always succeeds. The buffer may be dropped during processing
 *	for congestion control or by the protocol layers.
 *
 *	This function may only be called from softirq context and interrupts
 *	should be enabled.
 *
 *	Return values (usually ignored):
 *	NET_RX_SUCCESS: no congestion
 *	NET_RX_DROP: packet was dropped
 */
int netif_receive_skb(struct sk_buff *skb)
{
	int ret;

	trace_netif_receive_skb_entry(skb);

	ret = netif_receive_skb_internal(skb);
	trace_netif_receive_skb_exit(ret);

	return ret;
}
  1. 直到一段时间内没有 Frame 到达为止,关闭 ksoftirqd 内核线程,NIC Controller 重新切换到硬中断收包工作模式。

在这里插入图片描述

VLAN 协议族

在这里插入图片描述

Linux Bridge 子系统

在这里插入图片描述

网络协议层(L3 子系统)

在这里插入图片描述

  1. NIC Driver 调用 netif_receive_skb() 将 sk_buff 从 sk_buffer 中取出并交给 TCP/IP 协议栈处理的过程中,首先会根据 sk_buff 内层 Header 的 Protocol Type 选择相应的处理函数,如果是 IP 协议,则调用 ip_rcv() 进行处理。

在这里插入图片描述

  1. ip_rcv() 首先会解析 IP Header,根据 srcIP 和 dstIP 进入路由子系统处理流程。如果 dstIP 是本机地址,则根据内层 Header 的 Protocol Type 选择相应的传输层处理函数。UDP 对应 udp_rcv()、TCP 对应 tcp_rcv()、ICMP 对应 icmp_rcv()、IGMP 对应 igmp_rcv()。

ARP 子系统

在这里插入图片描述

IP 子系统

在这里插入图片描述

网络协议层(L4 子系统)

在这里插入图片描述

  1. 在使用 socket() 创建一个 TCP Socket 之后,Socket 对应的 Sock 结构体会被注册到一个 tcp_prot 全局变量中,并以 tcp_port 作为 Index。

  2. 当 tcp_rcv() 收到 sk_buff 之后,根据 TCP Header 中的 dstPort 字段索引到相应的 Sock。

  3. 然后将 sk_buff 加入到该 Sock 的 receive_queue 成员所指向的 Socket Receive Buffer 缓冲队列中,等待 Application 通过 read() 等 SCI 来进行读取。

TCP 子系统

在这里插入图片描述

协议接口层(BSD Socket 层)

当 Application 调用 read() 来进行读取时:

  1. 首先会根据 socket fd 查询 file inode,并从中得到相应的 Sock 结构体;
  2. 然后从 Sock 结构体成员 recieve_queue 指向的 Socket Receive Buffer 发起一次 CPU Copy;
  3. CPU 从用户模式转为内核模式,将数据包从 Kernel space Copy 到 User space。

收包流程总览

在这里插入图片描述

内核协议栈发包流程详解

在这里插入图片描述

以 UDP 数据报为例:

  1. 协议接口层:BSD socket 层的 sock_write() 会调用 INET socket 层的 inet_wirte()。INET socket 层会调用具体传输层协议的 write 函数,该函数是通过调用本层的 inet_send() 来实现的,inet_send() 的 UDP 协议对应的函数为 udp_write()。

  2. L4 子系统:udp_write() 调用本层的 udp_sendto() 完成功能。udp_sendto() 完成 sk_buff 结构体相应的设置和 Header 的填写后会调用 udp_send() 来发送数据。而在 udp_send() 中,最后会调用 ip_queue_xmit() 将数据包下放的网络层。

  3. L3 子系统:函数 ip_queue_xmit() 的功能是将数据包进行一系列复杂的操作,比如是检查数据包是否需要分片,是否是多播等一系列检查,最后调用 dev_queue_xmit() 发送数据。

  4. 驱动程序层:函数调用会调用具体设备提供的发送函数来发送数据包,e.g. hard_start_xmit(skb, dev)。具体设备的发送函数在协议栈初始化的时候已经设置了。

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

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

相关文章

基于MATLAB的语音识别仿真系统

本文实现的语音识别系统,主要是对语音识别的特征参数的提取和识别模型的匹配,进行深入的研究。首先,对语音识别进行了概述,给出了语音识别的系统框架。然后就是如何实现语音识别的问题,这个过程可分为两个部分:第一个是语音特征参…

【Ruby 2D】【unity learn】控制敌人随机运动以及动画控制

前两天考完蓝桥杯稍微休息了一下,昨天做了一个动画控制,但是想到写出来可能会字很多,我就搁置到今天来写了,unity learn是一个官方教程平台,里面有unity assert store的配套教程,全是文档,比看视…

#java mavn安装图像验证码jar失败kaptcha-2.3.2.jar#

场景:在登录的时候添添加图形验证码功能,使用 com.google.code.kaptcha开发图像验证码,。通过pom引入依赖一直红色提示,找打不到依赖,如图所示 原因:kaptcha-2.3.jar 没有放在mavan的中央仓库 解决方案,需…

Hive笔记

目录 第3章 Hive数据类型 第 4 章 DDL 数据定义 第5章DML数据操作 第6章 查询&#xff08;语法与MySQL一样&#xff09; 第 7 章 分区表和分桶表 第 8 章 函数 第3章 Hive数据类型 如array<map<string,int>> 集合数据类型工作中不是很常用&#xff0c;最常用…

Web 攻防之业务安全:Response状态值修改测试(修改验证码返回值 绕过限制.)

Web 攻防之业务安全&#xff1a;Response状态值修改测试 业务安全是指保护业务系统免受安全威胁的措施或手段。广义的业务安全应包括业务运行的软硬件平台&#xff08;操作系统、数据库&#xff0c;中间件等&#xff09;、业务系统自身&#xff08;软件或设备&#xff09;、业务…

2.17、多生产者-多消费者进程

桌子上有一只盘子&#xff0c;每次只能向其中放入一个水果。爸爸专向盘子中放苹果&#xff0c;妈妈专向盘子中放橘子&#xff0c;儿子专等着吃盘子中的橘子&#xff0c;女儿专等着吃盘子中的苹果。只有盘子空时&#xff0c;爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己…

Linux系统应用编程(四)Linux多线程

本篇文章主要内容&#xff1a;Linux系统应用编程&#xff08;四&#xff09;Linux多线程一、线程和进程的区别二、Linux多线程1.线程的使用 - 创建、退出、等待2.线程的同步 - 互斥量&#xff08;1&#xff09;互斥量的理解&#xff08;略&#xff09;&#xff08;2&#xff09…

你真的会自动化测试?自动化测试技术选型抉择

自动化测试框架 在学习自动化测试或者实践自动化测试时&#xff0c;我们一定会对一个名词不陌生&#xff0c;那就是“自动化测试框架”&#xff0c;而有些人也将Selenium、Appium这样的工具也称之为“自动化测试框架”&#xff0c;那么到底自动化测试框架如何理解呢&#xff1…

多种文字翻译软件-翻译常用软件

整篇文档翻译软件 整篇文档翻译软件是一种实现全文翻译的自动翻译工具&#xff0c;它能够快速、准确地将整篇文档的内容翻译成目标语言。与单词、句子翻译不同&#xff0c;整篇文档翻译软件不仅需要具备准确的语言识别和翻译技术&#xff0c;还需要考虑上下文语境和文档格式等多…

【Linux】一文带你探究网络世界的基石

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;计算机网络…

JVM专题

JVM类加载 Java里有如下几种类加载器&#xff1a; 引导类加载器&#xff1a;负责加载支撑JVM运行的位于JRE的lib目录下的核心类库&#xff0c;比如 rt.jar、charsets.jar等 扩展类加载器&#xff1a;负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包应用程序…

一篇文章让你搞懂TypeScript中的??和?:和?.和!.是什么意思

TypeScript中的??和?:和?.和!.是什么意思&#xff1f;知识回调&#xff08;不懂就看这儿&#xff01;&#xff09;场景复现核心干货???:?.!.知识回调&#xff08;不懂就看这儿&#xff01;&#xff09; 知识专栏专栏链接TypeScript知识专栏https://blog.csdn.net/xsl_…

私有化部署GPT,告别网络困扰

最近的GPT是热火朝天&#xff0c;基本人手一个。工具用的好&#xff0c;工作5分钟&#xff0c;划水一整天。 不过最近Chat的访问越来越限制了&#xff0c;访问官网都有网络的问题&#xff0c;今天给大家介绍一个方案&#xff0c;私人独享属于自己的chat&#xff0c;不再担心想…

sdx12使能bluetooth

最后的效果&#xff1a; 1.驱动使能 apps_proc/kernel/msm-5.4/arch/arm/configs/vendor/sdxnightjar.config #add bt driver CONFIG_BTy CONFIG_MSM_BT_POWERy使用的芯片是sdx12 QCA6174A-1 管脚配置如下&#xff08;如果管脚不同&#xff0c;需要修改对应的dts&#xff09…

(十)排序算法-冒泡排序

1 排序算法 1.1 介绍 排序也称为排序算法&#xff08;Sort Algorithm&#xff09;&#xff0c;排序是将一组数据&#xff0c;依指定的顺序进行排列的过程。 1.2 排序的分类 &#xff08;1&#xff09;内部排序 指将需要处理的所有数据都加载到内部存储器中进行排序。 &…

C/C++程序设计——const关键字

1.修饰变量 1.1 作用 功能&#xff1a;不能直接被修改 const修饰变量&#xff0c;就相当于是定义了一个常量。该变量不能直接被修改&#xff0c;但是可以通过指针修改。 作用&#xff1a;便于维护、提前发现可能错误的修改 比如程序中大量使用了一个数字10&#xff0c;且不会…

GaussDB工作级开发者认证—第一章GaussDB数据库介绍

一. GaussDB概述 GaussDB是华为基于openGauss自研生态推出的企业级分布式关系型数据库。具备企业级复杂事物混合负载能力&#xff0c;同时支持分布式事务强一致性&#xff0c;同城跨AZ部署&#xff0c;数据0丢失&#xff0c;支持1000的计算节点扩展能力&#xff0c;4PB海量存储…

springcloud2.1.0整合seata1.5.2+nacos2.10(附源码)

springcloud2.1.0整合seata1.5.2nacos2.10&#xff08;附源码&#xff09; 1.创建springboot2.2.2springcloud2.1.0的maven父子工程如下&#xff0c;不过多描述&#xff1a; 搭建过程中也出现很多问题&#xff0c;主要包括&#xff1a; 1.seataServer.properties配置文件的组…

安全配置管理 (SCM):建立安全的基础

通过确保在端点中建立和维护理想的安全配置&#xff0c;让自己在安全的基础上做好准备&#xff0c;这样公司就不会因单个漏洞而分崩离析。安全配置管理涉及持续检测端点中各个组件之间的配置偏差和错误配置&#xff0c;并使它们重新对齐。 在本文中&#xff0c;将了解 Vulnera…

<Linux开发> linux应用开发-之-进程通信之管道例程

一、简介 所谓管道&#xff0c;是指用于连接一个读进程和一个写进程&#xff0c;以实现它们之间通信的共享文件&#xff0c;又称 pipe 文件。 向管道&#xff08;共享文件&#xff09;提供输入的发送进程&#xff08;即写进程&#xff09;&#xff0c;以字符流形式将大量的数…