Microsoft MVP성태의 닷넷 이야기
Linux: 100. eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE) [링크 복사], [링크+제목 복사],
조회: 1557
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 11개 있습니다.)
Linux: 86. Golang + bpf2go를 사용한 eBPF 기본 예제
; https://www.sysnet.pe.kr/2/0/13769

Linux: 94. eBPF - vmlinux.h 헤더 포함하는 방법 (bpf2go에서 사용)
; https://www.sysnet.pe.kr/2/0/13783

Linux: 95. eBPF - kprobe를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13784

Linux: 96. eBPF (bpf2go) - fentry, fexit를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13788

Linux: 100.  eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)
; https://www.sysnet.pe.kr/2/0/13801

Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
; https://www.sysnet.pe.kr/2/0/13810

Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
; https://www.sysnet.pe.kr/2/0/13815

Linux: 106. eBPF / bpf2go - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
; https://www.sysnet.pe.kr/2/0/13817

Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
; https://www.sysnet.pe.kr/2/0/13819

Linux: 109. eBPF / bpf2go - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
; https://www.sysnet.pe.kr/2/0/13824

Linux: 110. eBPF / bpf2go - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
; https://www.sysnet.pe.kr/2/0/13825




eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)

초기에는 eBPF를 libbcc를 이용해 개발을 했는데요, 이 방식의 특징은 eBPF 코드를 "텍스트"로 포함한 뒤, 대상 리눅스 머신에서 실행하는 시점에 컴파일을 하고 로드하는 방식이라는 점입니다.

Why libbpf and BPF CO-RE?
; https://nakryiko.com/posts/bcc-to-libbpf-howto-guide/#why-libbpf-and-bpf-co-re

Historically, BCC was a framework of choice when you had to develop a BPF application that required peering into the internals of the kernel when implementing all sorts of tracing BPF programs. BCC provided a built-in Clang compiler that could compile your BPF code in runtime, tailoring it to a specific target kernel on the host. This was the only way to develop a maintainable BPF application that had to deal with internals of an ever-changing kernel.


웹상에 eBPF 코드 예제를 찾아보면, 대표적으로 파이썬 코드들이 그런 방식에 속하는데요,

#!/usr/bin/python3
from bcc import BPF

program = r"""
int hello(void *ctx) {
    bpf_trace_printk("Hello World!");
    return 0;
}
"""

b = BPF(text=program)
syscall = b.get_syscall_fnname ("execve")
b.attach_kprobe(event=syscall, fn_name="hello")
b.trace_print()

위의 프로그램을 실행하면, 파이썬 런타임이 대상 컴퓨터에서 실행되는 시점에 eBPF 코드를 clang/libbcc를 이용해 컴파일하고 로드합니다. 그러니까, 커널 의존적인 접근을 실행 시점에 컴파일함으로써 해결하고 있는 것입니다.

반면 libbpf를 이용한 방법은,

libbpf/libbpf
; https://github.com/libbpf/libbpf#bpf-co-re-compile-once--run-everywhere

// BPF loader (libbpf)
// https://nakryiko.com/posts/bpf-portability-and-co-re/#bpf-loader-libbpf

All the previous pieces of data (kernel BTF and Clang relocations) are coming together and are processed by libbpf, which serves as a BPF program loader. It takes compiled BPF ELF object file, post-processes it as necessary, sets up various kernel objects (maps, programs, etc), and triggers BPF program loading and verification.


eBPF 코드를 "바이트 코드"로 한 번 컴파일하는 작업을 수행한 뒤, 실행 시에는 (clang/libbcc 의존 없이) 대상 환경에서 바로 로드하면서 약간의 post-process 작업을 하는 식입니다.

따라서, CO-RE(Compile Once - Run Everywhere)라는 말은 eBPF 프로그램 중에서도 기존의 libbcc 방식이 아닌, 새롭게 나온 libbpf에서만 가능한 유형입니다.

