eBPF/Ftrace双剑合璧:nospaceleftondevice无处遁形

Linux阅码场 2022-03-31 08:00

最近在生产环境中遇到了几次创建容器报错 ”no space left on device“ 失败的案例,但是排查过程中发现磁盘使用空间和 inode 都比较正常。在常规的排查方式都失效的情况下,有没有快速通用思路可以定位问题根源呢?

本文是在单独环境中使用 eBPF + Ftrace 分析和排查问题流程的记录,考虑到该方式具有一定的通用性,特整理记录,希望能够起到抛砖引玉的作用。

作者水平有限,思路仅供参考,难免存在某些判断或假设存在不足,欢迎各位专家批评指正。

1. ”no space left on device“ ???

本地复现的方式与生产环境中的问题产生的根源并不完全一致,仅供学习使用。

在机器上运行 docker run ,系统提示 “no space left on device” ,容器创建失败:

$ sudo docker run --rm -ti busybox ls -hl|wc -l
docker: Error response from daemon: error creating overlay mount to /var/lib/docker/overlay2/40de1c588e43dea75cf80a971d1be474886d873dddee0f00369fc7c8f12b7149-init/merged:
no space left on device.
See 'docker run --help'.

错误提示信息表明在 overlay mount 过程中磁盘空间不足,通过 df -Th 命令进行确定磁盘空间情况,如下所示:

$ sudo df -Th
Filesystem             Type        Size  Used Avail Use% Mounted on
tmpfs                  tmpfs       392M  1.2M  391M   1% /run
/dev/sda1              ext4         40G   19G   22G  46% /
tmpfs                  tmpfs       2.0G     0  2.0G   0% /dev/shm
tmpfs                  tmpfs       5.0M     0  5.0M   0% /run/lock
/dev/sda15             vfat         98M  5.1M   93M   6% /boot/efi
tmpfs                  tmpfs       392M  4.0K  392M   1% /run/user/1000
overlay                overlay      40G   19G   22G  46% /root/overlay2/merged

但磁盘空间使用情况表明,我们挂载的 overlay 设备使用率仅为 46%。根据过往排查经验,我记得文件系统中 inode 如耗尽也可能导致这种情况发生,使用 df -i 对于 inode 容量进行查看:

$ sudo df -i
Filesystem                 Inodes  IUsed      IFree IUse% Mounted on
tmpfs                      500871    718     500153    1% /run
/dev/sda1                 5186560 338508    4848052    7% /
tmpfs                      500871      1     500870    1% /dev/shm
tmpfs                      500871      3     500868    1% /run/lock
/dev/sda15                      0      0          0     - /boot/efi
tmpfs                      100174     29     100145    1% /run/user/1000
overlay                   5186560 338508    4848052    7% /root/overlay2/merged

从 inode 的情况看,overlay 文件系统中的 inode 使用量仅为 7%,那么是否存在文件被删除了,但仍被使用使用延迟释放导致句柄泄露?抱着最后一根稻草,使用 lsof |grep deleted 命令进行查看,结果也一无所获:

$ sudo lsof | grep deleted
empty

常见的错误场景都进行了尝试后,仍然没有线索,问题一下子陷入了僵局。在常规排查思路都失效的情况下,作为问题排查者有没有一种不过于依赖内核专业人员进行定位问题的方式呢?

答案是肯定的,今天舞台的主角属于 ftrace 和 eBPF (BCC[1] 基于 eBPF 技术开发工具集)。

2. 问题分析及定位

2.1 初步锁定问题函数

常规的方式,是通过客户端源码逐步分析,层层递进,但是在容器架构中会涉及到 Docker -> Dockerd -> Containerd -> Runc 一系列链路,分析的过程会略微繁琐,而且也需要一定的容器架构专业知识。

因此,这里我们通过系统调用的错误码来快速确定问题,该方式有一定的经验和运气成分。在时间充裕的情况下,还是推荐源码逐步定位分析的方式,既能排查问题也能深入学习。

”no space left on device“ 的错误,在内核 include/uapi/asm-generic/errno-base.h 文件定义:

