服务拓扑串联难?eBPF为滴滴可观测带来解题新思路

Linux阅码场 2023-10-10 08:03

上篇文章我们讲到可观测性在滴滴的实践与落地,更多关注的是不同观测信号之间的关联关系。那服务与服务之间的关系又如何串联,业界当前爆火的 ebpf 又在滴滴有着怎样的应用,本文为你揭晓。

背景

业务介绍:业务接口调用观测

滴滴可观测平台除了负责滴滴 MTL 能力的建设,还涉及更偏向业务侧的数据及服务接口调用观测。


关于接口调用拓扑观测,这里先解释下以免引起歧义。如下图描述了一个调用关系:

一次请求、响应过程


这里用[caller=A, caller-func=/a, callee=B, callee-func=/b],简写成[A, /a, B, /b],以及 [A, /a, C, /c]来描述A服务的/a触发后调用B:/b以及C:/c的动作。在获取到足够多的接口调用数据时,通过给定某个业务的若干个调用入口(如上述示例中的[A, /a]),通过对接口调用链路的不断串联,可以梳理出该业务若干个重要的调用链路。


调用链路的构建对于服务稳定性保障有重要意义,无论是容灾放火、业务按需扩容、高峰期业务状态巡检护堤等均依赖于核心调用链路的构建。从经验上来看,在实际故障处理以及容量评估时,接口级的调用拓扑比服务级或者容器/物理机级的调用拓扑要有效很多。


一般来说,接口粒度的服务拓扑可以通过调用日志或者调用 metric 来进行串联。滴滴可观测早些时候采用调用日志+调用 metric 相结合的方式生成服务接口调用拓扑。后来随着统一服务治理的推进,业务上报 metric 完全可以覆盖调用日志里的调用关系,且生成接口拓扑的成本大幅降低,因此就接口拓扑生成这一场景而言,已经调整为基于服务调用的 metric 数据来生成。

通过metric串联接口拓扑的示意图

业务问题:服务接口拓扑的校验

看起来,通过接口调用 metric 来串联调用链路是一种通用的方式,但是其生成结果显然存在如下的问题:

  • 已生成的数据缺少校验方式。由于数据是业务方代码上报的,即使引入了通用的SDK,caller-func 信息也只能依赖于代码调用时主动传入。从实践经验来看,caller-func 的漏传错传问题比较明显。

  • 调用关系校验、生成成本高昂。依赖业务代码上报,意味着代码需要遵循相当的规范。较为核心的调用链路,推动代码的变更相对容易,业务配合度较高。但非核心的调用链路或已经稳定运行许久的遗留项目,代码的规范化变更是较难推动的。而手动添加则需要对项目进行人工梳理,对于存在近千个调用的链路而言,没有实际操作空间。


述两个问题是使用 metric 串联业务接口拓扑时常见的问题。


以滴滴可观测的实践来看,当核心链路的复杂度达到以千计的量级,即使有专门的团队推动业务调用链路的 metric 接入治理,也会有相当比例的调用关系缺失或者错误。

    

  理想情况下的正常结果        

metric 信息错误时可能的结果


针对服务接口拓扑校验的问题,滴滴可观测通过探索,形成了基于eBPF(后文如无其他说明,简称BPF)技术进行服务接口拓扑无侵入采集的方案。通过 metric+BPF 采集相结合的方式,实现了接口拓扑数据的准确性验证、缺失数据补充。同时,进一步探索了可观测更深层次使用 BPF,如 MTL 的融合。

方案

BPF介绍

BPF 最早是伯克利包过滤器(Berkely Packet Filter)的简称,内核自3.15开始对 BPF 进行扩展,通过增加 BPF 程序寄存器个数、扩充 BPF 程序可使用内存以及增加多个BPF事件使得 BPF 具备高可定制性。为了和扩展前的 BPF进行区分,将3.15之前的BPF称为 cBPF(classic BPF),扩展后的 BPF 称为 eBPF (extended BPF),而 BPF 也从一种缩写更多的成为了一种技术的代称。


