TÄ kÄ BPF virtuÄlÄ maŔīna turpina attÄ«stÄ«ties un tiek aktÄ«vi izmantota praksÄ, mÄs esam iztulkojuÅ”i jums rakstu, kurÄ aprakstÄ«tas tÄs galvenÄs iespÄjas un paÅ”reizÄjais stÄvoklis.
PÄdÄjos gados programmÄÅ”anas rÄ«ki un metodes ir kļuvuÅ”as arvien populÄrÄkas, lai kompensÄtu Linux kodola ierobežojumus gadÄ«jumos, kad nepiecieÅ”ama augstas veiktspÄjas pakeÅ”u apstrÄde. Viens no populÄrÄkajiem Å”Äda veida paÅÄmieniem tiek saukts kodola apieÅ”ana (kodola apieÅ”ana) un ļauj, apejot kodola tÄ«kla slÄni, veikt visu pakeÅ”u apstrÄdi no lietotÄja vietas. Kodola apieÅ”ana ietver arÄ« tÄ«kla kartes kontroli no lietotÄja telpa. Citiem vÄrdiem sakot, strÄdÄjot ar tÄ«kla karti, mÄs paļaujamies uz draiveri lietotÄja telpa.
Nododot pilnu tÄ«kla kartes vadÄ«bu uz lietotÄja telpas programmu, mÄs samazinÄm kodola papildu izmaksas (konteksta pÄrslÄgÅ”ana, tÄ«kla slÄÅa apstrÄde, pÄrtraukumi utt.), kas ir diezgan svarÄ«gi, ja darbojas ar Ätrumu 10Gb/s vai lielÄku. Kodola apieÅ”ana un citu funkciju kombinÄcija (partijas apstrÄde) un rÅ«pÄ«ga veiktspÄjas regulÄÅ”ana (NUMA grÄmatvedÄ«ba, CPU izolÄcijauc) atbilst augstas veiktspÄjas tÄ«kla apstrÄdes pamatiem lietotÄju telpÄ. VarbÅ«t Ŕīs jaunÄs pieejas pakeÅ”u apstrÄdei piemÄrs ir DPDK no Intel (Datu plaknes izstrÄdes komplekts), lai gan ir arÄ« citi labi zinÄmi rÄ«ki un paÅÄmieni, tostarp Cisco VPP (Vector Packet Processing), Netmap un, protams, Snabb.
TÄ«kla mijiedarbÄ«bas organizÄÅ”anai lietotÄja telpÄ ir vairÄki trÅ«kumi:
OS kodols ir aparatÅ«ras resursu abstrakcijas slÄnis. TÄ kÄ lietotÄju kosmosa programmÄm ir tieÅ”i jÄpÄrvalda savi resursi, tÄm ir arÄ« jÄpÄrvalda sava aparatÅ«ra. Tas bieži nozÄ«mÄ, ka paÅ”iem ir jÄprogrammÄ draiveri.
TÄ kÄ mÄs pilnÄ«bÄ atsakÄmies no kodola vietas, mÄs atsakÄmies arÄ« no visas kodola nodroÅ”inÄtÄs tÄ«kla funkcionalitÄtes. LietotÄja vietas programmÄm atkÄrtoti jÄievieÅ” funkcijas, kuras jau var nodroÅ”inÄt kodols vai operÄtÄjsistÄma.
Programmas darbojas smilÅ”kastes režīmÄ, kas nopietni ierobežo to mijiedarbÄ«bu un neļauj tÄm integrÄties ar citÄm operÄtÄjsistÄmas daļÄm.
BÅ«tÄ«bÄ, veidojot tÄ«klu lietotÄju telpÄ, veiktspÄjas pieaugums tiek sasniegts, pÄrvietojot pakeÅ”u apstrÄdi no kodola uz lietotÄja telpu. XDP rÄ«kojas tieÅ”i pretÄji: tas pÄrvieto tÄ«kla programmas no lietotÄja vietas (filtri, atrisinÄtÄji, marÅ”rutÄÅ”ana utt.) kodola telpÄ. XDP ļauj mums veikt tÄ«kla funkciju, tiklÄ«dz pakete nonÄk tÄ«kla saskarnÄ un pirms tÄ sÄk pÄrvietoties uz kodola tÄ«kla apakÅ”sistÄmu. RezultÄtÄ pakeÅ”u apstrÄdes Ätrums ievÄrojami palielinÄs. TomÄr kÄ kodols ļauj lietotÄjam izpildÄ«t savas programmas kodola telpÄ? Pirms atbildes uz Å”o jautÄjumu, apskatÄ«sim, kas ir BPF.
BPF un eBPF
Neskatoties uz mulsinoÅ”o nosaukumu, BPF (Berkeley Packet Filtering) patiesÄ«bÄ ir virtuÄlÄs maŔīnas modelis. Å Ä« virtuÄlÄ maŔīna sÄkotnÄji tika izstrÄdÄta, lai apstrÄdÄtu pakeÅ”u filtrÄÅ”anu, tÄpÄc arÄ« nosaukums.
Viens no slavenÄkajiem rÄ«kiem, kas izmanto BPF, ir tcpdump. Tverot paketes, izmantojot tcpdump lietotÄjs var norÄdÄ«t izteiksmi pakeÅ”u filtrÄÅ”anai. Tiks tvertas tikai paketes, kas atbilst Å”ai izteiksmei. PiemÄram, izteiciens "tcp dst port 80ā attiecas uz visÄm TCP paketÄm, kas nonÄk portÄ 80. Kompilators var saÄ«sinÄt Å”o izteiksmi, pÄrvÄrÅ”ot to BPF baitkodÄ.
$ sudo tcpdump -d "tcp dst port 80"
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 6
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 15
(004) ldh [56]
(005) jeq #0x50 jt 14 jf 15
(006) jeq #0x800 jt 7 jf 15
(007) ldb [23]
(008) jeq #0x6 jt 9 jf 15
(009) ldh [20]
(010) jset #0x1fff jt 15 jf 11
(011) ldxb 4*([14]&0xf)
(012) ldh [x + 16]
(013) jeq #0x50 jt 14 jf 15
(014) ret #262144
(015) ret #0
IepriekÅ” minÄtÄ programma pamatÄ dara to:
Instrukcija (000): ielÄdÄ paketi 12. nobÄ«dÄ kÄ 16 bitu vÄrdu akumulatorÄ. NobÄ«de 12 atbilst paketes Ätertipam.
Instrukcija (001): salÄ«dzina vÄrtÄ«bu akumulatorÄ ar 0x86dd, tas ir, ar Ätera tipa vÄrtÄ«bu IPv6. Ja rezultÄts ir patiess, programmas skaitÄ«tÄjs pÄriet uz instrukciju (002), un, ja nÄ, tad uz (006).
Instrukcija (006): salÄ«dzina vÄrtÄ«bu ar 0x800 (ethertype vÄrtÄ«ba IPv4). Ja atbilde ir patiesa, programma pÄriet uz (007), ja nÄ, tad uz (015).
Un tÄ tÄlÄk, lÄ«dz pakeÅ”u filtrÄÅ”anas programma atgriež rezultÄtu. Tas parasti ir BÅ«la vÄrtÄ«ba. VÄrtÄ«bas, kas nav nulle, atgrieÅ”ana (instrukcija (014)) nozÄ«mÄ, ka pakete tika pieÅemta, un nulles vÄrtÄ«bas atgrieÅ”ana (instrukcija (015)) nozÄ«mÄ, ka pakete netika pieÅemta.
TÄ kÄ BPF ir virtuÄla maŔīna, tÄ nosaka vidi, kurÄ darbojas programmas. Papildus baitkodam tas definÄ arÄ« pakeÅ”atmiÅas modeli (slodzes instrukcijas tiek netieÅ”i lietotas partijai), reÄ£istrus (A un X; akumulatoru un indeksu reÄ£istri), scratch atmiÅas krÄtuvi un netieÅ”o programmu skaitÄ«tÄju. Interesanti, ka BPF baitu kods tika veidots pÄc Motorola 6502 ISA. KÄ StÄ«vs Makkans atgÄdinÄja savÄ plenÄrsÄdes ziÅojums Sharkfest '11 viÅÅ” bija iepazinies ar build 6502 no vidusskolas laika programmÄÅ”anas uz Apple II, un Ŕīs zinÄÅ”anas ietekmÄja viÅa darbu, izstrÄdÄjot BPF baitu kodu.
BPF atbalsts ir ieviests Linux kodolÄ versijÄs v2.5 un jaunÄkÄs versijÄs, ko galvenokÄrt pievienoja Džeja Å ullista pÅ«les. BPF kods palika nemainÄ«gs lÄ«dz 2011. gadam, kad Äriks Dumasets pÄrveidoja BPF tulku, lai tas darbotos JIT režīmÄ (avots: JIT pakeÅ”u filtriem). PÄc tam kodols tÄ vietÄ, lai interpretÄtu BPF baitu kodu, varÄtu tieÅ”i konvertÄt BPF programmas uz mÄrÄ·a arhitektÅ«ru: x86, ARM, MIPS utt.
VÄlÄk, 2014. gadÄ, Aleksejs Starovoitovs ierosinÄja jaunu BPF JIT mehÄnismu. Faktiski Ŕī jaunÄ JIT kļuva par jaunu uz BPF balstÄ«tu arhitektÅ«ru, un to sauca par eBPF. Es domÄju, ka abas virtuÄlÄs maŔīnas kÄdu laiku pastÄvÄja lÄ«dzÄs, bet Å”obrÄ«d pakeÅ”u filtrÄÅ”ana tiek ieviesta, pamatojoties uz eBPF. Faktiski daudzos mÅ«sdienu dokumentÄcijas piemÄros BPF tiek saprasts kÄ eBPF, un klasiskais BPF mÅ«sdienÄs ir pazÄ«stams kÄ cBPF.
BalstÄ«ts uz modernÄm 64 bitu arhitektÅ«rÄm. eBPF izmanto 64 bitu reÄ£istrus un palielina pieejamo reÄ£istru skaitu no 2 (akumulators un X) lÄ«dz 10. eBPF nodroÅ”ina arÄ« papildu operÄcijas kodus (BPF_MOV, BPF_JNE, BPF_CALL...).
AtdalÄ«ts no tÄ«kla slÄÅa apakÅ”sistÄmas. BPF bija saistÄ«ts ar partijas datu modeli. TÄ kÄ tas tika izmantots pakeÅ”u filtrÄÅ”anai, tÄ kods atradÄs apakÅ”sistÄmÄ, kas nodroÅ”ina tÄ«kla sakarus. TomÄr eBPF virtuÄlÄ maŔīna vairs nav saistÄ«ta ar datu modeli un to var izmantot jebkuram mÄrÄ·im. TÄtad, tagad eBPF programmu var savienot ar tracepoint vai kprobe. Tas paver ceļu uz eBPF instrumentiem, veiktspÄjas analÄ«zi un daudziem citiem lietoÅ”anas gadÄ«jumiem citu kodola apakÅ”sistÄmu kontekstÄ. Tagad eBPF kods atrodas savÄ ceļÄ: kodols/bpf.
GlobÄlie datu veikali, ko sauc par Maps. Kartes ir atslÄgu vÄrtÄ«bu krÄtuves, kas nodroÅ”ina datu apmaiÅu starp lietotÄja vietu un kodola vietu. eBPF nodroÅ”ina vairÄku veidu kartes.
SekundÄrÄs funkcijas. Jo Ä«paÅ”i, lai pÄrrakstÄ«tu paketi, aprÄÄ·inÄtu kontrolsummu vai klonÄtu paketi. Å Ä«s funkcijas darbojas kodolÄ un nav lietotÄja telpas programmas. Varat arÄ« veikt sistÄmas zvanus no eBPF programmÄm.
Beigt zvanus. Programmas lielums eBPF ir ierobežots lÄ«dz 4096 baitiem. Astes izsaukuma funkcija ļauj eBPF programmai pÄrsÅ«tÄ«t vadÄ«bu uz jaunu eBPF programmu un tÄdÄjÄdi apiet Å”o ierobežojumu (Å”ÄdÄ veidÄ var saistÄ«t lÄ«dz 32 programmÄm).
eBPF: piemÄrs
Linux kodola avotos ir vairÄki eBPF piemÄri. Tie ir pieejami paraugi/bpf/. Lai apkopotu Å”os piemÄrus, vienkÄrÅ”i ievadiet:
$ sudo make samples/bpf/
Pats nerakstÄ«Å”u jaunu piemÄru priekÅ” eBPF, bet izmantoÅ”u kÄdu no samples/bpf/ pieejamajiem paraugiem. Es apskatÄ«Å”u dažas koda daļas un paskaidroÅ”u, kÄ tas darbojas. KÄ piemÄru es izvÄlÄjos programmu tracex4.
KopumÄ katrs no paraugiem/bpf/ esoÅ”ajiem piemÄriem sastÄv no diviem failiem. Å ajÄ gadÄ«jumÄ:
tracex4_kern.c, satur avota kodu, kas jÄizpilda kodolÄ kÄ eBPF baitkods.
tracex4_user.c, satur programmu no lietotÄja vietas.
Å ajÄ gadÄ«jumÄ mums ir jÄapkopo tracex4_kern.c uz eBPF baitkodu. PaÅ”laik atrodas gcc eBPF nav aizmugures. Par laimi, clang var izvadÄ«t eBPF baitkodu. Makefile izmanto clang apkopoÅ”anai tracex4_kern.c uz objekta failu.
IepriekÅ” minÄju, ka viena no interesantÄkajÄm eBPF funkcijÄm ir kartes. tracex4_kern definÄ vienu karti:
BPF_MAP_TYPE_HASH ir viens no daudzajiem eBPF piedÄvÄto karÅ”u veidiem. Å ajÄ gadÄ«jumÄ tas ir tikai hash. JÅ«s, iespÄjams, pamanÄ«jÄt arÄ« sludinÄjumu SEC("maps"). SEC ir makro, ko izmanto, lai izveidotu jaunu binÄrÄ faila sadaļu. PatiesÄ«bÄ piemÄrÄ tracex4_kern ir noteiktas vÄl divas sadaļas:
SEC("kprobe/kmem_cache_free")
int bpf_prog1(struct pt_regs *ctx)
{
long ptr = PT_REGS_PARM2(ctx);
bpf_map_delete_elem(&my_map, &ptr);
return 0;
}
SEC("kretprobe/kmem_cache_alloc_node")
int bpf_prog2(struct pt_regs *ctx)
{
long ptr = PT_REGS_RC(ctx);
long ip = 0;
// ŠæŠ¾Š»ŃŃŠ°ŠµŠ¼ ip-Š°Š“ŃŠµŃ Š²ŃŠ·ŃŠ²Š°ŃŃŠµŠ¹ ŃŃŠ¾ŃŠ¾Š½Ń kmem_cache_alloc_node()
BPF_KRETPROBE_READ_RET_IP(ip, ctx);
struct pair v = {
.val = bpf_ktime_get_ns(),
.ip = ip,
};
bpf_map_update_elem(&my_map, &ptr, &v, BPF_ANY);
return 0;
}
Å Ä«s divas funkcijas ļauj izdzÄst ierakstu no kartes (kprobe/kmem_cache_free) un pievienojiet kartei jaunu ierakstu (kretprobe/kmem_cache_alloc_node). Visi funkciju nosaukumi, kas rakstÄ«ti ar lielajiem burtiem, atbilst makro definÄtajiem bpf_helpers.h.
Ja es izmetu objekta faila sadaļas, man vajadzÄtu redzÄt, ka Ŕīs jaunÄs sadaļas jau ir definÄtas:
Ir arÄ« tracex4_user.c, galvenÄ programma. BÅ«tÄ«bÄ Å”Ä« programma klausÄs notikumus kmem_cache_alloc_node. Kad notiek Å”Äds notikums, tiek izpildÄ«ts atbilstoÅ”ais eBPF kods. Kods saglabÄ objekta IP atribÅ«tu kartÄ, un pÄc tam objekts tiek cilpas caur galveno programmu. PiemÄrs:
$ sudo ./tracex4
obj 0xffff8d6430f60a00 is 2sec old was allocated at ip ffffffff9891ad90
obj 0xffff8d6062ca5e00 is 23sec old was allocated at ip ffffffff98090e8f
obj 0xffff8d5f80161780 is 6sec old was allocated at ip ffffffff98090e8f
KÄ lietotÄja kosmosa programma un eBPF programma ir saistÄ«tas? Par inicializÄciju tracex4_user.c ielÄdÄ objekta failu tracex4_kern.o izmantojot funkciju load_bpf_file.
int main(int ac, char **argv)
{
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
char filename[256];
int i;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
perror("setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY)");
return 1;
}
if (load_bpf_file(filename)) {
printf("%s", bpf_log_buf);
return 1;
}
for (i = 0; ; i++) {
print_old_objects(map_fd[1]);
sleep(1);
}
return 0;
}
Darot load_bpf_file eBPF failÄ definÄtÄs zondes tiek pievienotas /sys/kernel/debug/tracing/kprobe_events. Tagad mÄs klausÄmies Å”os notikumus, un mÅ«su programma var kaut ko darÄ«t, kad tie notiek.
Visas pÄrÄjÄs programmas paraugÄ/bpf/ ir strukturÄtas lÄ«dzÄ«gi. Tajos vienmÄr ir divi faili:
XXX_kern.c: eBPF programma.
XXX_user.c: galvenÄ programma.
eBPF programma identificÄ kartes un funkcijas, kas saistÄ«tas ar sadaļu. Kad kodols izdod noteikta veida notikumu (piemÄram, tracepoint), tiek izpildÄ«tas saistÄ«tÄs funkcijas. Kartes nodroÅ”ina saziÅu starp kodola programmu un lietotÄja telpas programmu.
SecinÄjums
Å ajÄ rakstÄ BPF un eBPF tika apspriesti vispÄrÄ«gi. Es zinu, ka Å”odien ir daudz informÄcijas un resursu par eBPF, tÄpÄc es ieteikÅ”u vÄl dažus resursus turpmÄkai izpÄtei.
RÅ«pÄ«gs ievads eBPF Brendans Gregs. Raksts no LWN.net. Brendans bieži tvÄ«toja par eBPF un uztur resursu sarakstu par Å”o tÄmu emuÄra ziÅa.
PiezÄ«mes par BPF un eBPF Džūlija Evansa. KomentÄri par Suchakra Sharma prezentÄciju āThe BSD Packet Filter: A New Architecture for User-level Packet Captureā. KomentÄri ir labi un patieÅ”Äm palÄ«dz saprast slaidus.