一般可以拿报错信息在内核中直接搜索。

#define ENOSPC  28 /* No space left on device */

BCC 提供了系统调用跟踪工具 syscount-bpfcc[2],可基于错误码来进行过滤,同时该工具也提供了 -x 参数过滤失败的系统调用,在诸多场景中都非常有用。

请注意,syscount-bpfcc 后缀 bpfcc 为 Unbuntu 系统中专有后缀,BCC 工具中的源码为 syscount。

首先我们先简单了解一下 syscount-bpfcc 工具的使用帮助:

$ sudo syscount-bpfcc -h
usage: syscount-bpfcc [-h] [-p PID] [-i INTERVAL] [-d DURATION] [-T TOP] [-x] [-e ERRNO] [-L] [-m] [-P] [-l]

Summarize syscall counts and latencies.

optional arguments:
  -h, --help            show this help message and exit
  -p PID, --pid PID     trace only this pid
  -i INTERVAL, --interval INTERVAL
                        print summary at this interval (seconds)
  -d DURATION, --duration DURATION
                        total duration of trace, in seconds
  -T TOP, --top TOP     print only the top syscalls by count or latency
  -x, --failures        trace only failed syscalls (return < 0)
  -e ERRNO, --errno ERRNO
                        trace only syscalls that return this error (numeric or EPERM, etc.)
  -L, --latency         collect syscall latency
  -m, --milliseconds    display latency in milliseconds (default: microseconds)
  -P, --process         count by process and not by syscall
  -l, --list            print list of recognized syscalls and exit

在 syscount-bpfcc 参数中,通过 -e 参数指定我们要过滤返回 ENOSPEC 错误的系统调用:

$ sudo syscount-bpfcc -e ENOSPC
Tracing syscalls, printing top 10... Ctrl+C to quit.
^C[08:34:38]
SYSCALL                   COUNT
mount                         1

跟踪结果表明系统调用 mount 返回了 ENOSPEC 错误。

如果需要确定出错系统调用 mount 的调用程序,我们可通过 "-P" 参数来按照进程聚合显示:

$ sudo syscount-bpfcc -e ENOSPC -P
Tracing syscalls, printing top 10... Ctrl+C to quit.
^C[08:35:32]
PID    COMM               COUNT
3010   dockerd                1

跟踪结果表明,dockerd 后台进程调用的系统调用 mount 返回了 ENOSPEC 错误 。

syscount-bpfcc 可通过参数 -p 指定进程 pid 进行跟踪,适用于我们确定了特定进程后进行排查的场景。如果有兴趣查看相关实现的代码,可在命令行后添加 --ebpf 参数打印相关的源码 syscount-bpfcc -e ENOSPC -p 3010 --ebpf

借助于 syscount-bpfcc 工具跟踪的结果,我们初步确定了 dokcerd 系统调用 mount 的返回 ENOSPC 报错。

mount 系统调用为 sys_mount,但是 sys_mount 函数在新版本内核中,并不是我们直接可以跟踪的入口,这点需要注意。这是因为 4.17 内核对于系统调用做了一些调整,在不同的平台上会添加对应体系架构,详情可以参见 new BPF APIs to get kernel syscall entry func name/prefix[3]

排查的系统为 Ubuntu 21.10,5.13.0 内核,体系架构为 ARM64,因此 sys_mount 在内核真正入口函数为 __arm64_sys_mount

如果体系结构为 x86_64,那么 sys_mount 对应的函数则为 __x64_sys_mount,其他体系架构可在 /proc/kallsyms 中搜索确认。

到目前为止,我们已经确认了内核入口函数 __arm64_sys_mount ,但是如何定位错误具体出现在哪个子调用流程呢?毕竟,内核中的函数调用路径还是偏长,而且还可能涉及到各种跳转或者特定的实现。

为了确定出错的子流程,首先我们需要获取到 __arm64_sys_mount 调用的子流程,ftrace 中的 function_graph 跟踪器则可大显身手。本文中,我直接使用项目 perf-tools[4] 工具集中的前端工具 funcgraph,这可以完全避免手动设置各种跟踪选项。

