[[eBPF|BPF]]は狭義にはLinuxカーネルに含まれる**BPF仮想マシン**を指す。BPF仮想マシンは、[BPF用の独自の命令セット](https://github.com/iovisor/bpf-docs/blob/master/eBPF.md)で表現されたコード(**BPFバイトコード**)を解釈し、カーネルが動作するプロセッサに適したネイティブ命令に変換し、カーネルにロードする。ロードされたBPFプログラムは、指定されたイベントが発生するたびに実行される。
既存のソフトウェアをユーザーが拡張するために仮想マシンを組み込み、ユーザー定義のコードを解釈するような機構は他でもみられる。例えば、RedisはLuaの仮想マシンを組み込んでおり、ユーザーがLuaで独自のコマンドを定義できる。さらに、今日のより先進的な技術に[[WASM|Web Assembly]]がある。
BPFバイトコードは、一般に、制約付きのC言語で記述されたBPFプログラムからLLVM/Clangコンパイラにより生成される。BPFバイトコードは、ユーザー空間から[bpf(2)](https://man7.org/linux/man-pages/man2/bpf.2.html)システムコールにより、カーネルに渡される。カーネルはBPFの検証器(**BPF Verifier**)を使用して、BPFバイトコードがカーネルをクラッシュさせずに安全に実行可能かを検証する。検査結果に問題がなければ、BPFバイトコードは**JIT(Just in Time)コンパイラ**によりネイティブ命令によるマシンコードに変換される。このアーキテクチャによる恩恵は、BPFプログラムをロードするためにカーネルを再起動する必要がないことだ。
BPF Verifierの検証内容は多岐にわたる。例えば、カーネル内でのブロックを防ぐをために、終了が保証されないループを含むプログラムは棄却される。その他、変数の初期化や境界外のメモリへアクセスしないことを保証する。BPF Verifierのさらなる詳細は、eBPFの開発者であるStarovoitovによるeBPF Summit 2021の[Safe Programs, the Foundation of BPF](https://www.youtube.com/watch?v=AV8xY318rtc)の動画で解説されている。
カーネルはBPFプログラムを実行するにあたって、そのプログラムがどのフックポイントにアタッチされるかを知る必要がある。フックポイントは**BPFプログラムタイプ**に応じて定義されている。BPFプログラムタイプは、[bcc/kernel-versions.md](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#program-types)には、執筆時点で22個((この表にないBPF_PROG_TYPE_TRACINGもあるため、全てのプログラムタイプが記載されているわけではないかもしれない))のプログラムタイプが記載されている。トレーシングに使用されるプログラムタイプは次のようなものである。
- Kprobeプログラム(後述)
- Tracepointプログラム(後述)
- Perf Eventプログラム
- Raw Tracepointプログラム
その他、ネットワーキングに使用されるタイプとして、NIC(Network Interface Card)からカーネルに到着したパケットに対して処理をフックするためのXDP(eXpress Data Path)がある。
**BPFマップ**はカーネルとユーザスペースの間でデータを共有するためのストレージである。配列やハッシュマップ、キュー、スタック、リングバッファなどの様々な種類のデータ構造が用意されている。BPFマップのリストは[bcc/kernel-versions.md](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md#tables-aka-maps)に網羅されている。
BPFプログラムは、カーネルバージョンの互換性のために、カーネルが提供する安定したAPIである**ヘルパー関数**を呼び出せる。任意のカーネル関数を呼び出すことはできない。ヘルパー関数には、BPFマップにアクセスするための関数や自身のスレッドID、uid/gidなどを取得する関数などが含まれる。ヘルパー関数のリストは[bpf-docs/bpf_helpers.rst](https://github.com/iovisor/bpf-docs/blob/master/bpf_helpers.rst)にある。