아래의 글에 이에 대한 방식의 차이점을 잘 설명하고 있는데요,

Hello eBPF: First steps with libbpf (5)
; https://mostlynerdless.de/blog/2024/02/26/hello-ebpf-first-steps-with-libbpf-5/

위에서는 Python이 아닌 Java를 이용한 BPF 예제를 소개하고 있는데, libbcc와 libbpf를 이용할 때의 차이점을 아래 그림으로 잘 표현하고 있습니다.

ebpf_bcc_bpf_1.png

그렇다면, 제가 이전에 소개한 bpf2go를 이용한 프로그램은 어떤 방식일까요? 예상하시겠지만 bpf2go를 이용해 미리 eBPF 코드를 컴파일한 다음, 런타임 시에 "로드"만 한 후 실행하기 때문에 libbpf를 이용한 방식에 속합니다.

사실, 어느 한 쪽의 방식이 절대적인 장점을 갖고 있진 않습니다. libbcc는 파이썬과 같은 인터프리터 언어에서도 쉽게 사용할 수 있지만 clang/libbcc에 대한 의존을 가지고 있어 3rd-party 라이브러리나 독립적인 응용 프로그램을 제작/배포하는 시나리오에서는 약간 불편한 점이 있습니다. 만약 "그 정도의 의존성"이라고 생각한다면 libbcc를 이용한 방식도 괜찮지만, "의존성이 없는" 방식을 선호한다면 libbpf를 이용한 방식을 선택하는 것이 좋습니다.

따라서, python으로 개발한 방식은 시스템에 bpfcc-tools 패키지를 반드시 설치해야 하지만, bpf2go를 이용한 방식은 아무런 의존성 없이 그 프로그램을 곧바로 실행할 수 있습니다.

대충 어떤 차이점이 있는지 아시겠죠? ^^




기존 libbcc에서 libbpf 방식으로의 변경은 eBPF 코드에서도 수정이 필요하다고 합니다.

BPF code conversion
; https://nakryiko.com/posts/bcc-to-libbpf-howto-guide/#bpf-code-conversion

위의 글에 나온 내용을 간략히 정리해 볼까요? ^^


1. Detecting BCC vs libbpf modes

먼저 기존 환경에 대한 호환성을 확보해야 한다면, eBPF 소스코드를 libbcc, libbpf 2개에 맞춰 관리를 해야 할 텐데요, 다행히 기존 BCC 헤더에는 이렇게 매크로가 정의돼 있으므로,

#ifdef BCC_SEC
#define __BCC__
#endif

우리가 만드는 코드에서 바로 그 매크로 정의(__BCC__)를 이용해 코드 선택을 할 수 있다고 합니다.

#ifdef __BCC__
/* BCC-specific code */
#else
/* libbpf-specific code */
#endif


2. Header includes

libbpf/BPF CO-RE를 위해서는 리눅스 커널 헤더를 직접 include하는 방식에서, vmlinux.h를 포함하는 것으로 바꿔야 한다고 합니다.

eBPF - vmlinux.h 헤더 포함하는 방법 (bpf2go에서 사용)
; https://www.sysnet.pe.kr/2/0/13783

따라서 __BCC__를 이용한다면 대충 이렇게 처리할 수 있습니다.

#ifdef __BCC__
/* linux headers needed for BCC only */
#else /* __BCC__ */
#include "vmlinux.h"               /* all kernel types */
#include <bpf/bpf_helpers.h>       /* most used helpers: SEC, __always_inline, etc */
#include <bpf/bpf_core_read.h>     /* for BPF CO-RE helpers */
#include <bpf/bpf_tracing.h>       /* for getting kprobe arguments */
#endif /* __BCC__ */


3. Field accesses

기존 libbcc 방식은 소스코드에 사용한 커널 구조체의 필드 접근에 대해,

{
    struct task_struct* task = (struct task_struct*)bpf_get_current_task();
    pid_t pid = task->parent->pid;

    bpf_printk("parent-pid == %x\n", pid);

    return 0;
}

