还在为慢速数据传输苦恼?Linux 零拷贝技术来帮你!

前言

程序员的终极追求是什么?当系统流量大增,用户体验却丝滑依旧?没错!然而,在大量文件传输、数据传递的场景中,传统的“数据搬运”却拖慢了性能。为了解决这一痛点,Linux 推出了 零拷贝 技术,让数据高效传输几乎无需 CPU 操心。今天,我就用最通俗的语言讲解零拷贝的工作原理、常见实现方式和实际应用,彻底帮你搞懂这项技术!

1、传统拷贝:数据搬运的“旧时代”

为了理解零拷贝,我们先看看传统数据传输的工作方式。想象一下,我们需要把一个大文件从硬盘读取后发送到网络上。这听起来很简单,但实际上,传统的数据传输涉及多个步骤并占用大量 CPU 资源。

1.1 一个典型的文件传输过程(没有 DMA 技术):

假设我们要将一个大文件从硬盘读取后发送到网络。以下是传统拷贝方式的详细步骤:

  1. 读取数据到内核缓冲区:使用 read() 系统调用,数据从硬盘读取到内核缓冲区。此时,CPU 需要协调和执行相关指令来完成这一步。

  2. 拷贝数据到用户缓冲区:数据从内核缓冲区被拷贝到用户空间的缓冲区。这一步由 read() 调用触发,CPU 完全负责这次数据拷贝。

  3. 写入数据到内核缓冲区:通过 write() 系统调用,数据从用户缓冲区被再次拷贝回内核缓冲区。CPU 再次介入并负责数据拷贝。

  4. 传输数据到网卡:最终,内核缓冲区的数据被传输到网卡,发送到网络。如果没有 DMA 技术,CPU 需要拷贝数据至网卡。

1.2 来看个图,更直观点:

1.3 数据传输的“四次拷贝”

在这个过程中,数据在系统中经历了四次拷贝:

  • 硬盘 -> 内核缓冲区(CPU 参与,负责数据读取和传输)

  • 内核缓冲区 -> 用户缓冲区read() 调用触发,CPU 负责拷贝)

  • 用户缓冲区 -> 内核缓冲区write() 调用触发,CPU 负责拷贝)

  • 内核缓冲区 -> 网卡(最终发送数据,CPU 参与传输)

1.4 性能瓶颈分析

这种传统拷贝方式的问题显而易见:

  • CPU 资源占用高:每次 read() 和 write() 调用都需要 CPU 进行多次数据拷贝,严重占用 CPU 资源,影响其他任务的执行。

  • 内存占用:当数据量较大时,内存使用量明显增加,可能导致系统性能下降。

  • 上下文切换开销:每次 read() 和 write() 调用涉及用户态和内核态的切换,加重了 CPU 的负担。

这些问题在处理大文件或高频率传输时尤为明显,CPU 被迫充当“搬运工”,性能因此受到严重限制。那么, 有没有一种方法能够减少 CPU 的“搬运”工作?此时,DMA(Direct Memory Access,直接内存访问)技术登场了。

2、DMA:零拷贝的前奏

DMA(Direct Memory Access,直接内存访问) 是一种让数据在硬盘和内存之间直接传输的技术,不需要 CPU 逐字节参与。简单来说,DMA 是 CPU 的“好帮手”,减少了它的工作量。

2.1 DMA 如何帮 CPU?

在传统的数据传输中,CPU 需要亲自把数据从硬盘搬到内存,再送到网络,这很耗费 CPU 资源。而 DMA 的出现让 CPU 可以少干活:

  • 硬盘到内核缓冲区:由 DMA 完成,CPU 只需要下指令,DMA 就自动将数据拷贝至内核缓冲区。

  • 内核缓冲区到网卡:DMA 也能处理这部分,把数据直接送到网卡,CPU 只需监督整体流程。

有了 DMA,CPU 只需要说一句:“嘿,DMA,把数据从硬盘搬到内存去!” 然后 DMA 控制器就会接过这活,自动把数据从硬盘传到内核缓冲区,CPU 只需要在旁边监督一下。

2.2 有了 DMA , 再来看看数据传输的过程:

为了更好地理解 DMA 在整个数据搬运中的角色,我们用图来说明:

