eBPF on Go

本篇内容是根据2021年10月份#201 eBPF and Go音频录制内容的整理与翻译

eBPF(已有 7 年历史)是一个可以在 Linux 内核中运行代码的沙箱。它最初是一种构建防火墙的技术,随着时间的推移不断发展,包含一系列新功能。

本期大家讨论了 eBPF 的起源及其工作原理,并深入研究了一些实际用例。虽然 eBPF 程序本身不是用 Go(更像 C)编写的,但我们将了解如何从 Go 代码与 eBPF 程序进行通信。


过程中为符合中文惯用表达有适当删改, 版权归原作者所有.


Mat Ryer:大家好,欢迎来到 Go Time。我是 Mat Ryer,今天我们要聊聊 eBPF。eBPF 是一项技术,它允许你在沙盒中安全地运行程序,而不需要更改内核、代码或安装模块等。这通常是解决诸如网络、安全或可观察性问题的理想之地,因为内核控制一切,它能看到一切……所以可以说它非常完美。但是,由于它是一个如此核心的组件,意味着它实际上很难更改。设想一下你自己的代码,如果你有一个核心服务或其他系统的依赖项,你就会明白这种情况有多难更改;而当你无法更改某些东西的时候,你就无法在这里进行创新。

这通常就是更改内核的故事---它基本上是不可行的,直到 eBPF 出现,似乎改变了规则。让我们更多了解它吧,因为我们现在就做一期关于它的节目。今天和我一起的有 Derek Parker。你好,Derek!

Derek Parker:你好。

Mat Ryer:Derek,你创建了 Delve,对吧?你直接就创建了 Delve…

Derek Parker:是的,没错。

Mat Ryer:是的。而且你现在是 Red Hat 的高级软件工程师,没错吧?

Derek Parker:是的,没错。

Mat Ryer:非常酷。欢迎来到 Go Time。我们还邀请到了 Grant Seltzer。Grant 是 Aqua Security 开源工程团队的一员。

Grant Seltzer Richman:是的。

Mat Ryer:他住在纽约市布鲁克林。那是个很酷的地方。感谢你加入我们,Grant。

Grant Seltzer Richman:谢谢你邀请我。

Mat Ryer:荣幸之至。荣幸是我们所有人的,一半是我的,一半是---Johnny Boursiquot 也在这。你好,Johnny。

Johnny Boursiquot:你好,兄弟。我在这儿准备问一些关于 eBPF 的愚蠢问题。

Mat Ryer:哦,很好,很好,这样我就不用问了。

Johnny Boursiquot:我会让你看起来很棒。我会问所有的---

Mat Ryer:是的。[笑] 我其实刚才只是从 Wikipedia 上写了这个介绍,兄弟。接下来的---今天我是冒牌货。我不介意这些我不太懂的主题,因为我真的可以深入探讨,并且总能学到很多,尤其当我们邀请到像今天这样的尊贵嘉宾时。

那么,谁愿意给我们讲讲 eBPF 的背景?它从何而来,究竟是什么?

Grant Seltzer Richman正如你所说,eBPF 是一项技术,它允许你编写一些小片段的代码,然后把它们放入 Linux 内核中的特定位置,在某些钩子触发时运行。所以你可以把它看作是为一个网络服务注册一个 webhook;同样的,你也可以为你的实际系统做这件事。这些小片段---你可以把它们看作是脚本;这些响应的事件可以是内核函数被调用,比如 Linux 内核源代码中的某些事件。你可以将它们附加到函数上,诸如此类的事情……你可以将这些 eBPF 程序附加到网络套接字上,让它们对进出数据包作出响应。你还可以将它们附加到用户空间函数上,所以如果你在运行一个编译好的 Go 程序,一些服务,即使是长时间运行的服务,你可以将它们附加到实际的---其实你并不是附加到源代码上,而是附加到你编译后的二进制文件中的符号,这些符号对应于实际的函数。你可以让 eBPF 程序对此作出响应。 这是 Derek 肯定可以阐述的东西。

Johnny Boursiquot:等一下---我说过我要问一些愚蠢的问题。让我用一个对我来说更简单的方式说你刚才说的内容……

Grant Seltzer Richman:当然。

Johnny BoursiquoteBPF 就像---内核就像 HTML,而 eBPF 就像你的 JavaScript……当有人点击 HTML 上的按钮时,你的 JavaScript 可以对该事件的发生作出反应。

Grant Seltzer Richman:完全正确。这是很多人---我想这个类比最早是 Brendan Gregg 提出的,但这是人们喜欢解释 BPF 的经典方式。这个类比非常好。

Johnny Boursiquot:那它只是一个观察工具吗?你只能监听事件,还是可以更改事情?

Grant Seltzer Richman:不,你实际上可以更改事情,你可以作出响应。当然有一些限制,因为安全性肯定是一个问题……你不希望能够随意在运行的操作系统中放入任何东西,尤其是在生产环境中……但是的,你可以做很多事情。你可以采取行动,阻止某个进程的发生……在网络路由的情况下,你可以按照自己的意愿重新路由数据包……

Johnny Boursiquot:我的各种警觉感官都在作响,不过我们稍后再谈……[笑]

Derek Parker:是的,我见过纯 eBPF 的负载均衡实现,我觉得这非常酷,也非常有趣。而且关于 eBPF 程序本身的另一个有趣之处是---你基本上用 C 语言编写它们,但这就像是简化版的 C。与典型的 C 编译器作斗争不同,你得和 BTF 验证器作斗争,它会抱怨“你不能在 BPF 程序中使用循环。”你需要非常小心地分配栈空间,因为有非常严格的要求……因为它必须是安全的,毕竟它运行在内核中;即使它是在沙盒中运行,程序仍然必须终止,所以你不能有循环等验证器无法验证程序是否会实际终止的情况。

因此,在编写程序时,为了绕过这些限制,你需要做一些有趣的事情。

Mat Ryer:这些事情是得到内核许可的吗?这是内核明确允许的,还是某种对内核进行的操作?

Grant Seltzer Richman:BPF 确实存在于内核中。它是 Linux 内核的一部分虚拟机,伴随所有系统发布。在加载实际程序以及程序能做什么方面,是有权限体系的。你必须有 root 访问权限,或者加载 BPF 程序的进程要有特定的能力。

Mat Ryer:对。所以你不能对任何内核都这么做。这是一项明确支持的技术。

Grant Seltzer Richman没错。你不能在 macOS 上这样做。这是特定于 Linux 的。而且所有 Linux 发行版都支持它。还有一些我无法详细说明的事情,不过微软确实有在努力将 BPF 移植到 Windows 上。