라이브러리 자체적으로 bpf_probe_read로 미리 번역하는 작업을 할 수 있었다고 합니다. 하지만 libbpf 방식에서는 저런 단계를 거칠 수 없으므로 빌드 시 이런 오류가 발생합니다.

field KprobeSysClone: program kprobe_sys_clone: load program: permission denied: 1: (79) r1 = *(u64 *)(r0 +2520): R0 invalid mem access 'scalar' (5 line(s) omitted)

따라서 개발자가 직접 bpf_probe_read를 호출해 필드를 접근하는 것으로, 또는 그 과정을 편리하게 해주는 BPF_CORE_READ를 이용해야 합니다.

SEC("kprobe/sys_clone") int BPF_KPROBE(kprobe_sys_clone, unsigned long clone_flags)
{
    struct task_struct* task = (struct task_struct*)bpf_get_current_task();
    pid_t pid = BPF_CORE_READ(task, parent, pid);

    bpf_printk("parent-pid == %d\n", pid);

    return 0;
}


#define __CORE_RELO(src, field, info)                         \
    __builtin_preserve_field_info((src)->field, BPF_FIELD_##info)

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define __CORE_BITFIELD_PROBE_READ(dst, src, fld)                 \
    bpf_probe_read_kernel(                            \
            (void *)dst,                          \
            __CORE_RELO(src, fld, BYTE_SIZE),             \
            (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))
#else
/* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so
 * for big-endian we need to adjust destination pointer accordingly, based on
 * field byte size
 */
#define __CORE_BITFIELD_PROBE_READ(dst, src, fld)                 \
    bpf_probe_read_kernel(                            \
            (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \
            __CORE_RELO(src, fld, BYTE_SIZE),             \
            (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))
#endif

/*
 * Extract bitfield, identified by s->field, and return its value as u64.
 * All this is done in relocatable manner, so bitfield changes such as
 * signedness, bit size, offset changes, this will be handled automatically.
 * This version of macro is using bpf_probe_read_kernel() to read underlying
 * integer storage. Macro functions as an expression and its return type is
 * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error.
 */
#define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({                \
    unsigned long long val = 0;                       \
                                          \
    __CORE_BITFIELD_PROBE_READ(&val, s, field);               \
    val <<= __CORE_RELO(s, field, LSHIFT_U64);                  \
    if (__CORE_RELO(s, field, SIGNED))                    \
        val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64);  \
    else                                      \
        val = val >> __CORE_RELO(s, field, RSHIFT_U64);             \
    val;                                      \
})


4. BPF maps

libbpf에서는 BPF maps를 다루는 방식이 libbcc와 다르다고 합니다. 그런데, 이전과는 다르게 이번에는 오히려 매크로를 안 쓰는 방향으로 바뀌었군요. ^^

/* Array */
#ifdef __BCC__
BPF_ARRAY(my_array_map, struct my_value, 128);
#else
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 128);
    __type(key, u32);
    __type(value, struct my_value);
} my_array_map SEC(".maps");
#endif

/* Hashmap */
#ifdef __BCC__
BPF_HASH(my_hash_map, u32, struct my_value);
#else
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 10240);
    __type(key, u32);
    __type(value, struct my_value);
} my_hash_map SEC(".maps")
#endif

/* Per-CPU array */
#ifdef __BCC__
BPF_PERCPU_ARRAY(heap, struct my_value, 1);
#else
struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __uint(max_entries, 1);
    __type(key, u32);
    __type(value, struct my_value);
} heap SEC(".maps");
#endif

맵의 기본 크기도 바뀌었다고 하는데요, 기존 BCC에서는 10240이었던 것이, libbpf에서는 크기를 명시적으로 설정할 수 있다고 합니다.