说明

  • DMA 负责硬盘到内核缓冲区和内核到网卡的传输。

  • CPU 仍需处理内核和用户缓冲区之间的数据传输。

2.3 哪些步骤仍需 CPU 参与?

虽然 DMA 能帮 CPU 分担一些任务,但它并不能全权代理所有数据拷贝工作。CPU 还是得负责以下两件事

  • 内核缓冲区到用户缓冲区:数据需要被 CPU 拷贝到用户空间供程序使用。

  • 用户缓冲区回到内核缓冲区:程序处理完数据后,CPU 还得把数据拷回内核,准备进行后续传输。

就像请了一个帮手,但有些细致活儿还得自己干。所以,在高并发或大文件传输时,CPU 依旧会因为这些拷贝任务感到压力。

2.4 总结一下

总结来说,DMA 确实减轻了 CPU 在数据传输中的负担,让数据从硬盘传输到内核缓冲区和内核缓冲区到网卡时几乎无需 CPU 的参与。然而,DMA 无法彻底解决数据在内核和用户空间之间的拷贝问题。CPU 依然需要进行两次数据搬运,特别是在高并发和大文件传输场景下,这个限制变得尤为突出。

3、零拷贝:让数据“直达”

因此,为了进一步减少 CPU 的参与,提升传输效率,Linux 推出了 零拷贝 技术。这项技术的核心目标是:让数据在内核空间内直接流转,避免在用户空间的冗余拷贝,从而最大限度减少 CPU 的内存拷贝操作,提高系统性能。

接下来,我们来详细看看 Linux 中的几种主要零拷贝实现方式:

注意:Linux 中零拷贝技术的实现需要硬件支持 DMA。

3.1 sendfile:最早的零拷贝方式

sendfile 是最早在 Linux 中引入的零拷贝方式,专为文件传输设计。

3.2 sendfile 的工作流程

  1. DMA(直接内存访问)直接将文件数据加载到内核缓冲区。

  2. 数据从内核缓冲区直接进入网络协议栈中的 socket 内核缓冲区。

  3. 数据通过网络协议栈处理后,通过网卡直接发往网络。

通过 sendfile,整个传输过程 CPU 只需要一次数据拷贝,减少了 CPU 的使用。

3.3 简单图解:

sendfile 图解说明:

  1. 从硬盘读取数据:文件数据通过 DMA 从硬盘读取,直接加载到内核缓冲区,这个过程不需要 CPU 的参与。

  2. 拷贝数据至网络协议栈的 socket 缓冲区:数据不进入用户空间,而是从内核缓冲区直接进入网络协议栈中的 socket 缓冲区,在这里经过必要的协议处理(如 TCP/IP 封装)。

  3. 数据通过网卡发送:数据最终通过网卡直接发往网络。

3.4 sendfile 接口说明

sendfile函数定义如下:

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

  • out_fd:目标文件描述符,一般是 socket 描述符,用于网络发送。

  • in_fd:源文件描述符,通常是从硬盘读取的文件。

  • offset:偏移量指针,用于指定从文件的哪个位置开始读取。如果为 NULL,则从当前偏移位置开始读取。

  • count:要传输的字节数。

返回值是实际传输的字节数,出错时返回 -1,并设置 errno 来指示错误原因。

3.5 简单代码示例

#include <sys/sendfile.h>int main() {int input_fd = open("input.txt", O_RDONLY);int server_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in address;address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8080);bind(server_fd, (struct sockaddr *)&address, sizeof(address));listen(server_fd, 3);int client_fd = accept(server_fd, NULL, NULL);sendfile(client_fd, input_fd, NULL, 1024);close(input_fd);close(client_fd);close(server_fd);return 0;
}

这个例子展示了如何使用 sendfile 将本地文件发送到一个通过网络连接的客户端。只需要调用 sendfile,数据就能从 input_fd 直接传输到 output_fd

3.6 适用场景

sendfile 主要用于将文件数据直接传输到网络,非常适合需要高效传输大文件的情况,例如文件服务器、流媒体传输、备份系统等。

在传统的数据传输方式中,数据需要经过多个步骤:

  1. 首先,数据从硬盘读取到内核空间。

  2. 然后,数据从内核空间拷贝到用户空间。

  3. 最后,数据从用户空间再拷贝回内核,送到网卡发出去。