Derek Parker:是的,我也听说过很多,但我没有在 Windows 上开发,所以我不清楚它的状态……不过我觉得这很酷,因为---看到 Linux 中的一些创新传播到其他地方很棒。我真希望我们能在 macOS 上进行更多原生的容器化工作,而不是“哦,我们赶紧安装个 Linux 虚拟机,假装我们在 macOS 上做容器化工作,但实际上并没有。”如果微软真的在内核中采用这种技术,我觉得这很酷,而不是---我不知道这是否真的是在内核中,还是他们在幕后通过“WSL赶快把你的 eBPF 程序传那边”来做。

Grant Seltzer Richman:我认为它目前是基于用户空间的,但我不想误解……不过我觉得你说的没错。我觉得如果防作弊软件能够通过 BPF 程序检测到发现的新作弊手段,并应用于 Windows 上的游戏,那会非常酷。

Derek Parker:就像是---叫什么来着,Punk Buster 吧?这是一些大型游戏的防作弊系统……像 Punk Buster eBPF 版。

(译者注: 是一款游戏防作弊软件,通常被玩家简称为PB)

Grant Seltzer Richman:是的。没错。

Johnny Boursiquot:我试图将用户空间、内核空间等概念可视化……对于那些仍在努力理解的人来说---通常当你和我用我们喜欢的编程语言写程序时,比如 Go,我们写的是用户空间程序。当它们需要在操作系统级别做某些事情时,它们会发出系统调用,比如“嘿,我想打开一个文件。”

对我们来说,开发者是使用标准库。Go 会说“嘿,我想打开这个文件,因为我想读取内容”或其他操作。所以这是一个系统调用,然后操作系统会处理所有的事情,接着返回结果给我们。但这一切都发生在用户空间,对吧?而我们现在讨论的是编写程序的能力,让它们在内核空间中运行。这就深入了一层,实际上能监听、响应,甚至可能更改内核正在做的事情。

Grant Seltzer Richman:完全正确。这样做有很多优势。让我们以系统调用为例,事件的流程是你的 Go 程序尝试写入一个文件;在底层,Go 的标准库使用的是 write 系统调用。在系统调用之前,你的 Go 程序会把所需的信息放到正确的寄存器中,然后执行系统调用指令,内核接管,执行系统调用,返回用户空间,告诉你“嘿,我们成功写入了文件”或其他信息。

在 eBPF 的世界中,你可以写一个 eBPF 程序,它在每次调用系统调用时触发。所以如果你有一个 eBPF 程序,它在每次 write 系统调用被调用时触发……所以在原始事件流程上增加了一步,Go 程序设置系统调用,执行它……就在执行之前,eBPF 程序运行;它可以检查传递给系统调用的所有参数,并做任何它想做的事情……一旦它完成了,系统调用执行,然后返回到用户空间,等等。而 BPF 这一边对实际触发它的应用程序是完全不可见的。

Johnny Boursiquot:有意思。

Mat Ryer那么它是作为一种后台进程运行在主要任务的旁边,还是一种阻塞代码?当它看到某个调用时,它到底是如何运行的?

Derek Parker:如果探针附加在某个函数或类似的地方,它会阻塞。在那个时刻,触发探针的程序执行将暂停,以便 eBPF 程序可以执行任何需要的检查。这也是为什么在追踪的上下文中---这是让我对 eBPF 感兴趣的地方---涉及到系统调用的开销以及追踪的总体开销。

类似的事情也会发生---我对它感兴趣的原因是我想让 Delve 的追踪后端更高效,减少开销,这样你也许可以在生产环境中使用它。我曾经发过一条推文,当我刚开始研究这个时,我展示了它增加的开销。我有一个程序,运行时间是一些奇怪的微秒数… 然后使用基于 eBPF 的追踪,它从大约 20 微秒增加到了 300-400 微秒左右… 这听起来像是一个显著的开销,但我们谈论的是微秒。

然后我使用 Delve 之前基于 ptrace 的追踪进行了计时,时间增加到了 2.3 秒。所以你从微秒增加到实际的秒,这种开销在生产环境中是不可接受的。这让我对它产生了兴趣,写出一些非常小型、目标明确的程序,它们可以作为某些事件的结果被调用,不会在内核和用户空间之间进行上下文切换,尽可能短的时间内暂停程序,获得高效、详细但又灵活的追踪数据。

另一个难点是如何实现灵活性。很多时候,当人们编写 eBPF 程序时,它们非常有针对性。你在编写函数时已经知道它会附加到哪个对应的函数上;可能是内核函数,或者其他函数……你通常已经知道,所以你大概知道要期待哪些参数。但在 Delve 的情况下,我有点滥用了它,因为我想将探针附加到完全随机的函数上,我不知道它有多少参数或返回值。我对它一无所知,但我想从中获取所有信息。我该怎么做呢?

这引发了很多问题,比如如何编写一个通用的 eBPF 程序,以及如何在不引入我试图消除的慢速问题的前提下,在内核空间和用户空间之间进行通信。

Johnny Boursiquot:你之前提到你使用了一种受限的 C 语言;显然出于性能原因,有些事情是不允许的。那么开发流程是什么样的?如果我要使用 eBPF,我必须用 C 吗?还是有包装器或 SDK?开发流程是什么样的?

Grant Seltzer Richman:eBPF 程序本身,从高层次来看---实际上就是这样,你有两部分。你有 eBPF 程序本身,然后你有用户空间程序,它将 eBPF 程序加载到内核中,并监听反馈……本质上是与 eBPF 程序交互的代理。

在 BPF 这一侧,我的经验仅限于用 C 编写。我听说过有一个库可以让你用 Rust 编写实际的 BPF 程序,因为它的后端是 LLVM。LLVM 控制着 eBPF 字节码的规范,类似于 Windows 的 BPF,不过我对此一无所知,不想详细讨论……而且 Rust 是竞争语言,所以我们不能讨论它……

Mat Ryer:[笑]

Grant Seltzer Richman:是的,你用 C 编写 BPF 程序,所以大多数情况下你只是在将这些定义在 BPF 世界中的 helper 函数串联起来。它们是定义在头文件中的 BPF helper 函数,使用起来并不复杂。如果你是 Go 开发者,这不会花太长时间让你上手,尤其是看看例子……而且有很多入门指南。

