【视频回放】Kindling之慢系统调用
背景
系统调用是内核提供给用户的功能接口,在我们的系统中,通常会运行许多系统调用,其中有很多系统调用是由我们的用户程序来触发的(比如C语言中的printf()函数,实际会触发底层的write()系统调用)。在绝大多数情况下,系统调用可以在很短的时间内执行完成并且返回,但是在某些情况下, 系统调用可能会执行的比较慢,从而成为我们进程运行时的瓶颈。
对于这些执行较慢的系统调用我们可以分为两类,一类是在一定时间内执行完成,但是执行时间过长的系统调用,我们可以称之为慢系统调用;另一类是在一定时间内一直未返回的系统调用,我们称之为超时系统调用。在Kindling中,我们实现了对于这两类系统调用的捕获和分析,并将其报告给上层用户,以便用户洞察系统调用的情况。
实现原理
内核空间eBPF捕获系统调用
首先,我们可以使用eBPF技术通过内核提供的rawsyscalls目录下的tracepoint来捕获所有系统调用的入口和出口(sysenter对应系统调用的入口,sysexit对应系统调用的出口),可在`/sys/kernel/debug/tracing/events/rawsyscalls/`中看到该tracepoint的具体信息。
图1 sys_enter tracepoint的format信息
我们通过该tracepoint捕获所有系统调用,并将每一个系统调用关联到具体的线程上,发送至用户空间进行进一步处理。
用户空间关联系统调用时间信息
慢系统调用
对于一个系统调用而言,它有一个enter和一个exit(所有系统调用都只有一个trap,不会出现堆栈序列的enter和exit),我们可以在系统调用enter时获取当前时间戳,然后在exit时再获取当前时间戳,此时两个时间戳的差值就是该系统调用的实际执行时长,在Kindling中,我们将该值称为一个慢系统调用的latency。
图2 慢系统调用实现示意
超时系统调用
对于超时的系统调用,它们在一段时间范围内并未返回,因此我们无法获取到系统调用的exit信息,目前kindling采取的策略是维护一个以tid为key,syscall信息为value的map,当有系统调用进入时,在map做一次标记,然后我们会定时去扫描这个map,如果发现map中有超时的系统调用则立即将该系统调用标记为超时系统调用。
图3 超时系统调用实现示意
demo测试
这里我们写一个简单的demo来测试一下我们所捕获到的慢系统调用 ,我们写一个最简单的调用nanosleep()系统调用的例子,让进程睡眠3秒钟,我们可以在运行的Kindling输出日志中看到捕获到的信息:
我们可以看到,Kindling捕获到了该系统调用,并给出了其类型,执行时长以及TID、PID等具体信息。
总结
首先,我们通过eBPF技术和内核提供的系统调用tracepoint捕获了所有的系统调用数据,然后把系统调用与线程信息做了关联,然后我们在用户空间对系统调用的enter和exit进行了latency的计算以判断是否为慢系统调用,特别的是,对于那些超时的系统调用,我们无法直接计算latency,采取了使用map标记系统调用入口并定时扫描map的策略,从而完成对于超时系统调用的捕获。
Kindling是一款基于eBPF的云原生可观测性开源工具,旨在帮助用户更好更快的定界云原生系统问题,并致力于打造云原生全故障域的定界能力。
Kindling项目地址:Kindling
在云可观测性方面有任何疑问欢迎与我们联系:Kindling官网