杂谈
文章平均质量分 84
塵觴葉
这个作者很懒,什么都没留下…
展开
-
一种嵌入式应用软件内存泄露的跟踪定位方法
一种简单的C/C++应用软件内存泄露问题的定位方法原创 2023-07-02 12:10:32 · 505 阅读 · 0 评论 -
获取系统中各应用的运行时间
因为笔者的树莓派内核的配置选项为该值。但在其他嵌入式设备上运行结果是正确的(感兴趣的可以尝试一下)——该方案通常只适用于嵌入式设备,而不适合在。由此可判定各个应用进程的开始运行时的系统启动时间,其与当前系统已运行的时间之差,就是应用进程已持续运行的时间。另一种可行的方案是,增加一个动态库,当动态库被加载时会自动执行一个函数,记录应用开始运行时的系统时间。不过该方案的缺陷是需要修改各个应用的代码,并增加进程间通信的机制(通过上面的。这也是笔者在该分享前部分说的,相比之下,另两种方案的可取之处。原创 2023-05-14 16:26:45 · 681 阅读 · 1 评论 -
使用bindgen将C语言头文件转换为Rust接口代码
本文演示了使用bindgen将C语言头文件转换为Rust接口代码文件的两种方法原创 2023-01-25 16:34:09 · 2084 阅读 · 0 评论 -
在嵌入式设备运行Rust/bluer蓝牙简单应用
本文主要演示了如何在openwrt嵌入式设备上编译、运行bluer蓝牙简单应用程序的过程原创 2023-01-08 14:26:05 · 1571 阅读 · 0 评论 -
为嵌入式设备编译Rust/dbus进程间通信组件
本文演示了如何交叉编译Rust/DBus模块并在嵌入式设备上运行简单示例的过程原创 2022-11-26 17:37:37 · 1419 阅读 · 1 评论 -
Rust引用转换时避免使用变量
本文记录了笔者在Rust引用转换时避免使用多余变量的方法原创 2022-11-20 16:44:07 · 626 阅读 · 0 评论 -
为全志D1/RISCV64设备移植openwrt-22.03系统
为全志D1/RISCV64东山派设备移植openwrt-22.03原创 2022-09-19 22:16:50 · 2510 阅读 · 0 评论 -
基于uftrace的应用性能侧写
本文演示了使用开源工具uftrace的应用性能侧写操作方法,并对不同方法的结果作比较原创 2022-06-03 15:47:54 · 654 阅读 · 0 评论 -
Rust下的JSON动态反序列化
Rust下的结构体反序列化在之前的一篇文章中,笔者介绍了在Rust编程语言中,使用serde_json包对结构体的序列化和反序列化的基本操作;重点是在定义结构体时,继承源于serde包的 Deserialize及Serialize特性:use serde_json;use serde::{Deserialize, Serialize};#[derive(Serilize, Deserialize, Debug)]struct rust_struct { member_0: String,原创 2022-05-21 15:18:32 · 1549 阅读 · 0 评论 -
为嵌入式设备编译paho.mqtt.rust应用
为嵌入式MIPS准备rust开发环境在此前的一篇文章中,笔者对Rust的交叉编译开发环境的安装作了说明,演示了简单Hello World应用的交叉编译。笔者在该文章中记录了较为复杂的rust应用(paho.mqtt.rust)的编译过程,目柡设备为运行openwrt系统的MT7628设备。此处简要重复笔者安装嵌入式MIPS的rust交叉编译工具链的过程。首先,以根用户权限安装rust编译器:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs原创 2022-05-01 13:30:57 · 906 阅读 · 0 评论 -
PLY:嵌入式Linux环境下的内核探测工具
简单Linux系统环境下的内核探测在笔者之前的文章中提到,基于内核eBPF探针的常用工具主要bpftrace、bcc,二者复杂的依赖库使得其在嵌入式Linux系统环境下常常是不可用的。截止目前,一些嵌入式SDK(例如buildroot及openwrt等)未提供这两个性能分析工具的自动化构建功能。一种可行的方案是参考Linux内核源码samples/bpf下的示例编写基于eBPF的C代码,并编译生成BTF目柡文件和可执行应用,用于嵌入式设备上的性能分析。这种方案可行但实施的效率较低。幸运的是,同属于iovi原创 2022-04-17 08:45:19 · 1090 阅读 · 0 评论 -
虚拟动态共享库VDSO的实现机制
失落的strace在《BPF Performance Tools》一书中,作者评批strace用于监测系统应用的创建execve时写道:The current implementation of strace(1) uses breakpoints that can greatly slow the target (over 100x), making it dangerous for production use. ...Also, this example traces the exec() sy原创 2022-04-05 20:44:22 · 1768 阅读 · 0 评论 -
为Evince-PDF添加灰色背景
Ubuntu系统的默认PDF阅读软件ubuntu操作系统自带了Evince-PDF阅读器,其优点是简单便捷,不过与其他常用的PDF阅读器相比,缺少很多定制化的选项,例如为避免眼睛疲劳的替换阅读界面背景颜色的功能。使用GNU/Linux操作系统的一个优势是,可以获取从内核至应用各个层面的软件源码,并加以修改后重新编译,可以方便地实现所需的新功能。笔者在该文章中记录了为Evince阅读器添加灰色背景的操作过程。作为对比,在添加该功能之前,笔者打开SystemTap教程文档的背景如下:修改之后,在Evinc原创 2022-03-20 12:08:35 · 1168 阅读 · 0 评论 -
基于Linux内核的应用探测: uprobe
基于Linux内核的应用性能分析技术Linux内核有良好的分层设计,但了解并实时跟踪内核的行为并不容易。为此,Linux内核开发者实现了跟踪点(tracepoint)、性能监测(perf_event)、函数跟踪(ftrace)等功能,用于Linux的调试和性能分析。最近几年已经成熟的eBPF内核子系统带来了内核性能分析技术的飞跃,常用的主要有bcc、bpftrace;二者同属于github的iovisor用户,直译出来即为“输入输出监测”,即性能监测。这些工具同样可用于应用层的性能分析,这是笔者关注的功能原创 2022-03-13 20:31:28 · 4249 阅读 · 0 评论 -
SystemTap应用跟踪探测的使用
用户静态定义探测点USDTusdt(User-Statically-Defined-Tracepoint)是一种向应用插入跟踪点的技术方案,其特点是跟踪点的插入是静态的,通常需要修改应用的源码并再次编译。该技术方案源于DTrace,不过usdt应用跟踪的功能在Systemtap和bcc等内核跟踪调试工具中已有良好的支持。Systemtap开源工具提供了sys/sdt.h头文件,该头文件没有相应用C代码实现,仅仅提供了多个STAP_PROBExxx和DTRACE_PROBExxx宏定义。这些宏定义通过GCC原创 2021-11-07 23:23:41 · 814 阅读 · 0 评论 -
嵌入式设备上SystemTap调试工具使用
SystemTap调试工具简介SystemTap调试器常用于Linux内核的动态调试,不过该工具集也可用于应用的跟踪调试。随着Linux内核及其应用程序的复杂度不断加深,使用一些在功能上区别于传统的GDB调试工具就变得越来越重要了。这类调试工具具有低延时(Low Latency),高性能,动态调试的特点。嵌入式Linux设备的系统软件通常不需从头开发,这些调试工具可以帮助开发者快速理解Linux内核、系统层软件,同时定位、解决一些软件上的缺陷。SystemTap的工作机制比较特殊,它会将开发者编写的Sy原创 2021-10-31 21:54:43 · 1268 阅读 · 0 评论 -
BPF示例在64位ARM上的调试
eBPF调试工具eBPF作为Linux内核中调试功能强大的子系统,相应的应用层调试工具有很多,例如bpftool/bpftrace,以及提供了Lua和Python调试接口的bcc;内核信息收集、性能分析的开源工具SystemTap也用到了内核的eBPF功能。不过笔者希望在嵌入式设备上使用eBPF提供的调试功能,一种方法是使用开源的SDK(例如yocto,笔者未尝试过)自动化构建这些调试工具(否则就需要手动交叉编译);另一种方法是将某个支持arm64架构的Linux发行版(例如运行debian系统的树莓派)原创 2021-10-24 22:18:05 · 3918 阅读 · 5 评论 -
BPF内核调试开发坏境的搭建
BPF的内核调试方法一直以来,笔者对BPF功能的认知一直停留在与tcpdump工具进行网络抓包相关;最近几年BPF已成为内核的顶级子系统,从网络抓包的内核支持模块演进成为内核调试、性能跟踪的复杂模块。其对内核的调试(也可用于应用的调试)是动态的;不过该动态调试过程并不需要编译内核模块并加载之,而是将调试应用编译成为eBPF字节码,并通过bpf系统调用将该字节码加载到内核中的BPF虚拟机中解析执行,或由内核中的BPF Jit编译为机器码后运行。这一调试机制使得其与传统的GDB调试方法相比,具有更低的延时和更原创 2021-10-17 22:41:15 · 3625 阅读 · 3 评论 -
GDB内存调试初探八
Linux/amd64的调用规则为了方便调试,笔者在PC机上直接调试简单的内存相关的应用;这需要了解x86_64的ABI,该文档对函数调用制定了一些限定规则,其中重要的有两点,第一点是参数的传参(非浮点参数):User-level applications use as integer registersfor passing the sequence: %rdi, %rsi, %rdx, %rcx, %r8 and %r9.The kernel interface uses %rdi, %rsi,原创 2021-10-10 22:37:36 · 1065 阅读 · 0 评论 -
GDB内存调试初探七
非main_arena的内存分配在初探六中提到,正常情况下创建一个新的线程时,会为新线程创建独立的arena,其类型为struct malloc_state。当一个线程退出时,其动态分配的堆内存不会被释放,而是通过malloc_state结构体中的next_free指针链接保存起来(那么当再次创建新线程时可复用):/* malloc/malloc.c */struct malloc_state { ... struct malloc_state * next; struct m原创 2021-10-03 21:44:39 · 1291 阅读 · 0 评论 -
应用OOM及子进程的创建
Linux应用内存分配失败的问题大型的嵌入式应用常占用巨量的内存。一些“杂揉、拼凑”而成的应用,常在一个应用中包含多个功能模块,例如音视频处理模块,系统控制模块等。这样的应用设计会带来一系列的内存问题,最主要的一个是音视频的应用会占用大量的内存空间,从而影响应用的运行性能。笔者根据以往的经验,列出一种与子进程创建相关的内存分配失败问题。当某个应用向Linux内核申请内存但内核无法满足时,内核会根据配置,选择性地杀掉该进程;该功能与OOM-Killer相关(注意,区别于安卓内核中的lowmemory-kil原创 2021-09-25 22:35:02 · 998 阅读 · 0 评论 -
ARMv7-a的多寄存器Load/Store
多寄存器的加载与存储ARMv7-a架构的芯片提供了高效的寄存器数据加载与存储指令,一条指令可以从内存中加载多个寄存器,也可以将多个寄存器的数据存储至内存中。读写的内存必须是连续的;虽然数据量不大,但基于这些指令可以实现高效的数据拷贝、(软)中断的上下文保存、恢复,以及内核的任务切换、应用层的协程实现。Arm Architecture Reference Manual对这些指令做了详尽的说明:编写多寄存器的加载和存储示例ARMv7芯片提供了以上多种指令,原因是这些指令在加载、存储数据时,具体功能实现有原创 2021-09-19 23:02:38 · 604 阅读 · 0 评论 -
GDB内存调试初探六
内存访问越界的定位去年笔者分享了两篇内存访问越界的定位方法,第一种方法用到了mprotect系统调用设置内存属性,第二种方法则未用到该系统调用。两种方法对于调试用的简单应用都有效,不过笔者编写的调试应用是单线程的。在具体的工作实践中,只用到过第一种方法;因此不确定第二种方法效果如何。笔者认为这两种方法有一些缺点,某些情况下甚至可能会失效,例如系统调用产生的内存访问越界:read(fd, buf, buflen + overflowsize)。此外,第二种方法需要知晓具体的内存分配的数据结构,笔者希望通过本原创 2021-09-12 22:31:35 · 1232 阅读 · 0 评论 -
U-Boot的重定位实现机制
获取当前芯片平台的相关信息为了深入了解ARM 64位芯片架构,笔者为u-boot添加了archinfo命令,以获取CPU当前的工作状态等信息。不过在增加完整的64位ARM架构信息查看功能之前,笔者首先增加了简单的获取当前PC指针及栈指针的函数:diff --git a/arch/arm/lib/arch-info.c b/arch/arm/lib/arch-info.cnew file mode 100644index 00000000..b9bc9fd0--- /dev/null+++ b/a原创 2021-09-05 22:05:13 · 2196 阅读 · 0 评论 -
U-Boot启动Linux内核的简单实现
64位ARM Linux内核启动的环境要求在64位ARM处理器上,Linux内核启动前,对设备的环境要求主要有以下几点:内存(DDR)已初始化完成,禁用MMU,关闭数据缓存(dcache);蔽屏CPU中断,关闭指令缓存(icache);禁用驱动的DMA操作,防止Linux内核在启动过程中内存被IO设备访问;除此之外, Linux内核对64位ARM处理器的状态(例如异常级别,Exception Level)有一些细致的要求,这里不再探究,详细的说明请参考官方文档。U-Boot启动原创 2021-08-29 22:51:06 · 875 阅读 · 0 评论 -
GNU Makefile -- 规则依赖执行顺序和双冒号规则
Make的规则依赖执行顺序GNU Make的规则依赖有两种类型:普通依赖(normal prerequisites)和顺序依赖(order-only prerequisites)。对于普通依赖,其出现的顺序指定了依赖被更新的顺序。这一特性可以在一定程度上(非并行调用Make)隐含各依赖项之间的依赖关系,但不具备稳定性。笔者编写了简单的测试脚本:.PHONY: all foo bar cleanifeq ($(REORDER),yes)all: bar foo | orderedelseall:原创 2021-08-22 23:23:12 · 4467 阅读 · 0 评论 -
Rust对异步编程的支持
线程池与异步执行作为一名嵌入式底层开发人员,工作中很少遇到线程池和异步执行的概念。在Lua脚本语言中有一个协程的概念,与线程池的异步执行有一些相似,但仍存在很多区别。用户态应用创建一个线程,绝大部分时间处于阻塞的状态(如果不是这样,这个线程占用的CPU时间会很高);线程会占用一定的系统资源。当一个多线程的应用的大部分线程都处于一个阻塞的状态,那就会浪费很多的系统资源。此外,对于一些高并发的事件处理,若采用创建新的任务线程的方案,那么某些情况下创建的线程数量可能达到上百个甚至上千个。这两点都会浪费操作系统的原创 2021-08-15 23:18:40 · 1015 阅读 · 0 评论 -
Rust结构体的JSON序列化和反序列化
一个JSON的序列化问题与人聊天时偶然问到一个问题:“给定任意一个(C/C++)结构体,如何实现其JSON的序列化和反序列化,而不用专门编写相应的序列化、反序列化实现代码?”我摇摇头,表示不知如何实现这一功能;现在我也认为,这一功能对于C/C++,是不可能自动化实现的。不过对于其他的静态编译型编程语言,如Golang/Rust等,这一功能则相对容易实现。与Golang的反射机制(Reflection)不同,Rust使用到了trait机制。Rust的JSON序列化库serde提供了通用的序列化功能,诸多原创 2021-08-08 23:05:02 · 3804 阅读 · 0 评论 -
GNU Makefile--C/C++头文件依赖规则的生成
使用GCC编译器生成头文件依赖复杂的C/C++工程中的头文件比较多,在编写GNU Makefile时,手动指出其源代码文件的头文件依赖关系是不可行的,需要通过编译工具自动生成头文件的依赖关系。GNU GCC的-MXX命令行选项用于生成某个代码文件的依赖关系,通常使用的命令行选项为:-MT object.o -MP -MMD -MF object.d其中,-MT用于指定与源文件对应的目标文件名(此处为object.o);-MP指示GCC编译器为依赖的头文件增加伪目标规则;-MMD不会隐含增加-E编译选原创 2021-08-01 22:33:35 · 1132 阅读 · 0 评论 -
u-boot的自拷贝(重定位)
u-boot的加载地址早期的通用启动器(Universal Bootloader, u-boot)没有图形界面配置的功能,为某个设备修改配置的操作比较繁琐。较新版本的u-boot提供了图形化配置界面,可以在以下配置界面下修改u-boot的加载基地址(对应的配置项为CONFIG_SYS_TEXT_BASE):-> Boot options -> Boot images -> Text Base该地址指定了u-boot在内存中存放的起始地址,通常由SoC厂商的内置B原创 2021-07-25 21:41:53 · 1508 阅读 · 2 评论 -
GNU Makefile--命令行参数的传递
make的命令行变量参数在Makefile脚本中,可以通过$(MAKE)递归执行其他的Makefile。make的一些命令行选项(例如禁止输出当前目录的选项--no-print-directory等),会对其行为产生一些影响,而递归调用的make也应当继承这一类选项。此外,在编译u-boot或Linux内核等工程时,常用的命令为:make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- ...命令中的ARCH和CROSS_COMPILE两个变量参数也应当在由$原创 2021-07-18 22:05:02 · 13621 阅读 · 1 评论 -
Rust应用调用C语言动态库
外部功能接口FFI虽然高级(脚本)编程语言的功能丰富,表达能力强,但对底层的一些特殊操作的支持并不完善,就需要以其他编程语言来实现。调用其他编程语言的接口,被称为Foreign Function Interface,直译为外部功能接口。该接口通常是调用C语言实现的外部功能模块,因为C语言接近于全能,几乎任何功能都能够实现;正如同使用汇编语言也可以实现很多功能一样,但开发效率低下。很多脚本语言提供了FFI功能,例如Python、PHP和JIT版本的Lua解析器等。同样的,Rust也提供了FFI接口,作为标准原创 2021-07-11 21:38:06 · 10204 阅读 · 0 评论 -
netlink套接字监测网络设备
网络设备的状态监测嵌入式系统层的开发常涉及到设备的网络配置,如何监测设备网络状态的变化通常是一些系统级的服务在实现过程中需要考虑的问题。一种可行的方案是在系统服务里创建一个线程,每隔一段时间(如5秒)获取网络接口的连接状态及信息,并与之前的信息相比较。这种方案的优点是简单、直接;而缺点是响应网络变化不及时,轮询的方式从某种角度看,是不合理的。实事上,现在已有了很多开源的网络配置服务,如openwrt中的netifd,及桌面系统下的NetworkManager(一些嵌入式SDK集成了该网络服务的守护进程);原创 2021-07-04 20:38:40 · 1624 阅读 · 1 评论 -
GNU Makefile--调试器remake
remake调试器国外的开发人员对GNU Make进行了一定程度上的重构,增加了Makefile的动态调试功能,命名为remake。调试器的操作界面类似于GDB的命令行,开发者为其编写了详细的文档,有兴趣的可以参考。GNU Make自身也支持若干个调试选项,但其调试功能比较有限,remake的作者对此很不满。不过,remake工具对递归的调试(即一个Makefile脚本中调用$(MAKE)执行新的Makefile)支持不够完善,对单个Makefile的调试功能则相对完整。本文记录了笔者通过remake工具原创 2021-06-27 20:44:18 · 1476 阅读 · 0 评论 -
GNU Makefile--规则的先决条件:依赖
简单调试应用为了演示Makefile的依赖(prerequisite)的特性,笔者编写了简单的C代码,其中operators.c内容如下:#include "operators.h"OP_TYPE OP_FUNC (OP_TYPE a, OP_TYPE b){ return a OP_SYMBOL b;}该C文件会被编译生成四个目标文件:operators-add-int.o,operators-add-double.o,operators-mul-int.o,operators-mul-d原创 2021-06-20 21:35:24 · 767 阅读 · 2 评论 -
GNU Makefile--使用eval函数自动生成规则
Makefile的规则Makefile是一种小型的脚本语言,但编写良好的脚本,仍需要一个不断改进的过程。作为一个嵌入式开发人员,不论写什么代码,都需要经常考虑如何以最少的代码,实现最多的功能(以提升工作效率);同时也应考虑到代码的可维护性、可扩展性等。Makefile的规则指定了如何去编译、生成一个(伪)目标。这一点在之前的一篇文章中提到过,规则的操作(recipes)是经由**/bin/sh**解析器来执行的。当规则操作较复杂时,可以把其赋值给延迟变量,从而避免编写重复的代码。需要编写一个Makefil原创 2021-06-14 19:07:11 · 719 阅读 · 3 评论 -
Rust交叉编译开发环境的搭建
为嵌入式开发引入新的编程语言对于嵌入式系统软件和应用软件的开发而言,编程语言的重要性次于整体的软件架构设计的重要性。不过仍有必要谨慎地选择编程语言,因为这涉及开发效率、软件性能,以及团队成员的技术能力。譬如,若整个嵌入式软件的绝大数组件是用C++编写的,那么招聘一些对C++不十分熟悉的开发人员,就是团队建设的不合理了:精通C++的开发人员相对较少。对于小型的团队,选择一些不常用的编程语言,(短期内)不用考虑团队建设的问题,同时可以很大程度上提升开发效率、减少软件缺陷,提高软件的可维护性、可扩展性。随着嵌入原创 2021-06-06 10:46:08 · 5407 阅读 · 3 评论 -
GNU Makefile--基本变量与规则执行
GNU Makefile–基本变量与规则执行调试工具 phell为了方便调试GNU Make的两种基本变量与规则的执行操作,笔者用C语言编写了简单的调试工具,phell(Phony Shell),不带参数的执行会输出其支持的一些选项。其主要功能是将命令行参数以及其他相关的信息输出:yejq@UNIX:~/Downloads/phell$ phellUsage: phell [options] COMMAND ARGUMENTS LIST -e : toggle stderr原创 2021-05-30 11:56:54 · 429 阅读 · 1 评论 -
GDB内存调试初探五
Memory Accessing beyond valid range (2)Problem descriptionI presented a problem in the last blog article, in which an invalid memory write occurs beyond a chunk of memory range (here); at the end of article, I concluded that the method used sometimes fai原创 2020-09-20 14:28:40 · 732 阅读 · 0 评论 -
GDB内存调试初探四
Memory accessing beyond valid rangeHere I present a problem of memory accessing beyond valid range for an integer array pointer, the following code has two functions named good_allocation and bad_allocation. Glancing through the code, we can easily find t原创 2020-09-13 15:31:17 · 691 阅读 · 0 评论