总结来说,sendfile 可以让数据传输更加高效,减少 CPU 的干预,特别适合简单的大文件传输场景。然而,如果遇到更复杂的传输需求,比如要在多个不同类型的文件描述符之间移动数据,splice 则提供了一种更加灵活的方法。接下来我们来看看 splice 是如何实现这一点的。

4. splice : 管道式零拷贝

splice 是 Linux 中另一种实现零拷贝的数据传输系统调用,专为在不同类型的文件描述符之间高效地移动数据而设计,适用于在内核中直接传输数据,减少不必要的拷贝。

4.1 splice 的工作流程

  1. 从文件读取数据:使用 splice 系统调用将数据从输入文件描述符(例如硬盘文件)读取,数据直接通过 DMA(直接内存访问)进入内核缓冲区。

  2. 传输到网络 socket:随后,splice 继续将内核缓冲区中的数据直接传输到目标网络 socket 的文件描述符中。

整个过程在内核空间内完成,避免了数据从内核空间到用户空间的往返拷贝,大大减少了 CPU 的参与,提高了系统性能。

4.2 简单图解:

和 sendfile 图解类似,只是接口不一样。

splice 图解说明:

数据通过 splice 从文件描述符传输到网络 socket。数据首先通过 DMA 进入内核缓冲区,然后直接传输到网络 socket,整个过程避免了用户空间的介入,显著减少了 CPU 的拷贝工作。

4.3 splice 接口说明

splice 函数的定义如下:

ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

  • fd_in:源文件描述符,数据从这里读取。

  • off_in:指向源偏移量的指针,如果为 NULL,则使用当前偏移量。

  • fd_out:目标文件描述符,数据将被写入这里。

  • off_out:指向目标偏移量的指针,如果为 NULL,则使用当前偏移量。

  • len:要传输的字节数。

  • flags:控制行为的标志,例如 SPLICE_F_MOVESPLICE_F_MORE 等。

返回值是实际传输的字节数,出错时返回 -1,并设置 errno 来指示错误原因。

4.4 简单代码示例

int main() {int input_fd = open("input.txt", O_RDONLY);int server_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in address;address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8080);bind(server_fd, (struct sockaddr *)&address, sizeof(address));listen(server_fd, 3);int client_fd = accept(server_fd, NULL, NULL);splice(input_fd, NULL, client_fd, NULL, 1024, SPLICE_F_MORE);close(input_fd);close(client_fd);close(server_fd);return 0;
}

这个例子展示了如何使用 splice 将本地文件直接发送到网络 socket,以实现高效的数据传输。

4.5 适用场景

splice 适用于在文件描述符之间进行高效、直接的数据传输,例如从文件到网络 socket 的传输,或在文件、管道和 socket 之间传递数据。在这种情况下,数据在内核空间内完成传输,无需进入用户空间,从而显著减少拷贝次数和 CPU 的参与。另外 splice 特别适合需要灵活数据流动和减少 CPU 负担的场景,例如日志处理、实时数据流处理等。

4.6 sendfile 与 splice 的区别

虽然 sendfile 和 splice 都是 Linux 提供的零拷贝技术,用于高效地在内核空间传输数据,但它们在应用场景和功能上存在一些显著区别:

数据流动方式

  • sendfile:直接将文件中的数据从内核缓冲区传输到 socket 缓冲区,适合文件到网络的传输。适合需要简单高效的文件到网络的传输场景。

  • splice:更灵活,可以在任意文件描述符之间进行数据传输,包括文件、管道、socket 等。因此,splice 可以在文件、管道和 socket 之间实现更复杂的数据流转。

适用场景

  • sendfile:主要用于文件到网络的传输,非常适合文件服务器、流媒体等需要高效传输文件的场景。

  • splice:更适合复杂的数据流动场景,例如在文件、管道和网络之间需要多步传输或灵活控制数据流向的情况。

灵活性

  • sendfile:用于直接、高效地将文件发送到网络,虽然操作单一,但性能非常高效。

  • splice:可以结合管道使用,实现更复杂的数据流向控制,例如先通过管道对数据进行处理,再发送到目标位置。

