BPF(Berkely Packet Fileter)
eBPF(extended BPF)
1992년에 패킷 필터로 패킷을 분석하고 필터링 하는 곳에 사용됐던 in-kernel virtual machine이다. BSD에서 처음 도입했으며 리눅스에서도 이 개념을 빌려와서 서브 시스템을 만들었다. in-kernel virtual machine이라고 함은 정말로 가상의 레지스터와 스택을 갖고 있으며 이를 바탕으로 코드를 실행한다는 뜻이다.
virtual machine이라고 속도가 느린 것이 아니라 JIT 컴파일러가 추가되면서 BPF가 실행하는 명령어와 프로세서 사이에 어떤 계층이 존재하는 것이 아니라 프로세서가 직접 명령어를 수행하게 된다.
BPF는 패킷 필터로 시작해서 현재는 리눅스 시스템에 관측 가능성을 부여하는 도구이다. 만들어진 지는 리눅스만큼 오래 되었으나 최근 5년간 새로운 활용법을 찾아 많은 관심을 받고 있다.
BPF는 유연함과 안정성으로 사용자가 커널 내부를 들여다 볼 수 있도록 한다. 즉, 리눅스 안에서 무슨 일이 일어나는 지 분석할 수 있는 것이다. 관측 가능성을 부여하면 리눅스에서 왜 지연시간이 발생하는지, 어느 지점에서 병목 현상이 발생하는 지 등을 관찰할 수 있게 된다.
eBPF는 BPF의 확장 버전이다. 기존의 BPF에서 사용하던 머신에서 더 나아가서 레지스터의 크기를 늘려주고 스택과 맵을 도입하는 등의 변화가 있었다. 그래서 기존의 BPF를 cBPF(classic BPF)라고 부르고 새로운 BPF를 eBPF라고 부르게 되었다.
BPF 프로그램은 위의 코드처럼 커널 내에 미리 정의된 훅이나 kprobe, uprobe, tracepoint를 사용해서 프로그램을 실행할 수 있다. syscall_ret_execve()
이 실행될 때마다 BPF 프로그램을 실행해서 새로운 프로세스가 어떻게 만들어지는 지 관찰한다.
BPF 프로그램은 C로 작성되어 바이트코드로 번역되어 동작한다. 바이트코드로 번역된 프로그램은 Verifier에 의 검증된 후 JIT 컴파일러로 컴파일 하면 실행할 수 있는 상태가 된다.
BPF는 사용자 측에서 가져온 코드를 커널에서 실행하기 때문에 안전성이 매우 중요하다. 따라서 코드를 검증할 필요가 있는데, 시스템의 안정성을 해칠만한 코드는 사용할 수 없다. 예를 들어서 무한 루프가 발생할 수 있기 때문에 반복문은 매우 제한적으로 지원한다.
매우 자주 실행되는 함수를 트레이싱할 경우 오버헤드가 엄청나다.
인라인 함수를 트레이싱 하려면 매우 번거롭다.
사용자 공간 함수를 트레이싱 하는 경우에는 커널 공간을 들렀다가 가야 하므로 비효율적이다.
지원되는 구문이 제한적이다.
고정된 스택을 갖는다. (512바이트)