그 외에, PERF_EVENT_ARRAY, STACK_TRACE와 몇몇 맵 구현체(DEVMAP, CPUMAP, ...)들은 키/값으로 BTF 타입을 (아직) 사용할 수 없다고 합니다. 따라서, 그 크기를 직접 지정해야 한다고.

/* Perf event array (for use with perf_buffer API) */
#ifdef __BCC__
BPF_PERF_OUTPUT(events);
#else
struct {
    __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
    __uint(key_size, sizeof(u32));
    __uint(value_size, sizeof(u32));
} events SEC(".maps");
#endif


5. Accessing BPF maps from BPF code

BCC 방식에서는 map 접근 코드를 마치 C++ 문법처럼 사용할 수 있게 해주고 이후 중간에서 적절한 eBPF 코드로 바꿔주었다고 하는데, 이것 역시 libbpf에서는 개발자가 직접 처리해야 한다고 합니다.

#ifdef __BCC__
    struct event *data = heap.lookup(&zero);
#else
    struct event *data = bpf_map_lookup_elem(&heap, &zero);
#endif

#ifdef __BCC__
    my_hash_map.update(&id, my_val);
#else
    bpf_map_update_elem(&my_hash_map, &id, &my_val, 0 /* flags */);
#endif

#ifdef __BCC__
    events.perf_submit(args, data, data_len);
#else
    bpf_perf_event_output(args, &events, BPF_F_CURRENT_CPU, data, data_len);
#endif


6. BPF programs

eBPF 코드의 함수에는 반드시 SEC() 매크로를 이용한 사용자 정의 섹션명을 지정해야 한다고 합니다.

#if !defined(__BCC__)
SEC("tracepoint/sched/sched_process_exec")
#endif
int tracepoint__sched__sched_process_exec(
#ifdef __BCC__
    struct tracepoint__sched__sched_process_exec *args
#else
    struct trace_event_raw_sched_process_exec *args
#endif
) {
/* ... */
}

임의로 정할 수 있는 섹션명이긴 하지만 그건 어디까지나 libbpf 개발자들의 입장이고, 그 기반으로 eBPF 프로그램을 작성하는 개발자 입장에서는 libbpf가 정한 이름 규칙을 따라야 합니다.

예를 들면, 이런 식입니다.

  • tp/<category>/<name> for tracepoints;
  • kprobe/<func_name> for kprobe and kretprobe/<func_name> for kretprobe;
  • raw_tp/<name> for raw tracepoint;
  • cgroup_skb/ingress, cgroup_skb/egress, and a whole family of cgroup/<subtype> programs.


7. Tracepoints

tracepoint 유형으로 작성된 eBPF 함수의 인자 타입이 BCC 방식에서는 "tracepoint__<category>__<name>" 패턴이었다고 하는데, libbpf에서는 "trace_event_raw_<name>" 형식으로 바뀌었다고 합니다.

그런데, "name" 영역이 좀 애매한 것 같습니다. 가령, "tracepoint/syscalls/sys_enter_connect" 유형인 경우 그것의 이름에서 가장 의미 있는 것은 "connect"와 "enter"일 듯한데요, 실제로 찾아보면,