在用户空间这一侧,你可以用多种语言写程序。你可以使用 C 标准库,它叫做 libbpf。还有一个项目叫做 BCC,虽然不推荐使用,但它可以让你使用 Python 或 Go 版本,甚至还有一个旧的、不再维护的 Lua 版本,当然也有 Rust 的版本。用 Go 也有很多不同的库可以使用。我偏好一个叫做 libbpfgo 的库,它是 libbpf 的包装器。还有一个 Go 原生实现,它是 Cilium 项目的一部分……但我维护 libbpfgo 并在我帮助维护的项目中使用它,所以我偏好这个。

Johnny Boursiquot:你有点偏心。

Grant Seltzer Richman:是的,我对此很坦诚。

Johnny Boursiquot:显然你提到这是个 Linux 专属的东西,除了正在进行的 Windows 版本……所以如果我在 Mac 上需要编写这些程序,我得用某种虚拟机来测试和运行它们。

Grant Seltzer Richman:是的,暂时如此。

Derek Parker:我记得以前有 DTrace,用于 Darwin 内核,类似的东西。

Grant Seltzer Richman:是的,macOS 有一个---我不记得确切名字,但最近版本的 macOS 有一个类似的安全框架,不过它们之间没有互操作性。



Johnny Boursiquot:我们来谈谈使用场景吧。我对 eBPF 程序的最佳应用场景很感兴趣。我们谈到过可观察性,知道某些事件何时发生,Derek 提到这是一个非常有针对性的工具,对吧?所以你已经知道想要获取哪些系统调用的回调,除了你想获取所有信息的情况……我猜这种情况下写这些程序的方式会非常不同,而不是只是寻找某个文件是否被打开,或者类似的事情。

所以我很好奇使用场景……我听说过可观察性,似乎是个不错的用例……我还听说过网络故障排查、写负载均衡器的情况,我对这方面尤其感兴趣……eBPF 能帮你解决哪些问题?你在解决哪些问题?

Grant Seltzer Richman:当然。我最常用的场景是安全性。我帮助维护一个叫做 Tracee 的项目,它挂钩到数百个不同的事件,试图关联所有这些在内核中发生的事情,以确定是否有入侵或恶意软件……它允许你在上面应用策略,做一些很酷的事情,比如当程序或进程被执行时,尝试捕获实际运行的二进制文件以供以后检查……你可以做很多安全方面的事情。

可观察性---你可以在生产中使用 BPF 来确定你的 web 服务的健康状况。你可以附加到网络套接字或某种网络机制上……BPF 支持多种机制,以确定丢包数量或数据包的路由位置;你可以通过这种方式获取大量信息。

Johnny Boursiquot:我们谈论的这种可观察性,和我们现在常用的术语“可观察性”有些不同。我们通常想到的可观察性是“我需要一个仪表盘,我需要 Honeycomb 或 DataDog,看我的服务是否运行,延迟是多少”等等。而我们现在谈论的是一种不同层级的可观察性,没错吧?

Grant Seltzer Richman: 是的,确实不同。在大多数情况下,甚至可以说所有情况下,你可以访问到原始内存。你可以看到数据包的全部内容,或者用户空间程序的全部内存内容。不过即便你不检查内存,你仍然可以让这些 BPF 程序触发并报告“嘿,发生了这个事”,就像你在 Go 程序中添加一行代码,然后重新编译并运行它一样……比如 println,而不是编辑源代码并重新编译,你可以添加一个 BPF 程序,附加到内存中的某个地方,看看某行代码何时执行。这就是 Delve 所做的。

Mat Ryer:那么我们需要自己编写所有这些东西吗?是否已经有一些现成的工具?有没有工具可以监控内存分配,比如收集到 Prometheus 中并显示在仪表盘上?有没有现成的工具在围绕这个技术发展?是否有一个生态系统?

Derek Parker:是的,我知道有一些工具---从系统管理员的角度来看,Grant 之前提到的 Brendan Gregg,来自 Netflix,他是 DevOps 的专家……他有一整套基于 eBPF 的工具、脚本和单行代码,可以用来检查系统。我记得他有一篇很棒的博客,介绍如何在五分钟内调试生产问题,里面也提到了很多基于 eBPF 的脚本和工具。

但我觉得你提到的问题是这些工具的产品化,以及如何将它们集成到指标收集系统中……我知道在这个领域有很多努力正在进行。

Mat Ryer:是的,这很有趣。那么当你说在生产环境中运行这些东西时,是不是需要提前计划、启用、构建功能?还是可以直接附加到正在运行的进程上?因为它是在内核中;几乎在所有进程的底层。

Derek Parker:是的。我认为大多数准备工作在于确保你有一个可以加载这些程序的内核,我认为只要你运行的是现代内核,应该没有问题。但在用户空间程序方面,你不需要做任何协调。你只需要与内核协调,并让你正在运行的程序加载 eBPF 程序。但你不需要与用户空间程序进行协调。对于 Delve 或类似的工具来说,这就像是一个普通的调试会话,我们只是请求内核允许我们做一些事情,程序不需要参与决策。

Mat Ryer:是的,这很有趣。这对于调试或任何类型的检查都非常有用;你几乎不需要额外运行任何东西……我可以理解为什么会有一系列单行代码工具,因为这在工具箱中非常有用。非常有趣。我们会尝试找到它并把链接放在节目笔记中。这听起来非常有趣。至少我们可以看到一些真实的 eBPF 程序示例。

Grant Seltzer Richman:是的。我还想补充一下,关于之前的问题,关于生态系统……我会说,如果这项技术让你兴奋,或者说拥有这种可见性让你兴奋,但你可能会被吓到,甚至不想自己编写 eBPF 代码,实际上有一个正在发展和成熟的生态系统围绕着这个技术。许多产品正在开发中,以获得这种可见性。

另外,你完全不需要重新编译代码,这对 SRE 或安全人员来说非常好用。你可以运行服务并在不重启服务的情况下,编写 BPF 程序,检查不同的内存区域,这非常有价值。

Mat Ryer:那么稍微高层一点的呢?比如你是一个 web 开发者,想实现文件监控,自动重载的功能。你能不能写一个 eBPF 程序监控某个路径下的文件变化,然后采取行动,提醒你刷新?

Grant Seltzer Richman:是的,绝对可以。我觉得 Derek 说得很好,Brendan Gregg 有很多工具可以非常出色地完成非常特定的任务。我记得有一个工具叫 OpenSnoop,它可以告诉你每次文件被打开时的信息。或者一个更强大的工具……我再一次推荐我参与的项目 Tracee,你可以运行它并获得所有你想要的信息,还可以过滤不同的事件,而无需编写 eBPF 代码。

Mat Ryer:那就是 Tracee,对吧?

Grant Seltzer Richman:没错,它在 Aqua Security 的 GitHub 上。

Johnny Boursiquot:顺便提一下,你和 Liz Rice 一起工作,对吧?

Grant Seltzer Richman:我曾经和她一起工作。她在我加入后不久就离开了,很遗憾……

Johnny Boursiquot:[笑] 我本来想问你和一位摇滚明星(我用了一个敏感词)……一位我们社区中知名且受人尊敬的成员一起工作是什么感觉?[笑]

Mat Ryer:……她还玩摇滚。

Johnny Boursiquot:她还玩摇滚。[笑声]

Grant Seltzer Richman:当她在的时候感觉很棒。我在社区里仍然经常与她互动。她为 eBPF 社区做了很多工作,所以我仍然常与她合作。

Mat Ryer:你是说她在你加入后就离开了,还是之前?

Grant Seltzer Richman:在我加入后不久。我可能把她吓跑了……[笑声]

Mat Ryer:很可疑。这是个可能性,是吧?我就是这么想的。

Derek Parker:Johnny,你之前提到对我提到的基于 eBPF 的负载均衡感兴趣……

Johnny Boursiquot:是的。

Derek Parker:我知道 Liz Rice 做过一次关于如何实现负载均衡的非常好的演讲。如果你感兴趣,我强烈推荐你去找找她的演讲,真的非常棒。

Johnny Boursiquot:我会去找的,谢谢你。

Mat Ryer:很酷,我们也会找到并把它放在节目笔记中。

Grant Seltzer Richman:eBPF 周围有一个很棒的社区。虽然有很多东西需要学习,也有很多让人困惑的地方,但整个生态系统真的在不断发展,变得越来越容易接触……有很多人对它感到兴奋,并且乐于提供帮助。如果你访问 eBPF.io,那上面有一个 Slack 频道,你可以加入,非常有帮助。而且有很多相关的演讲……Derek 和我即将参加一个会议发表演讲。学习资料可以说是丰富多样。

Mat Ryer:是的。那么这个社区主要在哪里?是不是都集中在那个 Slack 频道里?如果有人想参与,eBPF 的社区还存在于哪些地方?

Derek Parker:我认为 eBPF 的技术,尤其是在云原生和 CNCF(云原生计算基金会)领域非常流行;云原生、Kubernetes 生态系统是社区主要的聚集地。此外,它也涉足了一些编程语言社区,针对那些想要实现相关功能的人们。但总体而言,大部分兴趣和社区活动都集中在云原生领域。

Johnny Boursiquot: 我在短暂了解 eBPF 的过程中,发现大多数示例似乎都围绕着 BCC(BPF Compiler Collection),主要用 Python 编写。我看过一些这样的例子,当时我心里想,“好吧,我们在这里写了一些 Python 代码,然后在某个地方插入了一大段 C 代码……” 我们可以看到代码中的钩子,但这让我回想起之前提到的开发体验。我很好奇,在 Go 中编写这些程序的体验是什么样的?在 Go 生态系统中,你通常用哪些库来与这些程序交互并编写它们?

Derek Parker:我先来回答这个问题……我也要为 libbpfgo 框架做个宣传,因为我在 Delve 中使用它来实现基于 eBPF 的追踪后端。对于编写和加载 eBPF 程序并将其应用到 Go 程序中,已经有非常不错的工具。不过在某些 eBPF 特性上与 Go 结合时会遇到一些挑战……回到正题,毕竟这是 Go Time 播客……例如,当你在 Go 程序中使用探针时可能会遇到一些棘手的情况,有两种探针。对于用户空间探针,有 uprobes 和 uretprobes。uprobes 可以附加到函数的入口点,而 uretprobes 则附加到函数的返回点。因此,你可以在函数入口和返回时分别进行钩取。

但这在 Go 中非常棘手,因为 uretprobes 的工作机制是修改一些数据,比如 goroutine 栈上的某些地址。如果你不熟悉,goroutine 的栈通常非常小,并会随着时间增长。而在栈增长过程中,Go 运行时需要遍历栈中的指针,移动它们并进行更新等操作。

所以,如果你不小心使用 uretprobes,可能会导致 Go 程序崩溃,因为当 Go 运行时试图复制栈时,会发现一个它无法识别的地址,然后程序就会崩溃。

在 Delve 中,我们不得不做一些非常巧妙的事情,用 ptrace 监控 Go 运行时何时准备复制栈,然后临时取消 uretprobe,让它完成操作后再重新附加探针。因此,涉及到 Go 运行时时需要多加小心。对于带有运行时的语言,或者像 Go 这种自检查的语言,使用 uretprobes 可能会带来一些奇怪的问题,这也是你在做这些低级别探查时需要注意的地方。

Mat Ryer:是的,我相信遇到这种崩溃会非常奇怪。如果你有一个小型的 eBPF 程序,它可能会发出一些有趣的信息,例如统计内存分配之类的,那你该如何提取这些信息呢?首先,这些信息会存储在哪里?eBPF 程序有自己的内存吗?Go 程序如何获取这些信息?

Grant Seltzer Richman:当然。也许我们在讨论 BPF 程序时遗漏了一个关键点,那就是 BPF 程序到底能做些什么。BPF 程序主要与各种形式的映射(map)交互;就像 Go 中的 map 一样,BPF 中也有不同类型的映射,你可以用它们来存储信息。这些 map 可以在用户空间和内核空间之间共享,或者在多个 BPF 程序之间共享。

例如,你可以有一个环形缓冲区(ring buffer),假设你有一个简单的 BPF 程序,它在每次某个函数被调用时触发,或者每次触发一个系统调用。在这个 BPF 程序中,你可以创建一个小消息,比如“系统调用被触发了”,然后将这个消息放入一个字符串中,使用环形缓冲区发送到用户空间。在用户空间中,你可以有一个 goroutine 来监听这些事件并将它们打印到屏幕上。这些缓冲区和映射使得在用户空间和 BPF 程序之间可以共享内存。

Mat Ryer:那么在 Go 端能否获得一个通道(channel)接口,可以通过 for range 来读取这些内容?

Grant Seltzer Richman:我会说是的,但这取决于你使用的库。实际上,底层的原语是不同的接口,但在使用 libbpfgo 的情况下,你确实可以得到一个通道。所以你可以像与其他 Go 程序一样与它交互。

Mat Ryer:这也包括发送数据吗?

Grant Seltzer Richman:发送数据有点不同,因为你是更新共享映射中的值。虽然有一个接口或 API 可以做到这一点,但环形缓冲区更适合从 BPF 向用户空间发送数据。

Mat Ryer:我明白了。所以这些 map 就像对象一样,它们是键值对。内核是否已经有了这种概念?或者这是 eBPF 模型化出来的?