截至4.18版本的内核,BPF支持的部分事件类型及其简要介绍如下:


本文涉及的内容有 uprobe 以及 kprobe,大多数的内核函数都可以通过 kprobe 来进行 hook。而在用户自定义程序中,符号表中存在的函数也均可通过 uprobe 进行 hook。


kprobe 和 uprobe 触发时,只能获取目标函数的参数或者堆栈信息。如下面一段代码是通过 bpftrace 来观测 /bin/bash 并通过获取 readline 返回值来观测用户 bash 命令的示例。

#!/usr/bin/bpftrace
BEGIN{ printf("开始观测bash...\n使用Ctrl-C停止\n");}
uretprobe:/bin/bash:readline{ printf("cmd: %s\n", str(retval));}


其中,bash 源码对 readline 的定义如下,参照目标函数的源码可以更好理解BPF 的逻辑。

/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */extern char *readline (const char *);


执行后,当出现目标内核函数执行时,触发如下:

$ sudo bpftrace ./bashreadline.btAttaching 2 probes...开始观测bash...使用Ctrl-C停止cmd: ls -lcmd: pwdcmd: crontab -ecmd: clear


eBPF 在3.15内核引入后,其功能不断扩展。比较重大的一个扩展是在4.18内核中引入了BTF(BPF Type Format),BTF 技术使得 BPF 字节码的加载、使用变得更加简单。


BPF的开发

原生的 BPF 实现各种功能一般是使用受限的C语言调用 bpf-helpers 函数,而后使用 LLVM 将其编译成 BPF-code 字节码,通过系统调用进行加载。原生的C语言编写方式较为繁琐,iovisor 项目推出了 bcc 库来增强 BPF 的开发便捷度,同时维护了支持 one-liner风格、极具易用性的 bpftrace 工具。业内知名的 cilium 也维护了一个 cilium-ebpf。除了bcc、bpftrace、cilium-ebpf,亦有 长于全生产周期支持的 coolbpf、在 libc 基础上使用 rust 提供 BPF 支持的 aya 等工具。

BPF生态,图源自ebpf.io

使用BPF解决服务接口拓扑问题

上一章节提到服务接口拓扑中无法对生成的拓扑数据进行校验,这样的问题目前在滴滴可观测是通过 BPF 来解决。这里通过一个简单的示例以及使用 bpftrace 脚本构建的解决方案来展示下效果。


示例:简单的golang服务

这里给出一个基于go1.16的简单的golang服务。从处理代码中可知,这里的四元组是 [local, /handle, local, /echo]。为了方便示例说明,这里的"handle"的逻辑和请求下游的逻辑是串行的,没有使用"goroutine"。这一点很重要,后面会进行说明。

func echo(c *gin.Context) {  c.JSON(http.StatusOK, &Resp{    Errno: 0,    Errmsg: "ok",  })
return}
/* s := http.Server{ Addr: "0.0.0.0:9932",}r := gin.Default()r.GET("/echo", echo)r.GET("/handle", handle)s.Handler = r*/func handle(c *gin.Context) { client := http.Client{} req, _ := http.NewRequest(http.MethodGet, "http://0.0.0.0:9932/echo", nil) resp, err := client.Do(req) if err != nil { fmt.Println("failed to request", err.Error()) c.JSON(http.StatusOK, &Resp{ Errno: 1, Errmsg: "failed to request", }) return }
respB, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("read resp failed") c.JSON(http.StatusOK, &Resp{ Errno: 2, Errmsg: "failed to read request", }) return }
defer resp.Body.Close()
fmt.Println("resp: ", string(respB)) c.JSON(http.StatusOK, &Resp{ Errno: 0, Errmsg: "request okay",  })
return}


采集的逻辑及执行效果:

