通过实例讲解Linux内存泄漏检测方法

一起学嵌入式 2023-09-11 07:50

扫描关注一起学嵌入式,一起学习,一起成长

一、mtrace分析内存泄露

mtrace(memory trace),是 GNU Glibc 自带的内存问题检测工具,它可以用来协助定位内存泄露问题。
它的实现源码在glibc源码的malloc目录下,其基本设计原理为设计一个函数 void mtrace (),函数对 libc 库中的 malloc/free 等函数的调用进行追踪,由此来检测内存是否存在泄漏的情况。mtrace是一个C函数,在里声明及定义,函数原型为:
void mtrace(void);

mtrace原理

mtrace() 函数会为那些和动态内存分配有关的函数(譬如 malloc()、realloc()、memalign() 以及 free())安装 “钩子(hook)” 函数,这些 hook 函数会为我们记录所有有关内存分配和释放的跟踪信息,而 muntrace() 则会卸载相应的 hook 函数。

基于这些 hook 函数生成的调试跟踪信息,我们就可以分析是否存在 “内存泄露” 这类问题了。

设置日志生成路径

mtrace 机制需要我们实际运行一下程序,然后才能生成跟踪的日志,但在实际运行程序之前还有一件要做的事情是需要告诉 mtrace (即前文提到的 hook 函数)生成日志文件的路径。

设置日志生成路径有两种,一种是设置环境变量:export MALLOC_TRACE=./test.log // 当前目录下 另一种是在代码层面设置:setenv("MALLOC_TRACE", "output_file_name", 1);``output_file_name就是储存检测结果的文件的名称。

测试实例

#include 
#include 
#include 

int main(int argc, char **argv)
{
    mtrace();  // 开始跟踪

    char *p = (char *)malloc(100);
    free(p);
    p = NULL;
    p = (char *)malloc(100);

    muntrace();   // 结束跟踪,并生成日志信息
    return 0;
}
从上述代码中,我们希望能够在程序开始到结束检查内存是否泄漏的问题,例子简单,一眼就能看出存在内存泄漏的问题,所以我们需要验证 mtrace 是否能够检查出来内存泄漏问题,且检查的结果如何分析定位。 gcc -g test.c -o test生成可执行文件。

日志

程序运行结束,会在当前目录生成 test.log 文件,打开可以看到一下内容:
= Start
@ ./test:[0x400624] + 0x21ed450 0x64
@ ./test:[0x400634] - 0x21ed450
@ ./test:[0x400646] + 0x21ed450 0x64
= End
从这个文件中可以看出中间三行分别对应源码中的 malloc -> free -> malloc 操作。解读:./test 指的是我们执行的程序名字,[0x400624] 是第一次调用 malloc 函数机器码中的地址信息,+ 表示申请内存( - 表示释放),0x21ed450 是 malloc 函数申请到的地址信息,0x64 表示的是申请的内存大小。
由此分析第一次申请已经释放,第二次申请没有释放,存在内存泄漏的问题。

泄露分析

使用addr2line工具定位源码位置

通过使用 "addr2line" 命令工具,得到源文件的行数(通过这个可以根据机器码地址定位到具体源码位置)
# addr2line -e test 0x400624
/home/test.c:9

使用mtrace工具分析日志信息

mtrace + 可执行文件路径 + 日志文件路径 mtrace test ./test.log执行,输出如下信息:
Memory not freed:
-----------------
           Address     Size     Caller
0x00000000021ed450     0x64  at /home/test.c:14

二、Valgrind分析内存泄露

Valgrind工具介绍

Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。
内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。Valgrind的体系结构如下图所示

1、Memcheck

最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc() / free() / new / delete 的调用都会被捕获。

所以,它能检测以下问题:对未初始化内存的使用;读/写释放后的内存块;读/写超出malloc分配的内存块;读/写不适当的栈中内存块;内存泄漏,指向一块内存的指针永远丢失;不正确的malloc/free或new/delete匹配;memcpy()相关函数中的dst和src指针重叠。

2、Callgrind

和 gprof 类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和 gprof 不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。

Callgrind 收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行 cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate 可以把这个文件的内容转化成可读的形式。

3、Cachegrind