Grant Seltzer Richman:不深入讨论不同特性的话---因为有很多我也不太了解的东西---eBPF 提供了对内核的可见性,这个概念并不新鲜,但它确实让事情变得更简单。

以前,可能你需要编写一个内核模块来实现的功能,那些模块没有太多安全保证,并且要求你重启,甚至有时候需要重新编译 Linux 内核,然后重启,整个过程耗时很长。而 BPF 让这一切变得更快、更安全,并且更容易接触。

Mat Ryer:是的,作为一个 Go 程序员,能够通过一个通道接收关于系统内部详细信息的实时数据,光是这种机制就足够让人兴奋了。因为谁知道你能构建出什么样的东西呢……我刚想到的一个用例是文件监控,但我相信,如果你能深入了解内核中的实际情况,可能会有很多其他的应用场景。

Grant Seltzer Richman:可能性是无穷的! [笑声]

Mat Ryer:是的,我对此感到非常兴奋。我们已经听说了很多关于 libbpfgo 的内容,我们一定会提供相关链接。我在查看仓库时,发现它确实提供了一个不错的 API。很有趣的是,即使使用这个库,我是否也可能会遇到我们之前提到的那些崩溃问题?如果我要编写这样的代码,是不是应该避免使用 goroutine?

Derek Parker如果你要在 Go 中使用 eBPF,我唯一建议避免的是 uretprobes,除非你非常非常清楚自己在做什么……因为几乎 100% 的情况下,它会导致你的程序崩溃。让它正常工作的唯一方法是做一些我们在 Delve 中做的那种奇怪的事情,这是一种非常复杂的小技巧。

Mat Ryer:这个技巧能不能打包成一个库呢?这个小技巧能不能一次解决所有问题?

Derek Parker这是有可能的……实际上,这个问题在 Delve 中的一个待处理的拉取请求里已经得到了解决。实现这个解决方案需要涉及很多东西,比如 Dwarf 知识(即二进制文件中的调试信息),使用 ptrace 并获得使用 ptrace 的权限,还要结合 eBPF……有很多组合在一起的东西,这些都不是典型的 Go 编程体验。所以这里确实有一些复杂的地方……但总的来说,除了 uretprobes,其他功能都可以安全地与 Go 一起使用。不过如果你使用 uretprobes,几乎肯定会导致问题。

Mat Ryer:它们通常会用来做什么?

Johnny Boursiquot:捕捉函数的返回值。

Derek Parker:对。

Mat Ryer:我明白了。所以如果你只需要读取数据,可以使用环形缓冲区之类的东西来实现。

Derek Parker:是的,从程序内部,uprobe 会触发并开始执行你的 eBPF 程序,而你的 eBPF 程序可以使用环形缓冲区或映射与用户空间通信。

Delve 同时使用了两者。它使用映射从用户空间向 eBPF 程序传递信息,然后使用环形缓冲区将 eBPF 程序生成的数据发送回 Delve。

Mat Ryer:我明白了。这真的很有趣。当我想到 Delve 时,我觉得它是一个非常底层的工具,因为我通常处理的是更大型的系统。但每次我深入研究时,我都会发现相似的层次结构,通常这些架构会更加复杂……我总是觉得这非常有趣。

Johnny Boursiquot:简单并不容易,伙计……

Mat Ryer:是的,绝对如此。

Mat Ryer:我想问一下,大家觉得 eBPF 的未来会怎样?我们是不是感觉这只是一个开始,未来会越来越令人兴奋?

Johnny Boursiquot:商业产品……这是下一个方向。商业产品。 [笑声]

Mat Ryer:这是下一步的发展吗?我们现在就可以开始创业了。我们四个。 [笑声] 就在 Go Time 上直播创业。不是 The Go Time,对吧?应该叫 Go Time。我刚才做了个像 The Facebook 那样的事。不过我觉得现在叫 The Go Time 反而更酷。

Johnny Boursiquot:The Go Time。

Mat Ryer:你懂我的意思吗?感觉我们已经绕了一圈。不过如果我们要创业,公司会是什么样子的?

Grant Seltzer Richman:我肯定会说这个生态系统正在成熟,或者说刚刚开始成熟,但还有很多用例尚未被发掘。libbpf 还没有达到 1.0 版本。我觉得现在有很多人正在进入这个社区,他们正在学习 BPF……内核这边也有很多讨论,关于 BPF 正在吞噬 Linux 的说法,甚至有人在讨论用 BPF 代码重写 Linux 内核的大部分内容,使其更加模块化。

比如调度器(scheduler),我们可以动态地将逻辑放入调度器中,改变我们调度进程的方式。当然,驱动程序也是另一个人们正在考虑的领域……但从一个更高的视角来看---我不想太过“思想领袖化”,但……

Johnny Boursiquot:请继续。请继续。

Grant Seltzer Richman:我忘了是谁在演讲中提到过这个观点,BPF 代表了一种新的软件范式,它让你能够动态改变软件与操作系统的交互方式。很难说 BPF 的未来会走向何方,因为 1)还有无数新的想法可以将 BPF 程序附加到不同的地方;2)有很多新的人才正在进入社区,带来了很多好点子,新的贡献者层出不穷……总的来说,这些想法几乎没有受到什么限制。就像问“Go 的下一个大事件是什么”,或者“你可以用 Go 写什么”一样,答案几乎是“任何东西”。不过也许 BPF 更酷一些。

Mat Ryer:好吧,这就引出了我们的常规环节……是时候进入“不受欢迎的意见”环节了。

Mat Ryer:Grant Seltzer,你有什么不受欢迎的意见要分享吗?

Grant Seltzer Richman:有的。我还想提一下,我可是这个环节的冠军……

Mat Ryer:真的吗?

Grant Seltzer Richman:我在这个节目上发表过有史以来最不受欢迎的意见。

Mat Ryer:是吗?你是说 eBPF 比 Go 更酷的时候吗? [笑声] 你打破了自己的记录。

Grant Seltzer Richman:我当时好像说的是关于棒球的。

Johnny Boursiquot:你想换个不受欢迎的意见吗?

Grant Seltzer Richman:不,我仍然认为棒球是最好的运动。但这次我不会试图超越自己。我不会说任何政治性的东西……不过我想说,我一直以来的观点是,工程团队里每个小组都应该有一个安全工程师。

Mat Ryer:真的吗?

Grant Seltzer Richman:我认为很多软件开发者,甚至是负责架构整个系统的人,在做决定时,如果能够有一个安全工程师的意见,或者是一个对安全有更多培训的开发人员的意见,对于整个组织的安全性会有非常大的帮助,而不是仅仅依赖一个在旁边的安全团队,他们只能对现有的基础设施抛出一些产品。

