요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
===============
libbpf Overview
===============
libbpf is a C-based library containing a BPF loader that takes compiled BPF
object files and prepares and loads them into the Linux kernel. libbpf takes the
heavy lifting of loading, verifying, and attaching BPF programs to various
kernel hooks, allowing BPF application developers to focus only on BPF program
correctness and performance.
The following are the high-level features supported by libbpf:
* Provides high-level and low-level APIs for user space programs to interact
with BPF programs. The low-level APIs wrap all the bpf system call
functionality, which is useful when users need more fine-grained control
over the interactions between user space and BPF programs.
* Provides overall support for the BPF object skeleton generated by bpftool.
The skeleton file simplifies the process for the user space programs to access
global variables and work with BPF programs.
* Provides BPF-side APIS, including BPF helper definitions, BPF maps support,
and tracing helpers, allowing developers to simplify BPF code writing.
* Supports BPF CO-RE mechanism, enabling BPF developers to write portable
BPF programs that can be compiled once and run across different kernel
versions.
This document will delve into the above concepts in detail, providing a deeper
understanding of the capabilities and advantages of libbpf and how it can help
you develop BPF applications efficiently.
BPF App Lifecycle and libbpf APIs
==================================
A BPF application consists of one or more BPF programs (either cooperating or
completely independent), BPF maps, and global variables. The global
variables are shared between all BPF programs, which allows them to cooperate on
a common set of data. libbpf provides APIs that user space programs can use to
manipulate the BPF programs by triggering different phases of a BPF application
lifecycle.
The following section provides a brief overview of each phase in the BPF life
cycle:
* **Open phase**: In this phase, libbpf parses the BPF
object file and discovers BPF maps, BPF programs, and global variables. After
a BPF app is opened, user space apps can make additional adjustments
(setting BPF program types, if necessary; pre-setting initial values for
global variables, etc.) before all the entities are created and loaded.
* **Load phase**: In the load phase, libbpf creates BPF
maps, resolves various relocations, and verifies and loads BPF programs into
the kernel. At this point, libbpf validates all the parts of a BPF application
and loads the BPF program into the kernel, but no BPF program has yet been
executed. After the load phase, it’s possible to set up the initial BPF map
state without racing with the BPF program code execution.
* **Attachment phase**: In this phase, libbpf
attaches BPF programs to various BPF hook points (e.g., tracepoints, kprobes,
cgroup hooks, network packet processing pipeline, etc.). During this
phase, BPF programs perform useful work such as processing
packets, or updating BPF maps and global variables that can be read from user
space.
* **Tear down phase**: In the tear down phase,
libbpf detaches BPF programs and unloads them from the kernel. BPF maps are
destroyed, and all the resources used by the BPF app are freed.
BPF Object Skeleton File
========================
BPF skeleton is an alternative interface to libbpf APIs for working with BPF
objects. Skeleton code abstract away generic libbpf APIs to significantly
simplify code for manipulating BPF programs from user space. Skeleton code
includes a bytecode representation of the BPF object file, simplifying the
process of distributing your BPF code. With BPF bytecode embedded, there are no
extra files to deploy along with your application binary.
You can generate the skeleton header file ``(.skel.h)`` for a specific object
file by passing the BPF object to the bpftool. The generated BPF skeleton
provides the following custom functions that correspond to the BPF lifecycle,
each of them prefixed with the specific object name:
* ``<name>__open()`` – creates and opens BPF application (``<name>`` stands for
the specific bpf object name)
* ``<name>__load()`` – instantiates, loads,and verifies BPF application parts
* ``<name>__attach()`` – attaches all auto-attachable BPF programs (it’s
optional, you can have more control by using libbpf APIs directly)
* ``<name>__destroy()`` – detaches all BPF programs and
frees up all used resources
Using the skeleton code is the recommended way to work with bpf programs. Keep
in mind, BPF skeleton provides access to the underlying BPF object, so whatever
was possible to do with generic libbpf APIs is still possible even when the BPF
skeleton is used. It's an additive convenience feature, with no syscalls, and no
cumbersome code.
Other Advantages of Using Skeleton File
---------------------------------------
* BPF skeleton provides an interface for user space programs to work with BPF
global variables. The skeleton code memory maps global variables as a struct
into user space. The struct interface allows user space programs to initialize
BPF programs before the BPF load phase and fetch and update data from user
space afterward.
* The ``skel.h`` file reflects the object file structure by listing out the
available maps, programs, etc. BPF skeleton provides direct access to all the
BPF maps and BPF programs as struct fields. This eliminates the need for
string-based lookups with ``bpf_object_find_map_by_name()`` and
``bpf_object_find_program_by_name()`` APIs, reducing errors due to BPF source
code and user-space code getting out of sync.
* The embedded bytecode representation of the object file ensures that the
skeleton and the BPF object file are always in sync.
BPF Helpers
===========
libbpf provides BPF-side APIs that BPF programs can use to interact with the
system. The BPF helpers definition allows developers to use them in BPF code as
any other plain C function. For example, there are helper functions to print
debugging messages, get the time since the system was booted, interact with BPF
maps, manipulate network packets, etc.
For a complete description of what the helpers do, the arguments they take, and
the return value, see the `bpf-helpers
<https://man7.org/linux/man-pages/man7/bpf-helpers.7.html>`_ man page.
BPF CO-RE (Compile Once – Run Everywhere)
=========================================
BPF programs work in the kernel space and have access to kernel memory and data
structures. One limitation that BPF applications come across is the lack of
portability across different kernel versions and configurations. `BCC
<https://github.com/iovisor/bcc/>`_ is one of the solutions for BPF
portability. However, it comes with runtime overhead and a large binary size
from embedding the compiler with the application.
libbpf steps up the BPF program portability by supporting the BPF CO-RE concept.
BPF CO-RE brings together BTF type information, libbpf, and the compiler to
produce a single executable binary that you can run on multiple kernel versions
and configurations.
To make BPF programs portable libbpf relies on the BTF type information of the
running kernel. Kernel also exposes this self-describing authoritative BTF
information through ``sysfs`` at ``/sys/kernel/btf/vmlinux``.
You can generate the BTF information for the running kernel with the following
command:
::
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
The command generates a ``vmlinux.h`` header file with all kernel types
(:doc:`BTF types <../btf>`) that the running kernel uses. Including
``vmlinux.h`` in your BPF program eliminates dependency on system-wide kernel
headers.
libbpf enables portability of BPF programs by looking at the BPF program’s
recorded BTF type and relocation information and matching them to BTF
information (vmlinux) provided by the running kernel. libbpf then resolves and
matches all the types and fields, and updates necessary offsets and other
relocatable data to ensure that BPF program’s logic functions correctly for a
specific kernel on the host. BPF CO-RE concept thus eliminates overhead
associated with BPF development and allows developers to write portable BPF
applications without modifications and runtime source code compilation on the
target machine.
The following code snippet shows how to read the parent field of a kernel
``task_struct`` using BPF CO-RE and libbf. The basic helper to read a field in a
CO-RE relocatable manner is ``bpf_core_read(dst, sz, src)``, which will read
``sz`` bytes from the field referenced by ``src`` into the memory pointed to by
``dst``.
.. code-block:: C
:emphasize-lines: 6
//...
struct task_struct *task = (void *)bpf_get_current_task();
struct task_struct *parent_task;
int err;
err = bpf_core_read(&parent_task, sizeof(void *), &task->parent);
if (err) {
/* handle error */
}
/* parent_task contains the value of task->parent pointer */
In the code snippet, we first get a pointer to the current ``task_struct`` using
``bpf_get_current_task()``. We then use ``bpf_core_read()`` to read the parent
field of task struct into the ``parent_task`` variable. ``bpf_core_read()`` is
just like ``bpf_probe_read_kernel()`` BPF helper, except it records information
about the field that should be relocated on the target kernel. i.e, if the
``parent`` field gets shifted to a different offset within
``struct task_struct`` due to some new field added in front of it, libbpf will
automatically adjust the actual offset to the proper value.
Getting Started with libbpf
===========================
Check out the `libbpf-bootstrap <https://github.com/libbpf/libbpf-bootstrap>`_
repository with simple examples of using libbpf to build various BPF
applications.
See also `libbpf API documentation
<https://libbpf.readthedocs.io/en/latest/api.html>`_.
libbpf and Rust
===============
If you are building BPF applications in Rust, it is recommended to use the
`Libbpf-rs <https://github.com/libbpf/libbpf-rs>`_ library instead of bindgen
bindings directly to libbpf. Libbpf-rs wraps libbpf functionality in
Rust-idiomatic interfaces and provides libbpf-cargo plugin to handle BPF code
compilation and skeleton generation. Using Libbpf-rs will make building user
space part of the BPF application easier. Note that the BPF program themselves
must still be written in plain C.
libbpf logging
==============
By default, libbpf logs informational and warning messages to stderr. The
verbosity of these messages can be controlled by setting the environment
variable LIBBPF_LOG_LEVEL to either warn, info, or debug. A custom log
callback can be set using ``libbpf_set_print()``.
Additional Documentation
========================
* `Program types and ELF Sections <https://libbpf.readthedocs.io/en/latest/program_types.html>`_
* `API naming convention <https://libbpf.readthedocs.io/en/latest/libbpf_naming_convention.html>`_
* `Building libbpf <https://libbpf.readthedocs.io/en/latest/libbpf_build.html>`_
* `API documentation Convention <https://libbpf.readthedocs.io/en/latest/libbpf_naming_convention.html#api-documentation-convention>`_
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
libbpf Overview 소개와 주요 기능
1-30`libbpf Overview` 문서는 `GPL-2.0` license를 따릅니다. libbpf는 compile된 BPF object file을 받아 준비한 뒤 Linux kernel에 load하는 BPF loader를 포함한 C library입니다. BPF program을 여러 kernel hook에 load, verify, attach하는 복잡한 작업을 맡으므로 application developer는 BPF program의 correctness와 performance에 집중할 수 있습니다.
Libbpf가 제공하는 high-level feature는 다음과 같습니다.
- User space program이 BPF program과 상호 작용할 수 있도록 high-level API와 low-level API를 제공합니다. Low-level API는 모든 `bpf` system call 기능을 감싸며, user space와 BPF program 사이의 상호 작용을 세밀하게 제어할 때 유용합니다.
- `bpftool`이 생성한 `BPF Object Skeleton`을 전반적으로 지원합니다. Skeleton file은 user space program이 global variable에 접근하고 BPF program을 다루는 과정을 단순화합니다.
- BPF helper definition, BPF map 지원, tracing helper를 포함한 BPF-side API를 제공하여 BPF code 작성을 단순화합니다.
- `BPF CO-RE` mechanism을 지원하여 한 번 compile한 portable BPF program을 서로 다른 kernel version에서 실행할 수 있게 합니다.
이 문서는 위 개념을 자세히 살펴보면서 libbpf의 capability와 advantage, 그리고 BPF application을 효율적으로 개발하는 데 libbpf가 어떤 도움을 주는지 설명합니다.
BPF application lifecycle과 libbpf API
31-68BPF application은 서로 협력하거나 독립적으로 동작하는 하나 이상의 BPF program, BPF map, global variable로 구성됩니다. Global variable은 모든 BPF program이 공유하므로 공통 data set을 바탕으로 협력할 수 있습니다. Libbpf API를 사용하는 user space program은 lifecycle의 각 phase를 trigger하여 BPF program을 조작합니다.
BPF application lifecycle의 각 phase는 다음과 같습니다.
- **Open phase**: libbpf가 BPF object file을 parse하여 BPF map, BPF program, global variable을 찾습니다. Application을 연 뒤에는 entity를 생성하고 load하기 전에 필요한 BPF program type을 설정하거나 global variable의 initial value를 미리 지정하는 등 추가 조정을 할 수 있습니다.
- **Load phase**: libbpf가 BPF map을 만들고 여러 relocation을 resolve하며 BPF program을 verify하여 kernel에 load합니다. 이 시점에는 application의 모든 부분을 검증하고 program을 load했지만 아직 BPF program을 실행하지 않았으므로, program execution과 race하지 않고 BPF map의 initial state를 설정할 수 있습니다.
- **Attachment phase**: libbpf가 BPF program을 tracepoint, kprobe, cgroup hook, network packet processing pipeline 같은 BPF hook point에 attach합니다. Program은 packet을 처리하거나 user space에서 읽을 수 있는 BPF map과 global variable을 update하는 등 실제 작업을 수행합니다.
- **Tear down phase**: libbpf가 BPF program을 detach하고 kernel에서 unload합니다. BPF map을 destroy하고 BPF application이 사용한 모든 resource를 해제합니다.
BPF Object Skeleton file
69-97BPF skeleton은 BPF object를 다루는 일반 libbpf API의 대체 interface입니다. Generic API를 추상화하여 user space에서 BPF program을 조작하는 code를 크게 단순화합니다. Skeleton code에는 BPF object file의 bytecode representation이 들어 있으므로 application binary와 함께 별도 BPF file을 배포하지 않아도 됩니다.
BPF object를 `bpftool`에 전달하면 해당 object의 skeleton header `(.skel.h)`를 생성할 수 있습니다. 생성된 skeleton은 특정 object name을 prefix로 사용하며 BPF lifecycle에 대응하는 다음 custom function을 제공합니다.
- `<name>__open()`: BPF application을 생성하고 엽니다. 여기서 `<name>`은 특정 BPF object name입니다.
- `<name>__load()`: BPF application의 각 부분을 instantiate하고 load하며 verify합니다.
- `<name>__attach()`: auto-attach할 수 있는 모든 BPF program을 attach합니다. 이 단계는 optional이며 generic libbpf API로 직접 더 세밀하게 제어할 수도 있습니다.
- `<name>__destroy()`: 모든 BPF program을 detach하고 사용한 resource를 해제합니다.
Skeleton code는 BPF program을 다루는 권장 방식입니다. Skeleton에서도 underlying BPF object에 접근할 수 있으므로 generic libbpf API로 가능했던 작업을 그대로 수행할 수 있습니다. 즉 syscall이나 번거로운 code를 추가하지 않고 편의를 더하는 additive feature입니다.
Skeleton file의 추가 장점
98-116- BPF skeleton은 user space program이 BPF global variable을 다룰 수 있는 interface를 제공합니다. Skeleton code는 global variable을 struct로 user space에 memory-map합니다. 이 struct interface를 통해 load phase 전에 BPF program을 initialize하고, 이후 user space에서 data를 가져오거나 update할 수 있습니다.
- `skel.h`는 사용할 수 있는 map과 program 등을 나열하여 object file structure를 반영합니다. BPF map과 BPF program을 struct field로 직접 노출하므로 `bpf_object_find_map_by_name()` 및 `bpf_object_find_program_by_name()` 같은 string-based lookup이 필요 없습니다. 그 결과 BPF source code와 user-space code가 서로 어긋나 발생하는 error를 줄입니다.
- Object file의 embedded bytecode representation은 skeleton과 BPF object file이 항상 같은 상태로 유지되도록 보장합니다.
BPF helper
117-129Libbpf는 BPF program이 system과 상호 작용할 때 사용할 BPF-side API를 제공합니다. BPF helper definition 덕분에 developer는 helper를 일반 C function처럼 BPF code에서 호출할 수 있습니다. Debug message 출력, system boot 이후 시간 조회, BPF map 상호 작용, network packet 조작 등을 위한 helper가 있습니다.
각 helper의 동작, argument, return value 전체 설명은 [bpf-helpers man page](https://man7.org/linux/man-pages/man7/bpf-helpers.7.html)에서 확인할 수 있습니다.
BPF CO-RE의 기반과 vmlinux.h 생성
130-155BPF program은 kernel space에서 동작하며 kernel memory와 data structure에 접근합니다. 하지만 kernel version과 configuration이 달라지면 portability가 떨어지는 문제가 있습니다. [BCC](https://github.com/iovisor/bcc/)도 BPF portability를 해결하는 방법이지만, compiler를 application에 내장하므로 runtime overhead와 큰 binary size를 수반합니다.
`BPF CO-RE`는 BTF type information, libbpf, compiler를 결합하여 여러 kernel version과 configuration에서 실행할 수 있는 단일 executable binary를 만듭니다.
Libbpf는 실행 중인 kernel의 BTF type information을 이용해 BPF program을 portable하게 만듭니다. Kernel은 self-describing authoritative BTF information을 `sysfs`의 `/sys/kernel/btf/vmlinux`에 공개합니다.
다음 command로 실행 중인 kernel의 BTF information을 생성할 수 있습니다.
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
CO-RE type과 relocation resolve
156-170이 command는 실행 중인 kernel이 사용하는 모든 kernel type을 담은 `vmlinux.h` header를 생성합니다. BPF program에 `vmlinux.h`를 include하면 system-wide kernel header에 대한 dependency가 사라집니다.
Libbpf는 BPF program에 기록된 BTF type 및 relocation information을 실행 중인 kernel이 제공하는 BTF information인 vmlinux와 대조합니다. 이어서 모든 type과 field를 resolve하고 match한 뒤 필요한 offset과 relocatable data를 update하여 host의 특정 kernel에서도 program logic이 올바르게 동작하도록 합니다.
따라서 BPF CO-RE는 BPF development에 수반되는 overhead를 없애고, target machine에서 source code를 수정하거나 runtime compile하지 않아도 portable BPF application을 작성할 수 있게 합니다.
bpf_core_read() 사용 예
171-200다음 code는 BPF CO-RE와 libbpf를 사용하여 kernel `task_struct`의 parent field를 읽습니다. CO-RE relocation이 가능한 방식으로 field를 읽는 기본 helper는 `bpf_core_read(dst, sz, src)`입니다. 이 helper는 `src`가 참조하는 field에서 `sz` byte를 읽어 `dst`가 가리키는 memory에 저장합니다.
//...
struct task_struct *task = (void *)bpf_get_current_task();
struct task_struct *parent_task;
int err;
err = bpf_core_read(&parent_task, sizeof(void *), &task->parent);
if (err) {
/* handle error */
}
/* parent_task contains the value of task->parent pointer */
먼저 `bpf_get_current_task()`로 현재 `task_struct` pointer를 얻습니다. 그런 다음 `bpf_core_read()`로 task struct의 parent field를 `parent_task` variable에 읽어 옵니다.
`bpf_core_read()`는 `bpf_probe_read_kernel()` BPF helper와 비슷하지만 target kernel에서 relocate해야 할 field 정보를 기록한다는 차이가 있습니다. 예를 들어 앞쪽에 새 field가 추가되어 `struct task_struct` 내부의 `parent` offset이 달라져도 libbpf가 실제 offset을 자동으로 올바른 값으로 조정합니다.
libbpf 시작 자료
201-210여러 BPF application을 libbpf로 만드는 간단한 예제는 [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) repository에서 확인할 수 있습니다.
함수와 type별 reference는 [libbpf API documentation](https://libbpf.readthedocs.io/en/latest/api.html)을 참고합니다.
Rust application과 libbpf
211-221Rust로 BPF application을 만들 때는 libbpf에 직접 bindgen binding을 적용하기보다 [Libbpf-rs](https://github.com/libbpf/libbpf-rs) library를 사용하는 것이 좋습니다. Libbpf-rs는 libbpf 기능을 Rust-idiomatic interface로 감싸고, BPF code compilation과 skeleton generation을 처리하는 libbpf-cargo plugin을 제공합니다.
Libbpf-rs는 BPF application의 user space 부분을 더 쉽게 만들 수 있게 하지만, BPF program 자체는 여전히 plain C로 작성해야 합니다.
libbpf logging
222-229기본적으로 libbpf는 informational message와 warning message를 stderr에 기록합니다. `LIBBPF_LOG_LEVEL` environment variable을 `warn`, `info`, `debug` 중 하나로 설정하여 verbosity를 제어할 수 있습니다. `libbpf_set_print()`로 custom log callback을 지정할 수도 있습니다.
추가 문서
230-236- [Program types and ELF Sections](https://libbpf.readthedocs.io/en/latest/program_types.html)
- [API naming convention](https://libbpf.readthedocs.io/en/latest/libbpf_naming_convention.html)
- [Building libbpf](https://libbpf.readthedocs.io/en/latest/libbpf_build.html)
- [API documentation Convention](https://libbpf.readthedocs.io/en/latest/libbpf_naming_convention.html#api-documentation-convention)
요약과 해설
libbpf_overview.rst:1-236Libbpf는 BPF object를 parse, verify, load, attach하는 C library입니다. Open, load, attachment, tear down lifecycle을 API와 generated skeleton으로 다루며, skeleton은 bytecode와 global variable interface를 application에 결합합니다.
BPF CO-RE는 kernel BTF와 `vmlinux.h`, compiler relocation 정보를 이용해 kernel version 차이를 흡수합니다. `bpf_core_read()` 같은 helper는 field 위치가 바뀌어도 libbpf가 target kernel의 실제 offset으로 조정할 수 있게 합니다.
문서는 helper reference, libbpf-bootstrap, API documentation, Libbpf-rs, logging level과 추가 libbpf guide도 함께 연결합니다.