5. mmap + write:映射式零拷贝

除了以上两种方式,mmap + write 也是一种常见的零拷贝实现方式。这种方式主要是通过内存映射来减少数据拷贝的步骤。

5.1 mmap + write 的工作流程

  1. 使用 mmap 系统调用将文件映射到进程的虚拟地址空间中,这样数据就可以直接在内核空间和用户空间共享,而不需要额外的拷贝操作。

  2. 使用 write 系统调用将映射的内存区域直接写入到目标文件描述符中(比如网络 socket),完成数据传输。

这种方式减少了数据拷贝,提高了效率,适合需要灵活操作数据后再发送的场景。通过这种方式,数据不需要显式地从内核空间拷贝到用户空间,而是通过映射的方式共享,从而减少了不必要的拷贝。

5.2 简单图解:

mmap + write 图解说明:

  • 使用 mmap 将文件数据映射到进程的虚拟地址空间,避免显式的数据拷贝。

  • 通过 write 直接将映射的内存区域数据发送到目标文件描述符(如网络 socket)。

5.3 mmap 接口说明

mmap 函数的定义如下:


void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

  • addr:指定映射内存的起始地址,通常为 NULL 由系统决定。

  • length:要映射的内存区域的大小。

  • prot:映射区域的保护标志,例如 PROT_READPROT_WRITE

  • flags:影响映射的属性,例如 MAP_SHAREDMAP_PRIVATE

  • fd:文件描述符,指向需要映射的文件。

  • offset:文件中的偏移量,表示从文件的哪个位置开始映射。

返回值为映射内存区域的指针,出错时返回 MAP_FAILED,并设置 errno

5.4 简单代码示例


int main() {int input_fd = open("input.txt", O_RDONLY);struct stat file_stat;fstat(input_fd, &file_stat);char *mapped = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0);int server_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in address;address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8080);bind(server_fd, (struct sockaddr *)&address, sizeof(address));listen(server_fd, 3);int client_fd = accept(server_fd, NULL, NULL);write(client_fd, mapped, file_stat.st_size);munmap(mapped, file_stat.st_size);close(input_fd);close(client_fd);close(server_fd);return 0;
}

这个例子展示了如何使用 mmap 将文件映射到内存,然后通过 write 将数据发送到网络连接的客户端。

5.5 适用场景

mmap + write 适用于需要对文件数据进行灵活操作的场景,例如需要在发送数据前进行修改或部分处理。与 sendfile 相比,mmap + write 提供了更大的灵活性,因为它允许在用户态访问数据内容,这对于需要对文件进行预处理的应用场景非常有用,例如压缩、加密或者数据转换等。

然而,这种方式也带来了更多的开销,因为数据需要在用户态和内核态之间进行交互,这会增加系统调用的成本。因此,mmap + write 更适合那些需要在数据传输前进行一些自定义处理的情况,而不太适合纯粹的大文件高效传输。

6. tee:数据复制的零拷贝方式

tee 是 Linux 中的一种零拷贝方式,它可以把一个管道中的数据复制到另一个管道,同时保留原管道中的数据。这意味着数据可以同时被发送到多个目标,而不影响原来的数据流,非常适合日志记录和实时数据分析等需要把同样的数据送往不同地方的场景。

6.1 tee 的工作流程

  • 数据复制到另一个管道tee 系统调用可以将一个管道中的数据复制到另一个管道,而不改变原有的数据。这意味着数据可以在内核空间中被同时用于不同的目的,而无需经过用户空间的拷贝。

6.2 tee 接口说明

tee 函数的定义如下:

ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

  • fd_in:源管道文件描述符,数据从这里读取。

  • fd_out:目标管道文件描述符,数据将被写入这里。

  • len:要复制的字节数。

  • flags:控制行为的标志,例如 SPLICE_F_NONBLOCK 等。

返回值是实际复制的字节数,出错时返回 -1,并设置 errno 来指示错误原因。

6.3 简单代码示例