Cache 分析器,它模拟 CPU 中的一级缓存 I1,Dl 和二级缓存,能够精确地指出程序中 cache 的丢失和命中。如果需要,它还能够为我们提供 cache 丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

4、Helgrind

它主要用来检查多线程程序中出现的竞争问题。Helgrind 寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。

Helgrind 实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind 仍然处于实验阶段。

5、Massif

堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。

Massif 能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

此外,lackey 和 nulgrind 也会提供。Lackey 是小型工具,很少用到;Nulgrind 只是为开发者展示如何创建一个工具。

Memcheck原理

本文的重点是在检测内存泄露,所以对于valgrind的其他工具不做过多的说明,主要说明下Memcheck的工作。Memcheck检测内存问题的原理如下图所示:

Memcheck 能够检测出内存问题,关键在于其建立了两个全局表。
  • Valid-Value 表 对于进程整个地址空间中的每一个字节(byte),都有与之对应的 8个bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。

  • Valid-Address 表 对于进程整个地址空间中的每一个字节(byte),还有与之对应的1个 bit,负责记录该地址是否能够被读写。

  • 检测原理:当要读写内存中某个字节时,首先检查这个字节对应的Valid-Address 表中的 A bit。如果该 A bit显示该位置是无效位置,memcheck 则报告读写错误。内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的Valid-Value 表中的 V bit 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。

内存泄露类型

valgrind 将内存泄漏分成 4 类:

  • 确立泄露(definitely lost):运行内存还没有释放出来,但早已沒有表针偏向运行内存,运行内存早已不能浏览。确立泄露的运行内存是强烈要求修补的。
  • 间接性泄露(indirectly lost):泄露的运行内存表针储存在确立泄露的运行内存中,伴随着确立泄露的运行内存不能浏览,造成间接性泄露的运行内存也不能浏览。例如:

struct list {
 struct list *next;
};

int main(int argc, char **argv)
{
 struct list *root;
 root = (struct list *)malloc(sizeof(struct list));
 root->next = (struct list *)malloc(sizeof(struct list));
 printf("root %p roop->next %p\n", root, root->next);
 root = NULL;
 return 0;
}
这里遗失的是 root 表针(是确立泄露类型),造成 root 储存的 next 表针变成了间接性泄露。间接性泄露的运行内存毫无疑问也要修补的,但是一般会伴随着 确立泄露 的修补而修补。
  • 很有可能泄露(possibly lost):表针并不偏向运行内存头详细地址,只是偏向运行内存內部的部位。valgrind 往往会猜疑很有可能泄露,是由于表针早已偏位,并沒有偏向运行内存头,只是有运行内存偏位,偏向运行内存內部的部位。有一些情况下,这并并不是泄露,由于这种程序流程便是那么设计方案的,比如为了更好地完成内存对齐,附加申请办理运行内存,回到两端对齐后的内存地址。
  • 仍可访达(still reachable):表针一直存有且偏向运行内存头顶部,直到程序流程撤出时运行内存还没有释放出来。

Valgrind参数设置

  • --leak-check= 如果设为 yes 或 full,在被调程序结束后,valgrind 会详细叙述每一个内存泄露情况 默认是summary,只报道发生了几次内存泄露
  • --log-fd= [default: 2, stderr] valgrind 打印日志转存到指定文件或者文件描述符。如果没有这个参数,valgrind 的日志会连同用户程序的日志一起输出,会显得非常乱。
  • --trace-children= [default: no] 是否跟踪子进程,若是多进程的程序,则建议使用这个功能。不过单进程使能了也不会有多大影响。
  • --keep-debuginfo= [default: no] 如果程序有使用 动态加载库(dlopen),在动态库卸载时(dlclose),debug信息都会被清除。使能这个选项后,即使动态库被卸载,也会保留调用栈信息。
  • --keep-stacktraces= [default: alloc-and-free] 内存泄漏不外乎申请和释放不配对,函数调用栈是只在申请时记录,还是在申请释放时都记录 如果我们只关注内存泄漏,其实完全没必要申请释放都记录,因为这会占用非常多的额外内存和更多的 CPU 损耗,让本来就执行慢的程序雪上加霜。
  • --freelist-vol= 当客户程序用 free 或 delete 释放一个内存块时,这个内存块不会立即可用于再分配,它只会被放在一个freed blocks的队列中(freelist)并被标记为不可访问,这样有利于探测到在一段很重要的时间后,客户程序又对被释放的块进行访问的错误。这个选项规定了队列所占的字节块大小,默认是20MB。增大这个选项的会增大memcheck的内存开销,但查这类错的能力也会提升。
  • --freelist-big-blocks= 当从 freelist 队列中取可用内存块用于再分配时,memcheck 将会从那些比 number 大的内存块中按优先级取出一个块出来用。这个选项就防止了 freelist 中那些小的内存块的频繁调用,这个选项提高了 查到针对小内存块的野指针错误的几率。若这个选项设为0,则所有的块将按先进先出的原则用于再分配。默认是1M。参考:valgrind 简介(内存检查工具)