$ cat vmlinux.h | grep "struct trace_event_raw_sys*"
struct trace_event_raw_sys_enter {
struct trace_event_raw_sys_exit {

"connect"의 의미가 날아간 trace_event_raw_sys_enter가 나옵니다. 다시 말하면, syscalls 유형의 경우 공통적으로 sys_enter, sys_exit 구조체가 매개변수 타입으로 사용된다고 보면 됩니다.

SEC("tracepoint/syscalls/sys_enter_connect")
int sys_enter_connect(struct trace_event_raw_sys_enter *ctx) {
    // ...[생략]...
}

// SEC("tp/syscalls/sys_exit_connect")
SEC("tracepoint/syscalls/sys_exit_connect")
int sys_exit_connect(struct trace_event_raw_sys_exit* ctx) {
    // ...[생략]...
}

그런데, 때로는 공통 타입을 재사용하기 때문에 "trace_event_raw_<name>" 형식으로 찾을 수 없는 경우도 있다고 합니다. 그 사례로, sched_process_exit가 있습니다.

$ sudo bpftrace -l "tracepoint:*sched_process_exit"
tracepoint:sched:sched_process_exit

일반적인 패턴을 따랐다면 이것의 매개변수 타입은 trace_event_raw_sched_process_exit가 되었어야 하지만, 커널 측에서는 이미 내부에 만들어져 사용하고 있던 타입, 즉 trace_event_raw_sched_process_template을 매개변수 타입으로 사용해야 한다는데... 이건 커널 측 소스코드를 참고하지 않고서는 알아내기가 꽤나 어렵지 않을까 싶습니다.


8. Kprobes

BPF_KPROBE 매크로를 이용해 kprobe를 정의할 때 매개변수를 pt_regs 구조체를 숨긴 채로 열거할 수 있습니다.

#ifdef __BCC__
int kprobe__acct_collect(struct pt_regs *ctx, long exit_code, int group_dead)
#else
SEC("kprobe/acct_collect")
int BPF_KPROBE(kprobe__acct_collect, long exit_code, int group_dead)
#endif
{
    /* BPF code accessing exit_code and group_dead here */
}

참고로, syscall 관련 함수의 이름 규칙이 4.17 커널부터 변경되었다고 하는데요,

// https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscall_64.c
#define __SYSCALL(nr, sym) extern long __x64_##sym(const struct pt_regs *);
#define __SYSCALL_NORETURN(nr, sym) extern long __noreturn __x64_##sym(const struct 

// https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscall_32.c
#define __SYSCALL(nr, sym) extern long __ia32_##sym(const struct pt_regs *);
#define __SYSCALL_NORETURN(nr, sym) extern long __noreturn __ia32_##sym(const struct pt_regs *);

그전에는 단순히 "sys_kill" 이름이었던 것이, 이제는 플랫폼에 따른 __x64_, __ia32_ 접두사가 붙는다고 합니다.

// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h#L360

#define __NR_kill 129
__SYSCALL(__NR_kill, sys_kill)

그러고 보니 전에 kprobe를 bpftrace로 테스트할 때는 sys_clone이라고 하면 안 되고, 반드시 __x64_sys_clone으로 지정해야만 했었는데요, 재미있는 건 이것도 프레임워크마다 달라질 수 있으므로 유의해야 합니다. 즉, bpf2go를 사용한 경우에는 그냥 SEC("kprobe/sys_clone")라고 지정해도 잘 동작합니다.

어쨌든, 이런 플랫폼 의존적인 이름 규칙이 싫다면 tracepoint를 사용하는 것이 좋겠습니다.

의미 있는 또 하나의 변화라면, 커널 5.5부터는 tracepoint/kprobe/kretprobe 각각에 대해 그보다 더 성능이 나은 raw_tp/fentry/fexit을 제공한다는 점!


9. Dealing with compile-time #if's in BCC

BCC 방식의 경우, 런타임 시에 컴파일을 하기 때문에 그 시점에 사용자 정의 매크로 변수를 전달하는 것이 가능한 구조였습니다. 하지만, libbpf의 경우에는 미리 컴파일을 하기 때문에 그 과정이 불가능한데요, 대신 이런 간극을 보완할 수 있도록 커널 버전에 따른 분기를 할 수 있는 방법을 제공한다고 합니다.

#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))

extern int LINUX_KERNEL_VERSION __kconfig;

if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 2, 0)) {
  /* deal with older kernels */
} else {
  /* 5.2 or newer */
}

이와 함께, 현재 커널 빌드 환경에 대한 Kconfig 변수를, 즉 CONFIG_xxx에 해당하는 값을,

// Ubuntu 22.04 환경
$ cat /boot/config-$(uname -r) | grep ^CONFIG_HZ=
CONFIG_HZ=1000