Johnny Boursiquot:所以在你看来,这与在产品发布前进行的一次安全审查不同?你说的是在我们构建软件时,团队中就应该有一个安全人员。

Grant Seltzer Richman:是的。

Mat Ryer:这有点像测试。以前测试几乎是独立于软件开发的一个环节,后来我们成为了测试驱动开发者,意识到编写良好测试代码是我们的责任……我们是不是正在走向一种“安全驱动开发”(Security-Driven Development,SDD)的模式?

Johnny Boursiquot:SDD。哦,我还以为你在说别的…… [笑声]

Mat Ryer:不,-驱动开发。DD。

Johnny Boursiquot:明白了。

Mat Ryer: 这真的很难做到,不知道为什么。写下来。我想我做到了。我觉得我们可以在剪辑中修复它。[笑声] 如果大家都同意的话,我很乐意继续下一个话题。

Derek Parker: 那样最理想了。

Mat Ryer: 设计系统时,安全性确实是你需要考虑的关键问题之一。你说得对,有时候你只需做一些设计决策就可以让系统更加稳健。例如,如果系统是幂等的,你可以多次重试某个操作,采取“宁可安全也不冒险”的原则,因为系统的设计方式确保不会因为重试而出问题。同样的做法也适用于安全问题。通过做出某些设计选择,你自然会让系统更加安全。所以,这确实是个有趣的话题……我们一定要在 Twitter 上测试这个观点。虽然我们也可以在 Facebook 上测试,但我想我们都知道为什么不这样做……

Grant Seltzer Richman: 哈哈。

Mat Ryer: 因为我们永远也看不到测试结果……

Derek Parker: BGP,和 BPF 不同。

Johnny Boursiquot: [笑声]

Mat Ryer: 哦,是吗?这就是我们做这个节目的原因……[笑声] 哦,不……!

Johnny Boursiquot: 这个澄清很及时,的确如此。

Mat Ryer: 我犯了个错误。好吧,非常有趣……Johnny,你怎么看?你觉得团队里有安全专家是什么感觉?

Johnny Boursiquot: 我并不反对。很难反对这个观点。

Mat Ryer: 说实话,很难反对任何关于安全的观点。你不能成为那个在房间里说“我觉得我们应该少关注安全”的人。[笑声] Derek,今天你有什么不受欢迎的观点吗?

Derek Parker: 有的。上次我没有发表什么观点,我紧张了。但这次我有一个。虽然没有 Grant 的观点那么发人深省。

Mat Ryer: 也没有那么戏剧性,毕竟你这次没紧张得说不出话来。[笑声] 这个环节的戏剧性正是来源于大家的紧张感。

Derek Parker: 最近我在写 Go 代码和 eBPF 代码之间来回切换,这让我回到了很多 C 语言的编程中。所以我的不受欢迎的观点是---蛇形命名法比驼峰命名法更好。[笑]

Johnny Boursiquot: 哇哦。

Mat Ryer: 那么,对于那些不熟悉这两种命名法的人来说,能解释一下它们的区别吗?

Derek Parker: 蛇形命名法是这样的:word_another_word_another_word,而驼峰命名法是这样的:wordAnotherWord……Go 语言通常使用驼峰命名法,而 C 或 Rust 通常使用蛇形命名法。我个人觉得蛇形命名法看起来更好,也更易读。我不知道,驼峰命名法总给人一种词语混在一起的感觉,而蛇形命名法看起来更像一个句子……我觉得它看起来更好。

Mat Ryer: 这个观点挺有意思的。那在 Twitter 上的标签(hashtag)呢?你会用蛇形命名法来写标签吗?

Derek Parker: 我通常都全用小写。我会把字符串转成小写后写标签。

Mat Ryer: 把字符串转成小写。

Derek Parker: 对。

Mat Ryer: 那你会用 eBPF 来处理这个问题吗?比如在发推之前改掉它?[笑声] 不过据说这其实是一个无障碍访问的问题,全用小写对屏幕阅读器来说不太友好。我之前在电脑上故意输入很多无意义的东西,让电脑读出来,我玩了好几个小时。

Derek Parker: 是的,是的。

Mat Ryer: 是的,基本上就是在模糊测试 say 命令。

Derek Parker: 确实,SSH 到别人的电脑上,然后用 say 命令输入一些随机的内容,乐趣无穷。[笑]

Mat Ryer: 哦,是的,确实有趣。

Derek Parker: 但通常我会保持单词简短,用一个词的标签,这样希望不会影响可读性。

Mat Ryer: 但蛇形标签会解决这个问题,不是吗?

Derek Parker: 没错。

Johnny Boursiquot: 虽然看起来有点可疑,但的确如此……

Derek Parker: [笑声]

Mat Ryer: 看起来是有点奇怪。我以前用 Ruby 编程时,标准输入(STDIN)也使用蛇形命名法,比如下划线分隔词汇……是啊,我也不知道。

Johnny Boursiquot: 俗话说,“入乡随俗。”

Grant Seltzer Richman: 有一年在 GopherCon 上有一个非常有趣的演讲,讲的是如何编写更易于访问的 Go 代码,或者说通用代码,其中的一部分就是让代码更容易被屏幕阅读器读取。当 Derek 提出他的观点时,我就在想这个问题。我觉得蛇形命名法可能比驼峰命名法更容易让屏幕阅读器读取。

Mat Ryer: 是的,可能是这样吧。不过 Derek,你的观点能走到多远?你会给你的孩子起蛇形命名的名字吗?

Johnny Boursiquot: 你还在考虑这个问题吗?[笑声]

Derek Parker: 是的,确实是这样。我最小的孩子叫 Davie_,你知道的……[笑]

Mat Ryer: 我真迫不及待想见到第一个给孩子起这种名字的工程师,比如名字中带下划线之类的。我会非常喜欢。

Derek Parker: [笑]

Mat Ryer: 这个观点不错。那么,Johnny,你接受这个观点了吗?

Johnny Boursiquot: 我做过一些 Ruby 编程,所以我对下划线的可读性非常熟悉,但既然这是 Go Time 播客,我们谈论的是 Go,我得说“不,我不喜欢这个观点。”

Derek Parker: [笑声]

Mat Ryer: 但 Derek,你在写 Go 代码时会用下划线吗?

Derek Parker: 不会……

Mat Ryer: 你真的会在 Go 代码里用下划线命名吗?

Derek Parker: 不会,不会。我还没那么怪。

Johnny Boursiquot: 他知道该怎么做。