uprobe:./http_demo:net/http.serverHandler.ServeHTTP{  $req_addr = sarg3;  $url_addr = *(uint64*)($req_addr+16);  $path_addr = *(uint64*)($url_addr+56);  $path_len = *(uint64*)($url_addr+64);
// 在http请求触发处,依据pid将caller_func存储起来 @caller_path_addr[pid] = $path_addr; @caller_path_len[pid] = $path_len; @callee_set[pid] = 0;}
uprobe:./http_demo:"net/http.(*Client).do"{ // 依据 pid 获取 caller 信息 printf("caller: \n caller_path: %s\n", str(@caller_path_addr[pid], @caller_path_len[pid])); $req_addr = sarg1;
// 获取 callee 信息 $addr = *(uint64*)($req_addr); $len = *(uint64*)($req_addr + 8); printf("callee: \n method: %s\n", str($addr, $len));
$url_addr = *(uint64*)($req_addr + 16); $addr = *(uint64*)($url_addr + 40); $len = *(uint64*)($url_addr + 48); printf(" host: %s\n", str($addr, $len));
$addr = *(uint64*)($url_addr + 56); $len = *(uint64*)($url_addr + 64); printf(" url: %s\n\n", str($addr, $len));
@callee_set[pid] = 1}
uprobe:./http_demo:"net/http.(*response).finishRequest"{ // 如果没有下游请求,单独输出 if (@callee_set[pid] == 0){ printf("caller: \n caller_path: %s\n", str(@caller_path_addr[pid], @caller_path_len[pid])); printf("callee: none\n\n"); @callee_set[pid] = 1;  }}


使用采集脚本进行采集,结果如下:

# 启动采集$ bpftrace ./http.btAttaching 2 probes... # 未触发请求前,停止在这里caller: # 触发请求后,输出caller_path: /handlecallee:  method: GET  host: 0.0.0.0:9932  url: /echocaller:  caller_path: /echo  callee: none
# 开始服务$ ./http_demo &# 触发请求$ curl http://0.0.0.0:9932/handle


可以看到,bpftrace 脚本实现了对目标服务接口调用四元组的采集,而这是在目标服务未进行任何代码变更的情况下进行的,BPF 展示了其在可观测领域的魅力。


实际的方案覆盖及效果

通过上面的示例,展示了使用 BPF 进行接口拓扑观测的主要思路。需要说明的是,示例里使用的是 pid 作为 caller_map 里的 key,但在实际的工程中,由于 golang goroutine 与 pid 并非一一对应的,需要使用 goid 来作为 key。


同时,由于 handleFunc 里会使用新的 goroutine 来发起下游的请求,BPF 也需要对 goid 的派生关系进行维护,以避免某个 goid 关联的 caller 信息丢失。这样一来, 对于 golang 服务而言,实际的处理思路就很明确了。

BPF观测服务拓扑的方案示意


上图是滴滴可观测现行的 golang 接口调用观测 BPF 方案,对方案进行总结,其核心在于:

  • 信息采集。包括 caller-func,callee,callee-func 等信息,均需要通过合适的 hook 点选择来获取。

  • 信息关联。基于 golang 服务的特性,使用 goid 进行关联。这就使得 caller 信息能够和 callee 信息相关联,以获取四元组。


目前滴滴可观测基于这样的思路,完成了对 golang 和 PHP 服务的覆盖。从实践结果来看,该方案对目标服务有效覆盖率约 80%。目标监控核心调用链路,经对 BPF 新增四元组的人工确认,无异常四元组。与基于 metric 的数据相对比,在部分核心调用链路,新增四元组调用可达20%。

问题

丢掉的关联性

上述方案确实是目前能够想到较为直观的方案。其中信息采集部分问题不大,虽使用了 uprobe,引入了对目标函数参数的依赖,但是就实际生产环境上使用的go1.10~go1.20而言,除了 go1.17 引入的函数调用规约需要适配外,其他必要的信息基本上没有变化。


信息关联部分比较麻烦,现有方案里是通过维护 goroutine 的派生关系来实现 caller 信息和 callee信息的关联,但现实往往不尽如意。比如,从实际的工程来看,下面的代码是会出现的:

/*用法1:通过channel来传递request。这种场景下,事件间的关联性丢失,无法形成四元组*/
var reqChan = make(chan *http.Request, 10)
func handle(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "Hello, World\n") reqChan <- req // 这里通过channel来传递请求 return}
func handleReq() { for { select { case req, ok := <-reqChan: if !ok { log.Println("channel closed") return }
log.Println("received, ", req.Host, req.Method) // do some stuff // 即使这里存在下游请求,也无法和caller关联起来。 } }}
func main() { go handleReq() http.HandleFunc("/hello", handle) http.ListenAndServe("0.0.0.0:9999", nil) return}
type GoroutinePool interface { Start() (error, bool) AddTask(func()) Stop() (error, bool)}
var pool GoroutinePool
func handle(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "Hello, World\n")
pool.AddTask(func() { // 这里由于采用了goroutine池,goroutine间的派生关系 会丢失,事件无法有效串联 handleReq(req) }) return}
func handleReq(req *http.Request) { log.Println("received, ", req.Host, req.Method) // do some stuff}
func main() { // init pool // pool = New() http.HandleFunc("/hello", handle) http.ListenAndServe("0.0.0.0:9999", nil) return}

上述的两个场景由于无法获取 goroutine 的派生关系,现有的方案将无法获取四元组,类似的问题会影响 BPF 的采集效果。从现有经验来看,golang 工程中受类似代码影响的四元组占比在20%以内。

uprobe:适配的复杂性

经过上节的介绍,可知滴滴可观测是基于 uprobe 构建的服务接口拓扑观测方案。


BPF uprobe 的使用具有处理数据高效、整体方案直观的特点。由于 uprobe 更接近于用户的代码,因此对于用户感知较强的问题更加得心应手,如框架中慢函数调用等。


但大多数的项目使用更多的是 kprobe,比如 bpftrace 中的很多实用工具。deepflow 的观测能力大都是在 kprobe 的基础上构建的,kindling 涉及网络数据处理的内容也是基于 kprobe 进行处理的。


目前在实际使用中,完全按照 uprobe 构建方案的项目仍属少数。究其原因, uprobe 的使用存在如下两个缺点:

  • 通用性较差。通过方案介绍可知,基于 uprobe 的方案和语言(甚至是框架)是强相关的。且在目标程序符号表不存在的情况下,uprobe 无法进行工作。这意味着如果目标使用场景不明确,使用 uprobe 就需要对每个具体的场景进行适配,整体的投入、产出将会很低。

  • 性能问题。uprobe 触发时,会涉及到用户态和内核态的两次切换,这意味着单次执行 uprob 时,其性能开销很高(单个 uprobe 的触发耗时在1us左右,而单个 kprobe 的触发耗时则在100ns左右)。当被 hook 的函数频繁触发时,目标进程的性能将会很差。


尽管 uprobe 存在上述所说的缺陷,滴滴可观测仍然选择了基于 uprobe 来构建方案,主要因为 uprobe 的开发效率更快,成本更低。


使用 uprobe 来开发,所见即所得。数据不存在退化,关键信息无须从传输层报文中获取。不仅节省了开发时间,处理的复杂性也大大降低:考虑一个长 http 报文,uprobe 可以直接从目标函数获取需要的数据,比如 URL 信息,而 kprobe 则会触发多次,且需要对报文进行解析以获取所需要的信息。就目前来看,滴滴可观测的 ebpf-agent 线上实际 CPU 开销常态在单核的10%以下(一般的业务进程,含 PHP 进程,路由 nginx 服务CPU会高些),对目标进程的性能影响几乎不会被感知。

展望

用户态VM的需求

滴滴可观测使用了大量的 uprobe ,在离线环境上,单个物理机常态运行1500多个 uprobe 的 hook 点。将来随着 BPF 功能的延伸,uprobe hook 点的数量还会增加。大量的 uprobe 放到内核中,不仅对内核造成稳定性压力,而且由于BPF VM 运行在内核态,使得 uprobe 触发时会导致程序触发内核态和用户态的2次切换,对目标进程的函数执行造成延迟。


这两点都让用户态的 VM 使用无法避免。只有将 uprobe 切换到用户态的 VM 执行,uprobe 的耗时才能降下来,大规模使用 uprobe 才不会对目标服务造成太大的影响。

基于BPF的MTL融合方案

当我们重新审视 bpf-helpers 时可以看到这样一个有意思的函数:

long bpf_probe_write_user(void *dst, const void *src, u32 len) DescriptionAttempt in a safe way to write len bytes from the buffer src to dst in memory. It only works for threads that are in user context, and dst must be a valid user space address. This helper should not be used to implement any kind of security mechanism because of TOC-TOU attacks, but rather to debug, divert, and manipulate execution of semi-cooperative processes. Keep in mind that this feature is meant for experiments, and it has a risk of crashing the system and running programs.  Therefore, when an eBPF program using this helper is attached, a warning including PID and process name is printed to kernel logs. Return 0 on success, or a negative error in case of failure.


这个函数的功能就强大了,意味着 BPF 的数据可以直接写入目标进程的空间,扩充了 BPF 的使用范围。而在 MTL 融合的过程中, 比较棘手的问题是 trace 信息无法有效关联到 metric 以及 log 中。

     

原始的MTL融合方案


如上图所示,当 metric 或者 log 上报时没有上报正确的 trace 信息,则 metric 及 log 将无法关联到 trace 中。


而如果每个请求的处理链路被 BPF 正常维护,且 BPF 维护了该请求的 trace 信息,metric 和日志在生成时,自然就可以和trace关联起来。下图分别展示了  BPF 增强的三种方案:

BPF增强的MTL融合方案

     

BPF+SDK的MTL融合方案


BPF为主的MTL融合方案

总结

有了各种观测采集手段,收集了大量的观测数据。这些数据是直接事无巨细地交付给用户,还是按指定维度聚合后展示,聚合使用什么样的计算引擎,spark 还是 flink?


下篇文章将为您呈现滴滴的可观测团队是如何实现数据计算的,敬请期待。





云原生夜话




你期待eBPF技术能够解决可观测的哪些问题?欢迎在评论区留言,如需与我们进一步交流探讨,也可直接私信后台

作者将选取1则最有意义的留言,送出滴滴元气牛仔托特包,9月21日晚9点开奖。


Linux阅码场 专业的Linux技术社区和Linux操作系统学习平台,内容涉及Linux内核,Linux内存管理,Linux进程管理,Linux文件系统和IO,Linux性能调优,Linux设备驱动以及Linux虚拟化和云计算等各方各面.
评论
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 170浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 80浏览
  • 彼得·德鲁克被誉为“现代管理学之父”,他的管理思想影响了无数企业和管理者。然而,关于他的书籍分类,一种流行的说法令人感到困惑:德鲁克一生写了39本书,其中15本是关于管理的,而其中“专门写工商企业或为企业管理者写的”只有两本——《为成果而管理》和《创新与企业家精神》。这样的表述广为流传,但深入探讨后却发现并不完全准确。让我们一起重新审视这一说法,解析其中的矛盾与根源,进而重新认识德鲁克的管理思想及其著作的真正价值。从《创新与企业家精神》看德鲁克的视角《创新与企业家精神》通常被认为是一本专为企业管
    优思学院 2025-01-06 12:03 113浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 55浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 66浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 180浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 103浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 141浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 98浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 83浏览
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 40浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 125浏览
  • 这篇内容主要讨论三个基本问题,硅电容是什么,为什么要使用硅电容,如何正确使用硅电容?1.  硅电容是什么首先我们需要了解电容是什么?物理学上电容的概念指的是给定电位差下自由电荷的储藏量,记为C,单位是F,指的是容纳电荷的能力,C=εS/d=ε0εrS/4πkd(真空)=Q/U。百度百科上电容器的概念指的是两个相互靠近的导体,中间夹一层不导电的绝缘介质。通过观察电容本身的定义公式中可以看到,在各个变量中比较能够改变的就是εr,S和d,也就是介质的介电常数,金属板有效相对面积以及距离。当前
    知白 2025-01-06 12:04 167浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 87浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 63浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