$ cat /boot/config-$(uname -r) | grep ^CONFIG_NLS_DEFAULT
CONFIG_NLS_DEFAULT="utf8"

// WSL + Ubuntu 20.04 환경
$ zcat /proc/config.gz | grep ^CONFIG_HZ=
CONFIG_HZ=100

이렇게 매핑해서 사용할 수 있습니다.

extern int LINUX_KERNEL_VERSION __kconfig;
extern int CONFIG_HZ __kconfig;
extern char CONFIG_NLS_DEFAULT[128] __kconfig;

SEC("tracepoint/syscalls/sys_enter_connect")
int sys_enter_connect(struct trace_event_raw_sys_enter *ctx) {
    long unsigned int hz = CONFIG_HZ;
    bool is_5_2_or_newer = LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 2, 0);

    if (is_5_2_or_newer)
    {
        bpf_printk("[new] hz == %d, kernel == %x, nls == %s\n", hz, LINUX_KERNEL_VERSION, CONFIG_NLS_DEFAULT);
    }
    else
    {
        bpf_printk("[old] hz == %d, kernel == %x, nls == %s\n", hz, LINUX_KERNEL_VERSION, CONFIG_NLS_DEFAULT);
    }
    return 0;
}

이 외에도 대상 커널에서 필드의 이름이 변경되었거나 하위 구조체로 위치가 변경된 경우를 감지하기 위해 bpf_core_field_exists 함수가 제공된다고 하는데요, 테스트를 위해 WSL 2 (5.15.153.1-microsoft-standard-WSL2+) 환경의 vmlinux.h에 있는 task_struct 타입에 "unsigned int cpu" 필드를 골라봤습니다.

SEC("tracepoint/syscalls/sys_exit_connect")
int sys_exit_connect(struct trace_event_raw_sys_exit* ctx) {

    struct task_struct* task = (struct task_struct *)bpf_get_current_task();
    unsigned int cpu_value = 0;

    if (bpf_core_field_exists(task->cpu)) {
        cpu_value = BPF_CORE_READ(task, cpu);
    } else {
        cpu_value = -1;
    }

    bpf_printk("sys_exit_connect - cpu_value == %d\n", cpu_value);

    return 0;
}

빌드 후 실행하면 cpu_value가 양수로 나왔는데요, (task_struct.cpu가 존재하지 않는) 6.11.5 버전의 커널 환경에서 실행했더니 eBPF 코드가 실행되면서 cpu_value == -1이 나왔습니다. 참고로, 저 코드의 평가는 실행 시점이 아닌 BPF verifier가 eBPF 코드를 로딩하는 단계에서 이뤄진다는 것입니다. 따라서 verifier는 cpu 필드가 없는 경우 그 브랜치를 "dead code"로 평가하게 되고 아예 삭제를 해버립니다. 따라서, 개발자는 부담 없이 bpf_core_field_exists를 이용한 if 평가를 남용해도 됩니다.

이런 식으로 bpf_core_type_exists, bpf_core_enum_value_exists가 제공되니 참고하세요.


10. Application configuration

Map 없이도 eBPF 소스코드에 전역 변수를 정의하는 방법이 제공되는데요, 즉 외부에서 자유롭게 설정값을 전달할 수 있어 런타임 시에 동작을 변경할 수 있는 유연함을 제공합니다. "10. Application configuration" 글의 예제를 보면, process id를 외부에서 설정함으로써 원하는 프로세스와 관련된 정보만을 다루고 있습니다.

bpf2go의 경우 웬일인지 "Global Variables" 문서에 나오는 대로 현재 실습이 안 됩니다. (loadVariables 함수도 없고, Variables 속성도 없습니다.) 찾아보니까,

New API in Collection{Spec} for modifying global BPF variables #1542
; https://github.com/cilium/ebpf/issues/1542

Collection: add Variables field to interact with global variables #1572
; https://github.com/cilium/ebpf/pull/1572