Mat Ryer: 那如果你真的这么做了,代码会变成什么样子?会有多糟?

Derek Parker: 我觉得问题出在大写的蛇形命名法让我觉得非常别扭。它在 Go 里行不通,因为大写字母表示导出功能---比如蛇形命名中的首字母大写……这就完全不对劲了。这不对劲。

Mat Ryer: 所有的东西在成为“标准做法”前看起来都不对。

Derek Parker: 对,没错。[笑]

Mat Ryer: 这就是一种趋势。其实没有什么问题。我现在就去试试看,看看是什么样子。嗯,我感觉不太好。我感觉有点恶心。太糟糕了。对……不过这个观点确实不错。我喜欢这种观点。Johnny,你最近有不受欢迎的观点吗?

Johnny Boursiquot: 我的不受欢迎的观点就是,我永远也想不出一个不受欢迎的观点。

Mat Ryer: 是啊,我知道。因为你太受欢迎了。

Johnny Boursiquot: 每个观点最终都会变得很受欢迎,所以我厌倦了不受欢迎的观点,因为它们总是变得很受欢迎。

Mat Ryer: 这太有深意了。你不受欢迎的观点似乎是“我们不应该继续这个环节。”[笑声] 有趣的是,当我们把这些观点发到 Twitter 上时---我真的认为 Grant 是记录保持者,因为……大多数时候,人们会同意这些观点。通常,大家的论点都很有力。

Johnny Boursiquot: 现在我更关注的是有多少人持不同意见的比例。我试图弄清楚究竟有多少人倾向于某个观点。这比去说“哦,这个观点非常不受欢迎”更有趣,因为这种情况不常发生。

Mat Ryer: 是的,确实不常发生。这非常有趣。好的,我们快结束了。我想快速做一个“喊出”(shout-at)环节……这就像“喊出”(shout-out),但我第一次做的时候说错了……

Johnny Boursiquot: 是的,你是在喊着叫。

Mat Ryer: 所以现在变成了“喊出”(shout-at)……我们要“喊出”一个特定的线下活动。今天我要“喊出”的是 Meetup.com 上的 GDN 页面。网址是 meetup.com/pro/go。在那里你可以找到很多资源、本地线下活动、附近的 Gopher 社区,你可以去认识并交流……谁知道呢?你可能会在那里找到对 eBPF 感兴趣的人,然后你们可以一起讨论并加入进来。看看你能否构建一些很酷的东西。

Johnny Boursiquot: 对于那些不知道的人来说,GDN 代表 Go Developer Network(Go 开发者网络)。这是所有 Go 线下活动和事件背后的元组织,甚至包括 GoBridge 等等。它是幕后团队的幕后团队。

Mat Ryer: 是的,它有很多成员。截至目前有 117,000 名成员……你可以成为其中一员;如果你已经是其中一员,你可以让这个数字再增加一个。

Johnny Boursiquot: 是的,加入 GDN,我们会把你的会员证信息邮寄给你。开玩笑的,没有会员证。

Mat Ryer: 我刚才还很兴奋呢。我在想“我要加入这个组织。”

Johnny Boursiquot: [笑] 我们不是发放执照的组织。

Mat Ryer: 哎……这样还有什么意思?我本来想加入的。如果没有徽章……

Johnny Boursiquot: 你想要证书吗?

Mat Ryer: 一个小银色的 Gopher 徽章,你可以到处炫耀,还能享受半价优惠。

Johnny Boursiquot: 我可以私信你我的地址,你可以给我寄张支票……

Mat Ryer: 好吧。那你会给我做个 Gopher 警徽吗?

Johnny Boursiquot: 是的,我会给你画个涂鸦,然后寄给你。

Mat Ryer: 它上面会写“治安官 Mat”吗?[笑声]

Johnny Boursiquot: 你可以做治安官,Mat。你可以做治安官。

Mat Ryer: 我们应该这样做。我们应该在社区里设立等级制度。

Johnny Boursiquot: 等级制度?我们在哪里?[笑声]

Mat Ryer: 是的,是的,现在就像警察部队一样。

Derek Parker: Go 开发者部队,对吧。

Mat Ryer: 对。现在就像政府机构一样。“Mat Ryer,GDN。” 这听起来像一个新闻组织。Johnny,这是它的主页吗?你似乎对此很了解……

Johnny Boursiquot: 是的,这是入门页面。

Mat Ryer: 你怎么知道这么多?

Johnny Boursiquot: 哦……我认识一些认识一些人的人。

Mat Ryer: 听起来像是个可疑的组织。[笑声] “我认识一些认识一些人的人……” 就像,“哦,你这 GitHub 账号不错。真是可惜,如果它出了什么事……” [笑声]

好吧,今天的时间差不多了。希望你喜欢这次关于 eBPF 的深度探讨。非常技术性,也非常有趣……而且相当令人兴奋。我确实想看看 Gopher 们将用它构建什么。我认为这里有一些令人兴奋的机会。如果你用它构建了什么有趣的东西,请发推告诉我们 @GoTimeFM,我们很想听到你的故事。

非常感谢今天的嘉宾。Derek Parker,Grant Seltzer---总是很高兴和你们一起交流。你们一定要再来。当然,还有 Johnny Boursiquot……还有我。就这样吧。再见!

这可能不是我最专业的结尾,但……[笑声] 就这样吧。现在我们要播放结束曲了。

Johnny Boursiquot: 是的。

Mat Ryer: 再见……

Johnny Boursiquot: 再见。



更多参考:

libbpf

libbpf/libbpf

深入浅出 eBPF 安全项目 Tracee

bcc 之opensnoop 工具的使用

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

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

相关文章

Thinkpad E15 在linux下升级 bios

安装xubuntu 24.04后,发现键盘的Fn按键全都无法使用,在Windows环境下是正常的,按说是驱动的问题,网上也有说可以通过升级BIOS解决,所以打算升级看看,升级有风险。 参考: https://blog.stigok.c…

Java学习Day61:薄纱王灵官!(Nginx review)

1.Nginx是什么 Nginx是一款轻量级、高性能,并发性好的HTTP和反向代理服务器 2.功能 2.1反向代理 正向代理是指客户端向代理服务器发送请求,代理服务器代表客户端去访问目标服务器。简单来说,正向代理是客户端的代理,客户端通过…

MATLAB用到的矩阵基础知识(矩阵的乘和矩阵的逆)

1. 矩阵乘法 方法: 设第一个矩阵为 A A A,第二个矩阵为 B B B,则 A A A的第一行乘 B B B的第一列,先想乘再相加,作为目标矩阵的一个元素。 前提条件: 所以我们可以看到矩阵相乘的前提条件:第一个矩阵的列数等于第二个矩阵的行数。否则,我们就无法进行行和列的相乘。 最…