编译参数推荐

为了更好地在出难题时要详尽打印出出去栈信息内容,实际上大家最好是在编译程序时加上 -g 选择项。如果有动态性载入的库,必须再加上 --keep-debuginfo=yes ,不然假如发觉是动态性载入的库发生泄露,因为动态库被卸载掉了,造成找不到符号表。编码编译程序提升,不建议应用 -O2既之上。-O0很有可能会造成运作变慢,建议使用-O1。

检测实例说明

申请不释放内存

#include 
#include 
void func()
{
  //只申请内存而不释放
    void *p=malloc(sizeof(int));
}
int main()
{
    func();
    return 0;
}

使用valgrind命令来执行程序同时输出日志到文件

valgrind --log-file=valReport --leak-check=full --show-reachable=yes --leak-resolution=low ./a.out
参数说明:
  • –log-file=valReport 是指定生成分析日志文件到当前执行目录中,文件名为valReport

  • –leak-check=full 显示每个泄露的详细信息

  • –show-reachable=yes 是否检测控制范围之外的泄漏,比如全局指针、static指针等,显示所有的内存泄露类型

  • –leak-resolution=low 内存泄漏报告合并等级

  • –track-origins=yes表示开启“使用未初始化的内存”的检测功能,并打开详细结果。如果没有这句话,默认也会做这方面的检测,但不会打印详细结果。执行输出后,报告解读,其中54017是指进程号,如果程序使用了多进程的方式来执行,那么就会显示多个进程的内容。

==54017== Memcheck, a memory error detector
==54017== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==54017== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==54017== Command: ./a.out
==54017== Parent PID: 52130

第二段是对堆内存分配的总结信息,其中提到程序一共申请了1次内存,其中0次释放了,4 bytes被分配(1 allocs, 0 frees, 4 bytes allocated)。

在head summary中,有该程序使用的总heap内存量,分配内存次数和释放内存次数,如果分配内存次数和释放内存次数不一致则说明有内存泄漏。

==54017== HEAP SUMMARY:
==54017==   in use at exit: 4 bytes in 1 blocks
==54017==   total heap usage: 1 allocs, 0 frees, 4 bytes allocated

第三段的内容描述了内存泄露的具体信息,其中有一块内存占用4字节(4 bytes in 1 blocks),在调用malloc分配,调用栈中可以看到是func函数最后调用了malloc,所以这一个信息是比较准确的定位了我们泄露的内存是在哪里申请的。

==54017== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==54017==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==54017==    by 0x40057E: func() (in /home/oceanstar/CLionProjects/Share/src/a.out)
==54017==    by 0x40058D: main (in /home/oceanstar/CLionProjects/Share/src/a.out)

最后这一段是总结,4字节为一块的内存泄露。

==54017== LEAK SUMMARY:
==54017==    definitely lost: 4 bytes in 1 blocks  // 确立泄露
==54017==    indirectly lost: 0 bytes in 0 blocks  // 间接性泄露
==54017==    possibly lost: 0 bytes in 0 blocks   // 很有可能泄露
==54017==    still reachable: 0 bytes in 0 blocks // 仍可访达
==54017==    suppressed: 0 bytes in 0 blocks

读写越界

#include 
#include 
int main()
{
    int len = 5;
    int *pt = (int*)malloc(len*sizeof(int)); //problem1: not freed
    int *p = pt;
    for (int i = 0; i < len; i++){
        p++;
    }
    *p = 5; //problem2: heap block overrun
    printf("%d\n", *p); //problem3: heap block overrun
    // free(pt);
    return 0;
}