현재 자동 생성되는 코드에 전역 변수의 get/set 접근자를 추가하는 중인 듯한데요, 아마도 저게 나오면 실습이 되지 않을까 싶습니다.




시간이 되시면 아래의 글도 읽어보세요. ^^

[eBPF] BPF 실행파일 로딩 과정 분석 (JIT)
; https://velog.io/@haruband/eBPF-BPF-실행파일-로딩-과정-분석-JIT

BPF Architecture - JIT
; https://docs.cilium.io/en/stable/bpf/architecture/#jit

eBPF를 실행하시는 시점에서조차도, 그 바이트 코드를 interpreter 형식으로 실행할지, 아니면 JIT를 거쳐 아예 기계어 코드로 변환해 실행할지에 대한 선택도 가능합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/15/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13838정성태12/4/2024632오류 유형: 935. Windbg - Breakpoint 0's offset expression evaluation failed.
13837정성태12/3/2024698디버깅 기술: 204. Windbg - 윈도우 핸들 테이블 (3) - Windows 10 이상인 경우
13836정성태12/3/2024922디버깅 기술: 203. Windbg - x64 가상 주소를 물리 주소로 변환 (페이지 크기가 2MB인 경우)
13835정성태12/2/2024985오류 유형: 934. Azure - rm: cannot remove '...': Directory not empty
13834정성태11/29/20241073Windows: 275. C# - CUI 애플리케이션과 Console 윈도우 (Windows 10 미만의 Classic Console 모드인 경우)파일 다운로드1
13833정성태11/29/20241077개발 환경 구성: 737. Azure Web App에서 Scale-out으로 늘어난 리눅스 인스턴스에 SSH 접속하는 방법
13832정성태11/27/20241110Windows: 274. Windows 7부터 도입한 conhost.exe
13831정성태11/27/2024977Linux: 111. eBPF - BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_RINGBUF에 대한 다양한 용어들
13830정성태11/25/20241069개발 환경 구성: 736. 파이썬 웹 앱을 Azure App Service에 배포하기
13829정성태11/25/20241026스크립트: 67. 파이썬 - Windows 버전에서 함께 설치되는 py.exe
13828정성태11/25/20241047개발 환경 구성: 735. Azure - 압축 파일을 이용한 web app 배포 시 디렉터리 구분이 안 되는 문제파일 다운로드1
13827정성태11/25/20241117Windows: 273. Windows 환경의 파일 압축 방법 (tar, Compress-Archive)
13826정성태11/21/20241174닷넷: 2313. C# - (비밀번호 등의) Console로부터 입력받을 때 문자열 출력 숨기기(echo 끄기)파일 다운로드1
13825정성태11/21/20241144Linux: 110. eBPF / bpf2go - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
13824정성태11/20/20241083Linux: 109. eBPF / bpf2go - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
13823정성태11/20/20241084개발 환경 구성: 734. Ubuntu에 docker, kubernetes (k3s) 설치
13822정성태11/20/20241044개발 환경 구성: 733. Windbg - VirtualBox VM의 커널 디버거 연결 시 COM 포트가 없는 경우
13821정성태11/18/20241170Linux: 108. Linux와 Windows의 프로세스/스레드 ID 관리 방식
13820정성태11/18/20241127VS.NET IDE: 195. Visual C++ - C# 프로젝트처럼 CopyToOutputDirectory 항목을 추가하는 방법
13819정성태11/15/20241122Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
13818정성태11/15/20241215Windows: 272. Windows 11 24H2 - sudo 추가
13817정성태11/14/20241100Linux: 106. eBPF / bpf2go - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
13816정성태11/14/20241157닷넷: 2312. C#, C++ - Windows / Linux 환경의 Thread Name 설정파일 다운로드1
13815정성태11/13/20241100Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
13814정성태11/13/20241213닷넷: 2311. C# - Windows / Linux 환경에서 Native Thread ID 가져오기파일 다운로드1
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...