如果你对 ftrace 还不熟悉,建议后续学习 Ftrace 必知必会[5]

2.2 定位问题根源

perf-tools 工具集中的 funcgraph 函数可用于直接跟踪内核函数的调用子流程。funcgraph 工具使用帮助如下所示:

$ sudo ./funcgraph -h
USAGE: funcgraph [-aCDhHPtT] [-m maxdepth] [-p PID] [-L TID] [-d secs] funcstring
                 -a              # all info (same as -HPt)
                 -C              # measure on-CPU time only
                 -d seconds      # trace duration, and use buffers
                 -D              # do not show function duration
                 -h              # this usage message
                 -H              # include column headers
                 -m maxdepth     # max stack depth to show
                 -p PID          # trace when this pid is on-CPU
                 -L TID          # trace when this thread is on-CPU
                 -P              # show process names & PIDs
                 -t              # show timestamps
                 -T              # comment function tails
  eg,
       funcgraph do_nanosleep    # trace do_nanosleep() and children
       funcgraph -m 3 do_sys_open # trace do_sys_open() to 3 levels only
       funcgraph -a do_sys_open    # include timestamps and process name
       funcgraph -p 198 do_sys_open # trace vfs_read() for PID 198 only
       funcgraph -d 1 do_sys_open >out # trace 1 sec, then write to file

首次使用,我先通过参数 -m 2 将子函数跟踪的层级设定为 2 ,避免一次查看到过深的函数调用。

$ sudo ./funcgraph -m 2 __arm64_sys_mount

如何对于跟踪结果在 vim 中折叠显示,可参考 ftrace 必知必会[6] 中的对应章节。

gic_handle_irq() 看名字是处理中断相关的函数,我们可以忽略相关调用。

通过使用 funcgraph 跟踪的结果,我们可以获取到 __arm64_sys_mount 函数中调用的主要子流程函数。

在内核函数调用过程中,如果遇到出错,一般会直接跳转到错误相关的清理函数逻辑中(不再继续调用后续的子函数),这里我们可将注意力从 __arm64_sys_mount 函数转移到尾部的内核函数 path_mount 中重点分析。

对于 path_mount 函数查看更深层级调用分析:

$ sudo ./funcgraph -m 5 path_mount > path_mount.log

基于内核函数中的最后一个可能出错函数调用逐步分析,我们可获得调用关系的逻辑:

__arm64_sys_mount()
 -> path_mount()
  -> do_new_mount()
   -> do_add_mount()
    -> graft_tree()
     -> attach_recursive_mnt()
      -> count_mounts()

基于上述的函数调用关系,我们很自然推测是 count_mounts 函数返回了错误,最终通过 __arm64_sys_mount 函数返回到了用户空间。

既然是推测,就需要通过手段进行验证,我们需要获取到整个函数调用链的返回值。通过 BCC 工具集中 trace-bpfcc[7] 进行相关函数返回值进行跟踪。trace-bpfcc 的帮助文档较长,可在 trace_example.txt[8] 文件中查看,这里从略。

在使用 trace-bpfcc 工具跟踪前,我们需要在内核中查看一下相关函数的原型声明。

为了验证猜测,我们需要跟踪整个调用链上核心函数的返回值。trace-bpfcc 工具一次性可以跟踪多个函数返回值,通过 'xxx' 'yyy' 进行分割。

$ sudo trace-bpfcc 'r::__arm64_sys_mount() "%llx", retval'  \
          'r::path_mount "%llx", retval' \
                   'r::do_new_mount "%llx", retval' \
                   'r::do_add_mount "%llx", retval'\
                   'r::graft_tree "%llx", retval' \
                   'r::attach_recursive_mnt "%llx" retval'\
                   'r::count_mounts "%llx", retval'
PID     TID     COMM            FUNC             -
3010    3017    dockerd         graft_tree       ffffffe4
3010    3017    dockerd         attach_recursive_mnt ffffffe4
3010    3017    dockerd         count_mounts     ffffffe4
3010    3017    dockerd         __arm64_sys_mount ffffffffffffffe4
3010    3017    dockerd         path_mount       ffffffe4
3010    3017    dockerd         do_new_mount     ffffffe4
3010    3017    dockerd         do_add_mount     ffffffe4

