目录
代码分析
BPF 程序部分
功能说明
BPF_KSYSCALL
用户程序部分
功能说明
执行效果
ksyscall与kprobe 的区别
#include "vmlinux.h"
#include
#include
#include
#define TASK_COMM_LEN 16
SEC("ksyscall/tgkill")
int BPF_KSYSCALL(tgkill_entry, pid_t tgid, pid_t tid, int sig)
{
char comm[TASK_COMM_LEN];
__u32 caller_pid = bpf_get_current_pid_tgid() >> 32;
if (sig == 0) {
/*
If sig is 0, then no signal is sent, but existence and permission
checks are still performed; this can be used to check for the
existence of a process ID or process group ID that the caller is
permitted to signal.
*/
return 0;
}
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk(
"tgkill syscall called by PID %d (%s) for thread id %d with pid %d and signal %d.",
caller_pid, comm, tid, tgid, sig);
return 0;
}
SEC("ksyscall/kill")
int BPF_KSYSCALL(entry_probe, pid_t pid, int sig)
{
char comm[TASK_COMM_LEN];
__u32 caller_pid = bpf_get_current_pid_tgid() >> 32;
if (sig == 0) {
/*
If sig is 0, then no signal is sent, but existence and permission
checks are still performed; this can be used to check for the
existence of a process ID or process group ID that the caller is
permitted to signal.
*/
return 0;
}
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk("KILL syscall called by PID %d (%s) for PID %d with signal %d.", caller_pid,
comm, pid, sig);
return 0;
}
char _license[] SEC("license") = "GPL";
它追踪了两个系统调用:kill 和 tgkill。当这些系统调用被执行时,BPF 程序会打印出相关的信息。
tgkill和kill都是 Linux 的系统调用,主要用于向进程或线程发送信号。但它们的工作方式和使用场景有所不同。
区别:kill 和 tgkill 的主要区别在于它们发送信号的目标不同。kill 只能向整个进程发送信号,而 tgkill 则可以精确地向进程中的特定线程发送信号。这使得 tgkill 在需要对多线程程序中的特定线程进行信号控制的场景下更为有用。例如,在一个多线程的应用中,你可能需要向一个特定的线程发送一个信号,而不是整个进程,这种情况下,使用 tgkill 就会更加合适。
BPF_KSYSCALL 做了以下工作:
#include
#include
#include
#include
#include
#include
#include
#include "ksyscall.skel.h"
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
return vfprintf(stderr, format, args);
}
static volatile sig_atomic_t stop;
static void sig_int(int signo)
{
stop = 1;
}
int main(int argc, char **argv)
{
struct ksyscall_bpf *skel;
int err;
/* Set up libbpf errors and debug info callback */
libbpf_set_print(libbpf_print_fn);
/* Open load and verify BPF application */
skel = ksyscall_bpf__open_and_load();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
/* Attach tracepoint handler */
err = ksyscall_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}
if (signal(SIGINT, sig_int) == SIG_ERR) {
fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
goto cleanup;
}
printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
"to see output of the BPF programs.\n");
while (!stop) {
fprintf(stderr, ".");
sleep(1);
}
cleanup:
ksyscall_bpf__destroy(skel);
return -err;
}
加载一个 BPF 程序,将其附加到内核,然后等待用户的中断信号。
clash-linux-6908 [002] d..31 78746.276570: bpf_trace_printk: tgkill syscall called by PID 6907 (clash-linux) for thread id 11399 with pid 6907 and signal 23.
clash-linux-6908 [002] d..31 78747.635256: bpf_trace_printk: tgkill syscall called by PID 6907 (clash-linux) for thread id 11399 with pid 6907 and signal 23.
clash-linux-6908 [002] d..31 78748.020156: bpf_trace_printk: tgkill syscall called by PID 6907 (clash-linux) for thread id 11399 with pid 6907 and signal 23.
clash-linux-6908 [002] d..31 78751.021938: bpf_trace_printk: tgkill syscall called by PID 6907 (clash-linux) for thread id 33841 with pid 6907 and signal 23.
clash-linux-6908 [000] d..31 78754.276769: bpf_trace_printk: tgkill syscall called by PID 6907 (clash-linux) for thread id 33841 with pid 6907 and signal 23.
ksyscall和kprobe都是用于Linux内核追踪和分析的机制,主要区别如下: