最近有同事反馈一个系统启动失败的问题,根因是系统的驱动模块加载失败导致system_server
无法正常启动。lsmod
查看,没有有任何的驱动加载,尝试insmod /vendor/lib/modules/cnss2.ko
会提示:
1 |
|
说明对应模块的签名与内核不一致,导致安装失败了。这里我们就来看看内核模块具体是怎么签名的,模块签名又是如何验证的,以及如何通过工具进行模块的签名检验。
最近有同事反馈一个系统启动失败的问题,根因是系统的驱动模块加载失败导致system_server
无法正常启动。lsmod
查看,没有有任何的驱动加载,尝试insmod /vendor/lib/modules/cnss2.ko
会提示:
1 |
|
说明对应模块的签名与内核不一致,导致安装失败了。这里我们就来看看内核模块具体是怎么签名的,模块签名又是如何验证的,以及如何通过工具进行模块的签名检验。
早期Linux内核的调度更多考虑的是系统调度的公平与吞吐量,对于实时性的支持并不友好。为了改善系统的响应时间,降低某些场景下实时任务的调度延迟,从2.6
版本开始支持了实时调度与抢占功能,开发人员为此专门建立了一个实时Linux的网站,上面提供了实时内核的一些历史状态与补丁信息。实时调度对于音视频、UI渲染等对时间非常敏感的任务来说,非常必要。比如对于Android
平台,会将音频、渲染相关的一些核心任务的调度策略设置为实时调度,这样可以减少系统调度延迟与任务抢占带来的延时。Linux内核中的实时调度主要有两种调度策略:
SCHED_FIFO
: 先入先出,即优先级高的任务优先执行,不会被其他任务抢占,直到对应的任务阻塞或者主动释放CPUSCHED_RR
: 轮询(也称随机轮盘)调度,相同优先级的任务轮流执行相同的时间片,时间片用完后会调度其他的任务本文基于Linux内核5.10版本分析
看Linux驱动相关的代码, 却没有一个好的调试环境可以跟踪内核相关的调用流程。于是,想着用QEMU虚拟机来搭建一个Linux系统。花了大半天时间,终于能够启动一个简单的Linux系统了。中间踩了不少坑,找了不少资料,这里简单总结下整个过程。
以下操作都是基于Ubuntu 18.04 x86_64平台
最开始参考了如何使用QEMU跑内核,使用系统自带的QEMU工具,结果提示如下错误:
1 |
|
怀疑是QEMU的版本太低导致,于是只好又重新编译QEMU源码,最后总算大功告成。总的说来,大致要做的事情有这么几个:
qemu-system-aarch64
跑起来虚拟机来最近因为公司安全的需求, 要修改openssh
的源码, 将其移植到一个ARM的嵌入式系统上, 替换原有的预编译的版本. 参考了网上的一些移植openssh
的资料, 如openssl官网编译安装说明; 移植openssh到arm-linux, 但是由于目标平台不一样, 实践起来并不能完全参考, 会有细微的差异. 这里把整个流程写下来, 总结一下, 方便后面移植相关开发工具.
Android从9.0版本开始全面支持eBPF(extended Berkeley Packet Filters
), 其主要用在流量统计上, 也可以用来监控CPU/IO/内存等模块的状态.简单来说, eBPF
可以与内核的kprobe
/tracepoints
/skfilter
等模块相结合, 将eBPF的函数hook到内核事件从而监控相应的系统状态.
Android为eBPF提供了许多封装的库, 并提供了eBPF加载器bpfloader
:
bpfloader
: 位于/system/bpf/bpfloader
, 系统启动时负责加载位于/system/etc/bpf
中的eBPF目标文件libbpf_android
: 位于/system/bpf/libbpf_android
提供创建bpf容器/加载bpf目标文件的接口libbpf
: 位于/external/bcc
, 封装了bpf的系统调用, 提供如attach/dettach程序的接口libnetdbpf
: 位于/system/netd/libnetdbpf
, 实现了netd流量统计功能的函数目前在Android(Q)上有两处eBPF的代码: 一个是/system/netd/bpf_progs/netd.c
, 主要是用于流量统计;一个是/system/bpfprogs/time_in_state.c
用于监控CPU运行频率以及上下文切换的耗时.
接下来我们就从三个部分来深入理解下Android是如何利用eBPF的:
最近碰到棘手的问题: 以太网进行iperf测试时, 发生了SMMU (System Memory Management Unit)
访问异常导致内核崩溃. 原本只是内部测试发现, 后面在试验车上也概率性的出现. 问题发生的概率还不小. 很严重. 只能先从头把一些基本概念与流程梳理清楚. 好在最后还是找到了原因并解决了. 松了口气, 才有时间把整个问题的来龙去脉细细的总结下, 算是一个SMMU相关问题的案例.
首先来看看问题的发生的背景.
问题发生在利用iperf
做网络性能测试的时候, 测试系统(采用高通8155平台, 内置一个EMAC芯片, 最高支持1Gbps速率)作为客户端:
1 |
|
这里加-R
参数表示客户端作为数据接收方(奇怪的是, 测试不加-R
参数就不会有问题, 这也说明只有在接收数据的过程才会出现问题), 而服务端是发送方:
1 |
|
这么测试几十个小时就很快出现了, 抓取到的问题堆栈如下. 前面的日志是SMMU相关的寄存器状态打印, 后面是内核调用堆栈.
在看Linux内核代码时,经常会遇到各种锁(lock)的使用。对于像spin_lock_irq
/spin_lock_irqsave
的区别感到困惑,每次都要重新查一下资料。遂决定写一篇文章记录下内核中使用到的锁,以及使用的场景。
与应用中的锁类似,内核中的锁也只是为了保护某个内核数据结构或者内存区域在多个并发执行路径时不被破坏,确保数据的一致性。Linux内核作为应用层服务的提供者,一方面要为应用提供系统调用接口(system call
),代表用户进程执行任务,即process context, 在进程上下文中可以休眠,执行调度;同时与硬件直接交互,要响应硬件中断的请求,处理诸如网卡数据/串口数据等请求,即Interrupt Context,在中断上下文内核不能休眠,无法重新调度. 内核就是在进程上下文/中断上下文直接来回切换,执行相应的任务请求。这就自然产生了数据的并发访问,产生了竞争条件(race condition
)。另一方面,目前大多数的系统都是多核CPU、支持多进程,多个CPU、多个进程同时访问内核数据也同样会产生竞争条件。
最近了解Linux的性能优化时, 接触到了BPF(Berkeley Packet Filter)。很有意思也很强大的功能;想把学到的一些基本原理与知识记录下来, 算是一个初步的总结. 这篇文章主要从如下几个方面介绍下BPF:
用过tcpdump
的同学应该都了解pcap
, 实际上pcap
就是基于BPF来实现网络数据包的过滤的. tcpdump
的原理如下图所示: tcpdump
将包过滤的表达式, 如查看某个网口所有udp包, 输入tcpdump -n -i eth0 udp
, 这个表达式通过PCAP库编译成伪机器字节码后, 通过系统调用发送给内核(内核中有对应的机器码解释器)解释执行, 这样只要系统有udp包, 内核都会过滤出来转发给用户进程tcpdump
:
平时在Android/Ubuntu这样的Linux系统中, 经常跟网络打交道, 不可避免的要使用网络工具来定位问题;这里对Linux常用的网络工具进行简单的总结, 方便后续查阅使用. 主要看看以下几个常用的网络工具:
socket通常用于跨进程通信(Inter-Process Communication,IPC),由于其最早BSD Unix发行版中使用,因此也叫做Barkeley sockets。socket封装了底层网络通信协议细节,为上层应用提供了一个统一的接口,按照使用的“域”不同,又分为用于网络通信的internet socket以及用于本地进程通信的Unix domain socket。
那么,如何利用socket实现IPC通信了?在多个客户端的情况下,服务端要如何实现同时响应多个客户请求了,即如何实现多个IO端口的监听(I/O multiplex)?这篇文章,就来看看这两个问题。首先,来看下socket编程的一些基本知识。