其中 r:: __arm64_sys_mount "%llx", retval 命令解释如下:

  • r:: __arm64_sys_mount r 表示跟踪函数返回值;
  • "%llx", retval 中 retval 为函数返回值变量, ”%llx“ 为返回值打印的格式;

跟踪到的返回值 0xffffffe4 转成 10 进制,则正好为 -28(0x1B),= -ENOSPC(28)。

Trace-bfpcc 底层使用的 perf_event 事件触发,由于多核并发,顺序不能完全保障,在高内核版本中,事件触发切换成 Ring Buffer[9] 机制则可保证顺序。

2.3 定位问题的根因

通过剥茧抽丝,我们将问题缩小至 count_mounts[10] 函数中。这时,我们需要通过源码分析函数的主流程逻辑,这里直接上代码,幸运的是该函数代码胆小精悍,比较容易理解:

int count_mounts(struct mnt_namespace *ns, struct mount *mnt)
{
  // 可以加载的最大值是通过 sysctl_mount_max 的变量读取的
 unsigned int max = READ_ONCE(sysctl_mount_max);
 unsigned int mounts = 0, old, pending, sum;
 struct mount *p;

 for (p = mnt; p; p = next_mnt(p, mnt))
  mounts++;

 old = ns->mounts;  // 当前 namespace 挂载的数量
 pending = ns->pending_mounts;  // 按照字面意思理解是 pending 的加载数量
 sum = old + pending;   // 加载的总和为 已经加载的数量 + 在路上加载的数量
 if ((old > sum) ||
     (pending > sum) ||
     (max < sum) ||
     (mounts > (max - sum)))  // 那么这些条件的判断也就比较容易理解了
  return -ENOSPC;

 ns->pending_mounts = pending + mounts;
 return 0;
}

通过代码逻辑的简单理解,我们可确定是当前 namespace 中加载的文件数量超过了系统所允许的 sysctl_mount_max 最大值, 其中 sysctl_mount_max 可通过 /proc/sys/fs/mount-max 设定)。

为复现问题,本地环境中我将 /proc/sys/fs/mount-max 的值被设置为了 10(默认值为 100000),达到了与生产环境中一样的报错。

$ sudo cat /proc/sys/fs/mount-max
10

在根源定位以后,我们将该值调大为默认值 100000,重新 docker run 命令即可成功。

当然在生产环境中的情况会比该场景更加复杂,即可能为 mount 的异常或也可能为泄露,但排查的思路却可以参考本文提供的思路。

至此,我们完成问题定位,貌似已经可完美收工,但是等等,这里还有部分跟踪过程中的疑惑需要澄清,这也是本次排查问题时候积累的经验(踩过的坑),并且这些经验对于运用工具排查问题所必须要了解的内容。

基于我们看到的代码进行分析,在实际的跟踪过程中,是否能够所见即所得与代码完全一致呢?答案是未必,sys_mount 跟踪就属于这种不一致的场景。

那么,让我们先来通过 sys_mount 的源码流程与实际跟踪的进行对比分析,来一探究竟。

3. 代码流程与跟踪流程差异分析

函数 sys_mount 定义在 fs/namespace.c[11] 文件中:

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
  char __user *, type, unsigned long, flags, void __user *, data)
{
 int ret;
 char *kernel_type;
 char *kernel_dev;
 void *options;

 kernel_type = copy_mount_string(type); // 子函数 1
 ret = PTR_ERR(kernel_type);
 if (IS_ERR(kernel_type))
  goto out_type;

 kernel_dev = copy_mount_string(dev_name); // 子函数 2
 ret = PTR_ERR(kernel_dev);
 if (IS_ERR(kernel_dev))
  goto out_dev;

 options = copy_mount_options(data); // 子函数 3
 ret = PTR_ERR(options);
 if (IS_ERR(options))
  goto out_data;

 ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options); // 子函数 4

 kfree(options);
out_data:
 kfree(kernel_dev);