problem1: 指针pt申请了空间,但是没有释放; problem2: pt申请了5个int的空间,p经过5次循环已达到p[5]的位置, *p = 5时,访问越界(写越界)。(下面valgrind报告中 Invalid write of size 4)

==58261== Invalid write of size 4
==58261==    at 0x400707: main (main.cpp:12)
==58261==  Address 0x5a23054 is 0 bytes after a block of size 20 alloc'd
==58261==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==58261==    by 0x4006DC: main (main.cpp:7)

problem1: 读越界 (下面valgrind报告中 Invalid read of size 4 )

==58261== Invalid read of size 4
==58261==    at 0x400711: main (main.cpp:13)
==58261==  Address 0x5a23054 is 0 bytes after a block of size 20 alloc'd
==58261==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==58261==    by 0x4006DC: main (main.cpp:7)

重复释放

#include 
#include 
int main()
{
    int *x;
    x = static_cast(malloc(8 * sizeof(int)));
    x = static_cast(malloc(8 * sizeof(int)));
    free(x);
    free(x);
    return 0;
}

报告如下,Invalid free() / delete / delete[] / realloc()

==59602== Invalid free() / delete / delete[] / realloc()
==59602==    at 0x4C2B06D: free (vg_replace_malloc.c:540)
==59602==    by 0x4006FE: main (main.cpp:10)
==59602==  Address 0x5a230a0 is 0 bytes inside a block of size 32 free'd
==59602==    at 0x4C2B06D: free (vg_replace_malloc.c:540)
==59602==    by 0x4006F2: main (main.cpp:9)
==59602==  Block was alloc'
d at
==59602==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==59602==    by 0x4006E2: main (main.cpp:8)

申请释放接口不匹配

申请释放接口不匹配的报告如下,用malloc申请空间的指针用free释放;用new申请的空间用delete释放(Mismatched free() / delete / delete []):

==61950== Mismatched free() / delete / delete []
==61950==    at 0x4C2BB8F: operator delete[](void*) (vg_replace_malloc.c:651)
==61950==    by 0x4006E8: main (main.cpp:8)
==61950==  Address 0x5a23040 is 0 bytes inside a block of size 5 alloc'd
==61950==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==61950==    by 0x4006D1: main (main.cpp:7)

内存覆盖

int main()
{
    char str[11];
    for (int i = 0; i < 11; i++){
        str[i] = i;
    }
    memcpy(str + 1, str, 5);
    char x[5] = "abcd";
    strncpy(x + 2, x, 3);
}

问题出在memcpy上, 将str指针位置开始copy 5个char到str+1所指空间,会造成内存覆盖。strncpy也是同理。报告如下,Source and destination overlap

==61609== Source and destination overlap in memcpy(0x1ffefffe31, 0x1ffefffe30, 5)
==61609==    at 0x4C2E81D: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1035)
==61609==    by 0x400721: main (main.cpp:11)
==61609== 
==61609== Source and destination overlap in strncpy(0x1ffefffe25, 0x1ffefffe23, 3)
==61609==    at 0x4C2D453: strncpy (vg_replace_strmem.c:552)
==61609==    by 0x400748: main (main.cpp:14)

三、总结

内存检测方式无非分为两种:

1、维护一个内存操作链表,当有内存申请操作时,将其加入此链表中,当有释放操作时,从申请操作从链表中移除。如果到程序结束后此链表中还有内容,说明有内存泄露了;如果要释放的内存操作没有在链表中找到对应操作,则说明是释放了多次。使用此方法的有内置的调试工具,Visual Leak Detecter,mtrace, memwatch, debug_new。
2、模拟进程的地址空间。仿照操作系统对进程内存操作的处理,在用户态下维护一个地址空间映射,此方法要求对进程地址空间的处理有较深的理解。因为Windows的进程地址空间分布不是开源的,所以模拟起来很困难,因此只支持Linux。采用此方法的是 valgrind。

文章来源于网络,版权归原作者所有,如有侵权,请联系删除。



个人微信开放,扫码添加,进高质量嵌入式交流群


关注我【一起学嵌入式】,一起学习,一起成长。



觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论
  •         霍尔传感器是根据霍尔效应制作的一种磁场传感器。霍尔效应是磁电效应的一种,这一现象是霍尔(A.H.Hall,1855—1938)于1879年在研究金属的导电机构时发现的。后来发现半导体、导电流体等也有这种效应,而半导体的霍尔效应比金属强得多,利用这现象制成的各种霍尔元件,广泛地应用于工业自动化技术、检测技术及信息处理等方面。霍尔效应是研究半导体材料性能的基本方法。通过霍尔效应实验测定的霍尔系数,能够判断半导体材料的导电类型、载流子浓度及载流子
    锦正茂科技 2024-12-10 11:07 64浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 70浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 49浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 74浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-10 16:13 105浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 66浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 84浏览
  • 我的一台很多年前人家不要了的九十年代SONY台式组合音响,接手时只有CD功能不行了,因为不需要,也就没修,只使用收音机、磁带机和外接信号功能就够了。最近五年在外地,就断电闲置,没使用了。今年9月回到家里,就一个劲儿地忙着收拾家当,忙了一个多月,太多事啦!修了电气,清理了闲置不用了的电器和电子,就是一个劲儿地扔扔扔!几十年的“工匠式”收留收藏,只能断舍离,拆解不过来的了。一天,忽然感觉室内有股臭味,用鼻子的嗅觉功能朝着臭味重的方向寻找,觉得应该就是这台组合音响?怎么会呢?这无机物的东西不会腐臭吧?
    自做自受 2024-12-10 16:34 136浏览
  • 概述 通过前面的研究学习,已经可以在CycloneVGX器件中成功实现完整的TDC(或者说完整的TDL,即延时线),测试结果也比较满足,解决了超大BIN尺寸以及大量0尺寸BIN的问题,但是还是存在一些之前系列器件还未遇到的问题,这些问题将在本文中进行详细描述介绍。 在五代Cyclone器件内部系统时钟受限的情况下,意味着大量逻辑资源将被浪费在于实现较大长度的TDL上面。是否可以找到方法可以对此前TDL的长度进行优化呢?本文还将探讨这个问题。TDC前段BIN颗粒堵塞问题分析 将延时链在逻辑中实现后
    coyoo 2024-12-10 13:28 101浏览
  • 智能汽车可替换LED前照灯控制运行的原理涉及多个方面,包括自适应前照灯系统(AFS)的工作原理、传感器的应用、步进电机的控制以及模糊控制策略等。当下时代的智能汽车灯光控制系统通过车载网关控制单元集中控制,表现特殊点的有特斯拉,仅通过前车身控制器,整个系统就包括了灯光旋转开关、车灯变光开关、左LED前照灯总成、右LED前照灯总成、转向柱电子控制单元、CAN数据总线接口、组合仪表控制单元、车载网关控制单元等器件。变光开关、转向开关和辅助操作系统一般连为一体,开关之间通过内部线束和转向柱装置连接为多,
    lauguo2013 2024-12-10 15:53 81浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 47浏览
  •         在有电流流过的导线周围会感生出磁场,再用霍尔器件检测由电流感生的磁场,即可测出产生这个磁场的电流的量值。由此就可以构成霍尔电流、电压传感器。因为霍尔器件的输出电压与加在它上面的磁感应强度以及流过其中的工作电流的乘积成比例,是一个具有乘法器功能的器件,并且可与各种逻辑电路直接接口,还可以直接驱动各种性质的负载。因为霍尔器件的应用原理简单,信号处理方便,器件本身又具有一系列的du特优点,所以在变频器中也发挥了非常重要的作用。  &nb
    锦正茂科技 2024-12-10 12:57 76浏览
  • 【萤火工场CEM5826-M11测评】OLED显示雷达数据本文结合之前关于串口打印雷达监测数据的研究,进一步扩展至 OLED 屏幕显示。该项目整体分为两部分: 一、框架显示; 二、数据采集与填充显示。为了减小 MCU 负担,采用 局部刷新 的方案。1. 显示框架所需库函数 Wire.h 、Adafruit_GFX.h 、Adafruit_SSD1306.h . 代码#include #include #include #include "logo_128x64.h"#include "logo_
    无垠的广袤 2024-12-10 14:03 69浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