Oracle OCP认证考试考点详解082系列22

题记: 本系列主要讲解Oracle OCP认证考试考点(题目),适用于19C/21C,跟着学OCP考试必过。 105. 第105题: 题目 解析及答案: 题目翻译: 关于Oracle数据库中的事务请选择两个正确的陈述&#xf…

【ict基础软件赛道】真题-50%openGauss

题目取自赛前测试链接 OpenGauss安装前使用哪个工具检查环境是否符合安装哪个功能不是gs_guc工具提供的opengauss数据库逻辑复制的特点描述正确的是opengauss的全密态数据库等值查询能力描述正确的是哪个不属于ssh客户端工具opengauss三权分立说法正确的是opengauss wdr snapsh…

MDK5(Keil5)工具设置及技巧

设置: 1点击扳手(设置) 2文字设置为GB2312简体 3勾选显示空格 4按一下TAB键移动4个空格 修改keil中数字大小及颜色(增加对比) 勾选全部 1提示结构体 2提示函数参数 3打上几个英文符号后开始提示 4TAB作为填充字符 5打开…

4G模组Air780E对json数据处理的基本方法~

4G模组Air780E在数据传输和处理中扮演着越来越重要的角色。在实际应用中,JSON作为一种轻量级的数据交换格式,被广泛应用于网络数据传输和存储。本文将详细介绍4G模组Air780E对JSON数据处理的基本方法,以帮助开发者更好地利用这一模组进行数据…

学习使用LVGL,依赖官方网址

LVGL Basics — LVGL documentation LVGL基础知识 LVGL是一个开源的图形库,提供创建嵌入式GUI的一切 LVGL数据流 您为每个物理显示面板 创建一个显示器 (lv_display) ,在其上创建屏幕小部件,将小部件添加到这些屏幕上。要处理触摸、鼠标、…

【数据库】组合索引生效规则及索引失效

文章目录 索引演示示例组合索引索引失效 索引演示示例 # 创建表结构 CREATE TABLE Employees (EmployeeID INT PRIMARY KEY,FirstName VARCHAR(50),LastName VARCHAR(50),DepartmentID INT,Salary DECIMAL(10, 2),HireDate DATE );# 插入示例数据 INSERT INTO Employees (Empl…

力扣 LeetCode 145. 二叉树的后序遍历(Day6:二叉树)

解题思路&#xff1a; 方法一&#xff1a;递归&#xff08;左右中&#xff09; class Solution {List<Integer> res new ArrayList<>();public List<Integer> postorderTraversal(TreeNode root) {recur(root);return res;}public void recur(TreeNode ro…

用指针遍历数组

#include<stdio.h> int main() {//定义一个二维数组int arr[3][4] {{1,2,3,4},{2,3,4,5},{3,4,5,6},};//获取二维数组的指针int (*p)[4] arr;//二维数组里存的是一维数组int[4]for (int i 0; i < 3; i){//遍历一维数组for (int j 0; j <4; j){printf("%d &…

[HCTF 2018]Warmup 详细题解

知识点: 目录穿越_文件包含 static静态方法 参数传递引用 mb_strpos函数 mb_substr函数 正文: 页面有一张 滑稽 的表情包,查看一下页面源代码,发现提示 那就访问/source.php 得到源码 <?phphighlight_file(__FILE__);class emmm{public static function checkFil…

从0-1训练自己的数据集实现火焰检测

随着工业、建筑、交通等领域的快速发展,火灾作为一种常见的灾难性事件,对生命财产安全造成了严重威胁。为了提高火灾的预警能力,减少火灾损失,火焰检测技术应运而生,成为火灾监控和预防的有效手段之一。 传统的火灾检测方法,如烟雾探测器、温度传感器等,存在响应时间慢…

小程序20-样式:自适应尺寸单位 rpx

手机设备的宽度逐渐多元化&#xff0c;也就需要开发者开发过程中&#xff0c;去适配不同屏幕宽度的手机&#xff0c;为了解决屏幕适配问题&#xff0c;微信小程序推出了 rpx 单位 rpx&#xff1a;小程序新增的自适应单位&#xff0c;可以根据不同设备的屏幕宽度进行自适应缩放 …

在Ubuntu系统中,默认情况下会安装`apt`作为包管理工具(Advanced Package Tool)

文章目录 方法一&#xff1a;使用命令行检查apt是否存在方法二&#xff1a;尝试运行一个apt命令方法三&#xff1a;查看包列表如果apt没有安装主要功能高级用法注意事项 在Ubuntu系统中&#xff0c;默认情况下会安装 apt作为包管理工具。你可以通过以下几种方法来检查系统中是…

Github 2024-11-16Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2024-11-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Go项目1Python项目1Lapce:用 Rust 编写的极快且强大的代码编辑器 创建周期:2181 天开发语言:Rust协议类型:Apache License 2.0St…

C++:基于红黑树封装map和set

目录 红黑树的修改 红黑树节点 红黑树结构 红黑树的迭代器 红黑树Insert函数 红黑树的默认成员函数 修改后完整的红黑树 set、map的模拟实现 set map 测试封装的set和map 红黑树的修改 想要用红黑树封装map和set&#xff0c;需要对之前实现的key-value红黑树进行修…

LeetCode 3240.最少翻转次数使二进制矩阵回文 II:分类讨论

【LetMeFly】3240.最少翻转次数使二进制矩阵回文 II&#xff1a;分类讨论 力扣题目链接&#xff1a;https://leetcode.cn/problems/minimum-number-of-flips-to-make-binary-grid-palindromic-ii/ 给你一个 m x n 的二进制矩阵 grid 。 如果矩阵中一行或者一列从前往后与从后…

在kile 5中一个新工程的创建

这两天博主学习到了在kile5中创建一个工程&#xff0c;当然博主不会忘了小伙伴们的&#xff0c;这就和你们分享。 本次创建以STM32F103C8为例 创建过程&#xff1a; 1首先创建文件 名字随意&#xff0c;但也不要太随意&#xff0c;因为是外国软件&#xff0c;所以多少对中文…

深度学习工具和框架详细指南:PyTorch、TensorFlow、Keras

引言 在深度学习的世界中&#xff0c;PyTorch、TensorFlow和Keras是最受欢迎的工具和框架&#xff0c;它们为研究者和开发者提供了强大且易于使用的接口。在本文中&#xff0c;我们将深入探索这三个框架&#xff0c;涵盖如何用它们实现经典深度学习模型&#xff0c;并通过代码…