out_dev:
 kfree(kernel_type);
out_type:
 return ret;
}

通过代码分析,我们可以印证上述从最后子函数调用分析的依据:在函数调用出错时,则不再直接调用后续函数,直接跳转到函数出错部分处理,例如copy_mount_string 函数调用出错,则会直接跳转到函数的清理部分 out_type: ,其后的子函数 copy_mount_string/copy_mount_options/do_mount 将不再被调用,这也是我们定位出错函数为什么直接从最后子函数进行分析的原因。

通过简单代码调用关系分析,我们可以得到如下调用关系:

  • __arm64_sys_mount()
    • -> copy_mount_string()
    • -> copy_mount_string()
    • -> copy_mount_options()
    • -> do_mount()

但是,细心的读者肯定已经发现了一些端倪,代码分析调用流程和我们实际跟踪的调用流程,并不能直接对应起来。基于我们前面使用 funcgraph 工具跟踪到调用关系则如下所示:

  • __arm64_sys_mount()
    • -> strndup_user()
    • -> strndup_user()
    • -> copy_mount_options()
    • -> path_mount()
    • -> path_put()

的确,这并不是所见即所得的效果。但是通过简单分析,我们还是比较容易发现两者的对应关系:

其中 copy_mount_string()strndup_user() 函数关系如下,定义在 fs/namespace.c[12] 文件中:

static char *copy_mount_string(const void __user *data)
{
 return data ? strndup_user(data, PATH_MAX) : NULL;
}

之所以代码与实际跟踪的结果不一致,这是因为,编译内核的时一般都会设置了 -O2 或 -Os 进行代码级别的优化 ,例如调用展开。这里的 do_mount() 函数的情况也是类似。关于代码编译优化的详情,可以参考 Linux 编译优化[13]这篇文档。

除了使用 funcgraph 跟踪分析外,我们也可通过安装调试符号[14],使用 gdb 调试通过反汇编进行确认。

通过此处分析,我们可以得到这样的常识:即使通过源码了解到了函数调用关系,也需要在跟踪前通过 funcgraph 工具进行确认。

在本次问题排查开始,我一直尝试使用 BCC trace-bpfcc 工具对 do_mount 进行结果跟踪,但总是不能够得到结果,也让自己对于排查思路产生过怀疑。

4. 总结

至此,我们通过 funcgraph 工具配合基于 eBPF 技术 BCC 项目中的 syscount-bpfcc 和 trace-bpfcc 等工具,快速定位到了 mount 报错的异常函数。

尽管排查是基于 mount 报错,但思路也适用于其他场景下的问题分析和排查。同时,该思路,也可以作为源码阅读和分析内核代码时的有益的工具补充。

最后,也希望本文能给大家带来思路参考,如果你发现文中的错误或者有更好的案例,也期待留言交流。

5. 附录部分错误

$ sudo syscount-bpfcc
...

Traceback (most recent call last):
  File "/usr/sbin/syscount-bpfcc", line 20, in 
    from bcc.syscall import syscall_name, syscalls
  File "/usr/lib/python3/dist-packages/bcc/syscall.py", line 387, in 
    raise Exception("ausyscall: command not found")
Exception: ausyscall: command not found

系统缺少 ausyscall 命令,各系统安装方式参见 ausyscall[15]:

$ sudo apt-get install auditd

参考资料

  • Docker cp 提示“no space left on device”Docker cp 提示“no space left on device”[16]
  • Fix mount loop on docker cp[17]

原创不易,如果觉得本文对你有帮助,欢迎一键三连(点赞、在看和收藏),让更多的人能够看到该内容吧!!!

参考资料

[1]

BCC: https://github.com/iovisor/bcc

[2]

syscount-bpfcc: https://github.com/iovisor/bcc/blob/master/tools/syscount.py

[3]

new BPF APIs to get kernel syscall entry func name/prefix: https://github.com/iovisor/bcc/commit/83b49ad6cd9efba88f922c2e7b892fc275208514

[4]

perf-tools: https://github.com/brendangregg/perf-tools

[5]

