struct key_t { // This is the process ID // which we will use to identify // in the hash map __u32 pid; };
存储数据的方式是 value struct
1 2 3 4 5 6
struct val_t { // used to understand the start time of the process __u64 start_time; // used to store the elapsed time of the process __u64 elapsed_time; };
eBpf HashMap 将他们联系在一起
1 2 3 4 5 6 7 8 9 10 11 12 13
struct { // The type of BPF map we are creating __uint(type, BPF_MAP_TYPE_HASH); // specifying the type to be used for the key __type(key, struct key_t); // specifying the type to be used as the value __type(value, struct val_t); // max amount of entries to store in the map __uint(max_entries, 10240); // name of the map as well as a section macro // from the bpf lib to designate this type // as a BPF map } process_time_map SEC(".maps");
我已经添加了注释,解释这些结构中每一行的作用。
创建 eBPF 函数
最后一步是创建 eBPF 函数。为此,我们需要一个 eBPF 程序。 这是一个 C 语言函数,带有一些宏标识,这样我们就可以使用先前定义的类型进行交互,例如:
SEC("tracepoint/sched/sched_switch") int cpu_processing_time(struct sched_switch_args *ctx) { // get the current time in ns __u64 ts = bpf_ktime_get_ns(); // we need to check if the process is in our map struct key_t prev_key = { .pid = ctx->prev_pid, }; struct val_t *val = bpf_map_lookup_elem(&process_time_map, &prev_key); // if the previous PID does not exist it means that we just started // watching or we missed the start somehow // so we ignore it for now if (val) { // Calculate and store the elapsed time for the process and we reset the // start time so we can measure the next cycle of that process __u64 elapsed_time = ts - val->start_time; struct val_t new_val = {.start_time = ts, .elapsed_time = elapsed_time}; bpf_map_update_elem(&process_time_map, &prev_key, &new_val, BPF_ANY); return 0; }; // we need to check if the next process is in our map // if it's not we need to set initial time struct key_t next_key = { .pid = ctx->next_pid, }; struct val_t *next_val = bpf_map_lookup_elem(&process_time_map, &prev_key); if (!next_val) { struct val_t next_new_val = {.start_time = ts}; bpf_map_update_elem(&process_time_map, &next_key, &next_new_val, BPF_ANY); return 0; } return 0; }
// this is the structure of the sched_switch event struct sched_switch_args { char prev_comm[TASK_COMM_LEN]; int prev_pid; int prev_prio; long prev_state; char next_comm[TASK_COMM_LEN]; int next_pid; int next_prio; };
SEC("tracepoint/sched/sched_switch") int cpu_processing_time(struct sched_switch_args *ctx) { // get the current time in ns __u64 ts = bpf_ktime_get_ns(); // we need to check if the process is in our map struct key_t prev_key = { .pid = ctx->prev_pid, }; struct val_t *val = bpf_map_lookup_elem(&process_time_map, &prev_key); // if the previous PID does not exist it means that we just started // watching or we missed the start somehow // so we ignore it for now if (val) { // Calculate and store the elapsed time for the process and we reset the // start time so we can measure the next cycle of that process __u64 elapsed_time = ts - val->start_time; struct val_t new_val = {.start_time = ts, .elapsed_time = elapsed_time}; bpf_map_update_elem(&process_time_map, &prev_key, &new_val, BPF_ANY); return 0; }; // we need to check if the next process is in our map // if it's not we need to set initial time struct key_t next_key = { .pid = ctx->next_pid, }; struct val_t *next_val = bpf_map_lookup_elem(&process_time_map, &prev_key); if (!next_val) { struct val_t next_new_val = {.start_time = ts}; bpf_map_update_elem(&process_time_map, &next_key, &next_new_val, BPF_ANY); return 0; } return 0; }
char _license[] SEC("license") = "Dual MIT/GPL";
这样,我们就完成了 BPF 程序。在本系列的下一篇文章中,我将介绍用 GO 语言编写用户空间程序,并使用名为 bpf2go 的工具来帮助我们实现程序绑定。
-------------The End-------------
subscribe to my blog by scanning my public wechat account