int main() {int pipe_fd[2];pipe(pipe_fd);int server_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in address;address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(8080);bind(server_fd, (struct sockaddr *)&address, sizeof(address));listen(server_fd, 3);int client_fd = accept(server_fd, NULL, NULL);// 使用 tee 复制数据tee(pipe_fd[0], pipe_fd[1], 1024, 0);splice(pipe_fd[0], NULL, client_fd, NULL, 1024, SPLICE_F_MORE);close(pipe_fd[0]);close(pipe_fd[1]);close(client_fd);close(server_fd);return 0;
}

这个例子展示了如何使用 tee 将管道中的数据复制,并通过 splice 将数据发送到网络 socket,从而实现高效的数据传输和复制。

6.4 适用场景

tee 非常适合需要将数据同时发送到多个目标的场景,比如实时数据处理、日志记录等。 通过 tee,可以在内核空间内实现多目标数据复制,提高系统性能,减少 CPU 负担。

总结对比:

下面我将 Linux 的几种零拷贝方式做了总结,方便大家对比学习:

最后:

希望这篇文章让你对 Linux 的零拷贝技术有了更全面、更清晰的了解!这些技术看起来可能有些复杂,但一旦掌握后,你会发现它们非常简单, 并且在实际项目中非常实用。

文章转载自:江小康

原文链接:https://www.cnblogs.com/xiaokang-coding/p/18529737

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

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

相关文章

密码学是如何保护数据传输的安全性?

密码学通过一系列算法和协议来保护数据传输的安全性。 一、加密技术 对称加密算法 原理&#xff1a;使用相同的密钥进行加密和解密。应用&#xff1a;在数据传输过程中&#xff0c;发送方和接收方共享一个密钥&#xff0c;数据在传输前被加密&#xff0c;接收方使用相同的密钥…

python怎么打开py文件

1、首先在资源管理器里复制一下py文件存放的路径&#xff0c;按下windows键&#xff0b;r&#xff0c;在运行里输入cmd&#xff0c;回车打开命令行&#xff1a; 2、在命令行里&#xff0c;先切换到py文件的路径下面&#xff0c;接着输入“python 文件名.py ”运行python文件&a…

云计算——ACA学习 云计算核心技术

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 写在前面 本系列将会持续更新云计算阿里云ACA的学习&#xff0c;了解云计算及网络安全相关…

企业办公管理软件排名 | 九款企业管理软件助你制胜职场!(好用+实用+全面)

在寻找合适的企业办公管理软件时&#xff0c;你是否感到困惑不已&#xff0c;不知道从众多选项中选择哪一个&#xff1f; 一款好的管理软件不仅能简化工作流程&#xff0c;还能增强数据安全性&#xff0c;优化决策支持。 以下是九款备受推崇的企业管理软件&#xff0c;它们将助…

DNS服务器

DNS服务器 1、简介 DNS域名解析服务器&#xff0c;它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;端口号为53&#xff0c;通常使用UDP协议&#xff0c;但是在没有查询到完整的信息时&#xff0c;会以TCP这个协议来重新查询&#xff0c;所以在启动NDS服务器时&a…

顾荣辉在新加坡金融科技节发表主旨演讲:安全不仅是竞争优势,更是共同责任

在全球数字化和去中心化进程中&#xff0c;Web3的作用日益凸显&#xff0c;安全问题也日益成为行业的焦点。在这一背景下&#xff0c;顾荣辉教授于新加坡金融科技节&#xff08;SFF&#xff09;上发表主旨演讲《超越代码&#xff0c;引领信任》。顾教授在演讲中深入阐述了安全在…

Leetcode328奇偶链表,Leetcode21合并两个有序链表,Leetcode206反转链表 三者综合题

题目描述 思路分析 这题的思路就和我们的标题所述一样&#xff0c;可以看作是这3个题的合并&#xff0c;但是稍微还有一点点区别 比如&#xff1a;奇偶链表这道题主要是偶数链在了奇数后面&#xff0c;字节这个的话是奇偶链表分离了 所以字节这题的大概思路就是&#xff1a; …

「Mac玩转仓颉内测版1」入门篇1 - Cangjie环境的搭建

本篇详细介绍在Mac系统上快速搭建Cangjie开发环境的步骤&#xff0c;涵盖VSCode的下载与安装、Cangjie插件的离线安装、工具链的配置及验证。通过这些步骤&#xff0c;确保开发环境配置完成&#xff0c;为Cangjie项目开发提供稳定的基础支持。 关键词 Cangjie开发环境搭建VSC…