Ftrace 必知必会: https://www.ebpf.top/post/ftrace_tools

[6]

ftrace 必知必会: https://www.ebpf.top/post/ftrace_tools/#3-%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E5%AD%90%E6%B5%81%E7%A8%8B%E8%B7%9F%E8%B8%AA%E6%A0%88

[7]

trace-bpfcc: https://github.com/iovisor/bcc/blob/master/tools/trace.py

[8]

trace_example.txt: https://github.com/iovisor/bcc/blob/master/tools/trace_example.txt

[9]

Ring Buffer: https://www.ebpf.top/post/bpf_ring_buffer/

[10]

count_mounts: https://elixir.bootlin.com/linux/v5.13/source/fs/namespace.c#L2039

[11]

fs/namespace.c: https://elixir.bootlin.com/linux/v5.13/source/fs/namespace.c#L3433

[12]

fs/namespace.c: https://elixir.bootlin.com/linux/v5.13/source/fs/namespace.c#L3141

[13]

Linux 编译优化: https://cloud.tencent.com/developer/article/1517858

[14]

安装调试符号: https://www.ebpf.top/post/ubuntu-21-10-dbgsym/

[15]

ausyscall: https://command-not-found.com/ausyscall

[16]

Docker cp 提示“no space left on device”Docker cp 提示“no space left on device”: https://www.memeta.co/zh-Hant/article/1wwac_2570p.html

[17]

Fix mount loop on docker cp: https://github.com/moby/moby/pull/38993






Linux阅码场 专业的Linux技术社区和Linux操作系统学习平台,内容涉及Linux内核,Linux内存管理,Linux进程管理,Linux文件系统和IO,Linux性能调优,Linux设备驱动以及Linux虚拟化和云计算等各方各面.
评论
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 45浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 68浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 84浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    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浏览
  • 我的一台很多年前人家不要了的九十年代SONY台式组合音响,接手时只有CD功能不行了,因为不需要,也就没修,只使用收音机、磁带机和外接信号功能就够了。最近五年在外地,就断电闲置,没使用了。今年9月回到家里,就一个劲儿地忙着收拾家当,忙了一个多月,太多事啦!修了电气,清理了闲置不用了的电器和电子,就是一个劲儿地扔扔扔!几十年的“工匠式”收留收藏,只能断舍离,拆解不过来的了。一天,忽然感觉室内有股臭味,用鼻子的嗅觉功能朝着臭味重的方向寻找,觉得应该就是这台组合音响?怎么会呢?这无机物的东西不会腐臭吧?
    自做自受 2024-12-10 16:34 136浏览
  • 【萤火工场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浏览
  • 智能汽车可替换LED前照灯控制运行的原理涉及多个方面,包括自适应前照灯系统(AFS)的工作原理、传感器的应用、步进电机的控制以及模糊控制策略等。当下时代的智能汽车灯光控制系统通过车载网关控制单元集中控制,表现特殊点的有特斯拉,仅通过前车身控制器,整个系统就包括了灯光旋转开关、车灯变光开关、左LED前照灯总成、右LED前照灯总成、转向柱电子控制单元、CAN数据总线接口、组合仪表控制单元、车载网关控制单元等器件。变光开关、转向开关和辅助操作系统一般连为一体,开关之间通过内部线束和转向柱装置连接为多,
    lauguo2013 2024-12-10 15:53 78浏览
  • 概述 通过前面的研究学习,已经可以在CycloneVGX器件中成功实现完整的TDC(或者说完整的TDL,即延时线),测试结果也比较满足,解决了超大BIN尺寸以及大量0尺寸BIN的问题,但是还是存在一些之前系列器件还未遇到的问题,这些问题将在本文中进行详细描述介绍。 在五代Cyclone器件内部系统时钟受限的情况下,意味着大量逻辑资源将被浪费在于实现较大长度的TDL上面。是否可以找到方法可以对此前TDL的长度进行优化呢?本文还将探讨这个问题。TDC前段BIN颗粒堵塞问题分析 将延时链在逻辑中实现后
    coyoo 2024-12-10 13:28 101浏览
  • 一、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 73浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 44浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