麒麟操作系统文件监控实现--bpftrace内核探针
bpftrace 是基于 eBPF 和 BCC 的开源系统跟踪工具。本文详述通过bpftrace在麒麟操作系统下,快速定位文件异常丢失的根本原因。
bpftrace内核探针 - 文件监控实现
一、bpftrace简介
bpftrace 是基于 eBPF 和 BCC 的开源系统跟踪工具。
bpftrace 使用 LLVM 作为后端将脚本编译为 BPF 字节码,并利用 BCC 与 Linux BPF 系统进行交互,通过探针机制采集内核和程序运行的信息,帮助开发者找到隐藏较深的Bug、安全问题和性能瓶颈。
二、bpftrace安装
bpftrace 官方建议 Linux 内核高于 4.9 ,实际低版本内核也可编译安装,但某些功能可能不可用。
麒麟V10-SP1桌面可直接联网apt安装:
apt-get install bpftrace
麒麟V10-SP1服务器需配置SP2 Update源安装:
[ks10-adv-updates-sp2]
name = Kylin Linux Advanced Server 10 - Updates - sp2
baseurl = http://update.cs2c.com.cn:8080/NS/V10/V10SP2/os/adv/lic/updates/$basearch/
gpgcheck = 1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-kylin
enabled = 1
yum install bpftrace
yum info bpftrace
Name : bpftrace
Version : 0.11.0
Release : 8.ky10
Architecture : x86_64
Size : 4.2 M
Source : bpftrace-0.11.0-8.ky10.src.rpm
Repository : @System
From repo : ks10-adv-updates-sp2
Summary : High-level tracing language for Linux eBPF
URL : https://github.com/iovisor/bpftrace
License : ASL 2.0
Description : BPFtrace is a high-level tracing language for Linux enhanced Berkeley Packet
: Filter (eBPF) available in recent Linux kernels (4.x). BPFtrace uses LLVM as a
: backend to compile scripts to BPF-bytecode and makes use of BCC for
: interacting with the Linux BPF system, as well as existing Linux tracing
: capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing
: (uprobes), and tracepoints. The BPFtrace language is inspired by awk and C,
: and predecessor tracers such as DTrace and SystemTap
三、bpftrace简单使用
3.1 探针
如图,kprobeb/kretprobe 为动态跟踪、内核级探针,kprobeb 是检测函数执行的开始,kretprobe 为检测结束(返回)。uprobe/uretprobe 为动态跟踪、用户级探针,uprobeb 是检测用户级函数执行的开始,uretprobe 为检测结束(返回)。tracepoint 为静态跟踪、用户级探针。
3.2 基础语法
3.2.1 单行语句
单行语句可以直接在命令行输入执行,ctrl + c 结束执行。
//打印输出
bpftrace -e 'BEGIN { printf("Hello, World!\n"); }'
//跟踪系统打开文件的进程,并打印文件名
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
tracepoint:syscalls:sys_enter_openat: 该探针类型为内核静态跟踪,并且当进入openat()系统调用时执行该探针;
comm 是内建变量,代表当前进程的名字。其它类似的变量还有 pid 和 tid,分别表示进程标识和线程标识;
args 为指针,指向该 tracepoint 的参数;
str() 用来把字符串指针转换成字符串;
3.2.2 文件形式
bpftrace 也可以执行 C style 的代码文件,格式为 bpftrace <filename>
。这种方式可以实现相对复杂的逻辑。比如通过 bpftrace 获取当前所有 shell 的输入,代码如下:
// 初始化时执行,主要用于打印提示信息
BEGIN
{
printf("Tracing bash commands... Hit Ctrl-C to end.\n");
printf("%-9s\t%s\t%s\t%s\n", "TIME", "UID", "PID", "COMMAND");
}
//探针追踪内容
uretprobe:/bin/bash:readline
{
time("%H:%M:%S\t");
printf("%d\t%d\t%s\n", uid, pid, str(retval));
}
//bpftrace结束时执行
END
{
printf("end-test");
}
更多 bpftrace 的使用说明可以参考官方文档:
bpftrace/reference_guide.md at master · iovisor/bpftrace (github.com)
bpftrace/tutorial_one_liners_chinese.md at master · iovisor/bpftrace (github.com)
四、文件监控实现
4.1 需求
通过监控 passwd 文件来找到是哪个进程或者是哪个用户删除了该文件
4.2 具体实现
创建 trace-delete.bt 文件
#include <linux/sched.h>
#include <linux/dcache.h>
tracepoint:syscalls:sys_enter_unlink,tracepoint:syscalls:sys_enter_unlinkat {
printf("%s deleted by command '%s' with pid %d\n",
str(args->pathname), comm, pid);
//取内核 task_struct 中 real_parent 字段
$p = curtask->real_parent;
/*
* 循环打印父进程名和 pid
* 5.3以上内核可使用 while
*/
unroll(10) {
printf(" ... whose parent is '%s' command with pid %d\n",
$p->comm,
$p->tgid);
$p = $p->parent;
}
}
Linux 下文件的删除一般通过2个系统调用:unlink 和 unlinkat。内核在系统调用出入口都有 tracepoint,能够暴露传入的参数。bpftrace 程序可以在这些点挂载 BPF 程序来采集实时状态。
注意:5.3以上的内核开始支持 C style whlie 和 for loop,并且可以使用 continue 和 break 关键字进行循环控制。5.3以下的内核只可使用 unroll() 方法。
4.3 演示(基于Kylin-Server-V10SP1-0518)
1、创建 bpftrace 监控程序和测试用文件夹
trace-delete.bt
测试文件夹
2、编写 DeleteFile 程序删除 /root/test-delete 文件夹,编译导出为可执行jar文件,模拟应用删除文件
package test;
import java.io.File;
public class DeleteFile {
public static void main(String[] args) {
File file = new File("/root/test-delete");
remove(file);
file.delete();
if (!file.exists()){
System.out.println("删除成功!");
}
try {
//模拟常驻运行的某服务
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void remove(File file){
File[] files = file.listFiles();
if (files!=null){
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()){
files[i].delete();
}else if(files[i].isDirectory()){
remove(files[i]);
}
files[i].delete();
}
}
}
}
3、运行监控程序
bpftrace trace-delete.bt
4、执行java程序删除文件夹
java -jar DeleteFile.jar
5、查看输出结果
更多信创知识分享,请关注 《大冻梨XC运维》
更多推荐
所有评论(0)