2023数学分析【南昌大学】

计算 求极限 lim ⁡ n → ∞ ( 1 n 2 + 1 2 + 1 n 2 + 2 2 + ⋯ + 1 n 2 + n 2 ) \mathop{\lim }\limits_{n \to \infty } \left( \frac{1}{{\sqrt {n^2 + 1^2} }} + \frac{1}{{\sqrt {n^2 + 2^2} }} + \cdots + \frac{1}{{\sqrt {n^2 + n^2} }} \right) n→∞lim​(n2+12 ​1…

从技术创新到商业应用,智象未来(HiDream.ai)创新不止步

在人工智能领域的最新动态中&#xff0c;智象未来&#xff08;HiDream.ai&#xff09;公司&#xff0c;作为全球领先的多模态生成式人工智能技术先驱&#xff0c;已经引起了广泛的行业瞩目。该公司专注于深度学习和计算机视觉技术的融合&#xff0c;致力于开发和优化视觉多模态…

ssm基于Vue的戏剧推广网站+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码看文章最下面 需要定制看文章最下面 目 录 摘 要 I Abstract II 第1章 绪论 1 1.1 课题背景 1 1.2 课题意义 1 1.3 研究内容 1 第2…

利用泰勒公式近似计算10的平方根

文章目录 1. 泰勒公式是什么2、利用泰勒公式计算 10 \sqrt{10} 10 ​第 1 步&#xff1a;泰勒级数展开第 2 步&#xff1a;计算各阶导数第 3 步&#xff1a;在 x 9 x 9 x9 处计算各阶导数第 4 步&#xff1a;构建各阶泰勒展开式第 5 步&#xff1a;计算 f ( 10 ) f(10) f(1…

AI芯片:推动高性能计算场景的关键力量

​ 大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 AI工具集1&#xff1a;大厂AI工具【共2…

C语言--结构体详解

一.前言 为了保证文章的质量和长度&#xff0c;小编将会分两篇介绍&#xff0c;思维导图如下&#xff0c;本文主要讲解概念部分&#xff0c;其中关于结构体内存对齐、位段等更加详细的内容将会在下一篇加以介绍&#xff0c;希望大家有所收获&#x1f339;&#x1f339; 在C语言…

完整教学:胡须图像分割

胡须图像分割系统源码&#xff06;数据集分享 [yolov8-seg-act&#xff06;yolov8-seg-C2f-Parc等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Global Al lnnovatio…

LeetCode 热题100 之 栈

1.有效的括号 思路分析&#xff1a;我们可以使用栈&#xff08;stack&#xff09;来解决这个问题。栈是一种先进后出的数据结构&#xff0c;这与括号匹配的需求非常契合。 unordered_map<char, char> bracket_map&#xff1a;这个哈希表用来存储右括号与左括号的对应关系…

git clone,用https还是ssh

前言 在使用Git去克隆项目时&#xff0c;会遇到https和ssh等形式&#xff0c;这两种又有何种区别呢&#xff0c;本文将重点讨论在具体使用中的问题。 注:第一次使用Git 时&#xff0c;需要先设置全局用户名和邮箱&#xff0c;否则后续使用命令时会报错&#xff0c;也是提醒先添…

【深圳大学/大学物理实验2】霍尔效应及其应用实验预习参考

霍尔效应及其应用 总分&#xff1a;100 组卷人&#xff1a;系统管理员成绩&#xff1a;95 一、单选题 共 10 小题 共 50 分 1. (5分)测量霍尔电压的原理公式是&#xff1a; 学生答案&#xff1a;C √ A. B. C. D. 2. (5分)载流子浓度n的计算式…

[全网最完整最详细C++篇]第四篇:类和对象(上)

目录 1->面向过程和面向对象初步认识 2->类的引入 3->类的定义 4->类的访问限定符及封装 4.1 访问限定符 4.2封装 5->类的作用域 6->类的实例化 7->类的对象大小的计算 7.1 如何计算类对象的大小 7.2 类对象的存储方式猜测 7.3 结构体内存对齐规…

Linux 服务器使用指南:从入门到登录

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; &#x1f6a9;博主致力于用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 一…