Cyflwyniad Byr i BPF ac eBPF

Helo, Habr! Hoffem eich hysbysu ein bod yn paratoi llyfr i'w ryddhau."Arsylwi Linux gyda BPF".

Cyflwyniad Byr i BPF ac eBPF
Gan fod peiriant rhithwir BPF yn parhau i esblygu ac yn cael ei ddefnyddio'n ymarferol yn ymarferol, rydym wedi cyfieithu erthygl i chi sy'n disgrifio ei brif alluoedd a'i gyflwr presennol.

Yn ystod y blynyddoedd diwethaf, mae offer a thechnegau rhaglennu wedi dod yn fwyfwy poblogaidd i wneud iawn am gyfyngiadau'r cnewyllyn Linux mewn achosion lle mae angen prosesu pecynnau perfformiad uchel. Gelwir un o'r technegau mwyaf poblogaidd o'r math hwn ffordd osgoi cnewyllyn (ffordd osgoi cnewyllyn) ac yn caniatáu, gan osgoi'r haen rhwydwaith cnewyllyn, i berfformio'r holl brosesu pecynnau o ofod defnyddwyr. Mae osgoi'r cnewyllyn hefyd yn golygu rheoli'r cerdyn rhwydwaith o gofod defnyddiwr. Mewn geiriau eraill, wrth weithio gyda cherdyn rhwydwaith, rydym yn dibynnu ar y gyrrwr gofod defnyddiwr.

Trwy drosglwyddo rheolaeth lawn o'r cerdyn rhwydwaith i raglen gofod defnyddiwr, rydym yn lleihau gorbenion cnewyllyn (newid cyd-destun, prosesu haen rhwydwaith, ymyriadau, ac ati), sy'n eithaf pwysig wrth redeg ar gyflymder o 10Gb / s neu uwch. Ffordd osgoi cnewyllyn ynghyd â chyfuniad o nodweddion eraill (prosesu swp) a thiwnio perfformiad yn ofalus (NUMA cyfrifyddu, ynysu CPU, ac ati) yn cyfateb i hanfodion prosesu rhwydwaith perfformiad uchel yn y gofod defnyddwyr. Efallai mai enghraifft ragorol o'r dull newydd hwn o brosesu pecynnau yw DPDK gan Intel (Pecyn Datblygu Plane Data), er bod offer a thechnegau adnabyddus eraill, gan gynnwys VPP Cisco (Vector Packet Processing), Netmap ac, wrth gwrs, snab.

Mae nifer o anfanteision i drefnu rhyngweithiadau rhwydwaith yn y gofod defnyddwyr:

  • Mae'r cnewyllyn OS yn haen tynnu ar gyfer adnoddau caledwedd. Oherwydd bod yn rhaid i raglenni gofod defnyddwyr reoli eu hadnoddau'n uniongyrchol, mae'n rhaid iddynt hefyd reoli eu caledwedd eu hunain. Mae hyn yn aml yn golygu gorfod rhaglennu eich gyrwyr eich hun.
  • Oherwydd ein bod yn rhoi'r gorau i ofod cnewyllyn yn gyfan gwbl, rydym hefyd yn rhoi'r gorau i'r holl ymarferoldeb rhwydweithio a ddarperir gan y cnewyllyn. Rhaid i raglenni gofod defnyddiwr ail-weithredu swyddogaethau a allai gael eu darparu eisoes gan y cnewyllyn neu'r system weithredu.
  • Mae rhaglenni'n gweithredu yn y modd blwch tywod, sy'n cyfyngu'n ddifrifol ar eu rhyngweithio ac yn eu hatal rhag integreiddio â rhannau eraill o'r system weithredu.

Yn y bôn, wrth rwydweithio yn y gofod defnyddwyr, cyflawnir enillion perfformiad trwy symud prosesu pecynnau o'r cnewyllyn i ofod defnyddwyr. Mae XDP yn gwneud yn union i'r gwrthwyneb: mae'n symud rhaglenni rhwydweithio o ofod defnyddwyr (hidlwyr, datryswyr, llwybro, ac ati) i ofod cnewyllyn. Mae XDP yn caniatáu inni gyflawni swyddogaeth rhwydwaith cyn gynted ag y bydd pecyn yn taro rhyngwyneb rhwydwaith a chyn iddo ddechrau symud i fyny i'r is-system rhwydwaith cnewyllyn. O ganlyniad, mae cyflymder prosesu'r pecyn yn cynyddu'n sylweddol. Fodd bynnag, sut mae'r cnewyllyn yn caniatáu i'r defnyddiwr weithredu eu rhaglenni yn y gofod cnewyllyn? Cyn ateb y cwestiwn hwn, gadewch i ni edrych ar beth yw BPF.

BPF ac eBPF

Er gwaethaf yr enw dryslyd, mae BPF (Berkeley Packet Filtering), mewn gwirionedd, yn fodel peiriant rhithwir. Cynlluniwyd y peiriant rhithwir hwn yn wreiddiol i drin hidlo pecynnau, a dyna pam yr enw.

Un o'r offer mwyaf enwog sy'n defnyddio BPF yw tcpdump. Wrth gipio pecynnau gan ddefnyddio tcpdump gall y defnyddiwr nodi mynegiant i hidlo pecynnau. Dim ond pecynnau sy'n cyfateb i'r mynegiad hwn fydd yn cael eu dal. Er enghraifft, mae'r ymadrodd “tcp dst port 80” yn cyfeirio at yr holl becynnau TCP sy'n cyrraedd porthladd 80. Gall y casglwr fyrhau'r ymadrodd hwn trwy ei drosi i god byte BPF.

$ 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

Dyma beth mae'r rhaglen uchod yn ei wneud yn y bôn:

  • Cyfarwyddyd (000): Yn llwytho'r pecyn ar wrthbwyso 12, fel gair 16-did, i'r cronadur. Mae Offset 12 yn cyfateb i ethertype y pecyn.
  • Cyfarwyddyd (001): yn cymharu'r gwerth yn y cronadur â 0x86dd, hynny yw, gyda'r gwerth ethertype ar gyfer IPv6. Os yw'r canlyniad yn wir, yna mae rhifydd y rhaglen yn mynd i'r cyfarwyddiadau (002), ac os na, yna i (006).
  • Cyfarwyddyd (006): yn cymharu'r gwerth â 0x800 (gwerth ethertype ar gyfer IPv4). Os yw'r ateb yn wir, yna mae'r rhaglen yn mynd i (007), os na, yna i (015).

Ac yn y blaen nes bod y rhaglen hidlo pecyn yn dychwelyd canlyniad. Boole yw hwn fel arfer. Mae dychwelyd gwerth di-sero (cyfarwyddyd (014)) yn golygu bod y pecyn wedi'i dderbyn, ac mae dychwelyd gwerth sero (cyfarwyddyd (015)) yn golygu na dderbyniwyd y pecyn.

Cynigiwyd peiriant rhithwir BPF a'i god beit gan Steve McCann a Van Jacobson ddiwedd 1992 pan gyhoeddwyd eu papur Hidlo Pecyn BSD: Pensaernïaeth Newydd ar gyfer Dal Pecyn Lefel Defnyddiwr, cyflwynwyd y dechnoleg hon gyntaf yng nghynhadledd Usenix yn ystod gaeaf 1993.

Oherwydd bod BPF yn beiriant rhithwir, mae'n diffinio'r amgylchedd y mae rhaglenni'n rhedeg ynddo. Yn ogystal â'r bytecode, mae hefyd yn diffinio'r model cof swp (cymhwysir cyfarwyddiadau llwyth yn ymhlyg i'r swp), cofrestrau (A ac X; cofrestrau croniadur a mynegai), storio cof crafu, a chownter rhaglen ymhlyg. Yn ddiddorol, modelwyd y bytecode BPF ar ôl y Motorola 6502 ISA. Fel y cofiodd Steve McCann yn ei adroddiad llawn yn Sharkfest '11, roedd yn gyfarwydd ag adeiladu 6502 o'i raglennu dyddiau ysgol uwchradd ar yr Apple II, a dylanwadodd y wybodaeth hon ar ei waith yn dylunio côd byte BPF.

Gweithredir cefnogaeth BPF yn y cnewyllyn Linux mewn fersiynau v2.5 ac uwch, wedi'i ychwanegu'n bennaf gan ymdrechion Jay Schullist. Arhosodd y cod BPF heb ei newid tan 2011, pan ailgynlluniodd Eric Dumaset y dehonglydd BPF i redeg yn y modd JIT (Ffynhonnell: JIT ar gyfer hidlwyr pecyn). Ar ôl hyn, gallai'r cnewyllyn, yn lle dehongli cod byte BPF, drosi rhaglenni BPF yn uniongyrchol i'r bensaernïaeth darged: x86, ARM, MIPS, ac ati.

Yn ddiweddarach, yn 2014, cynigiodd Alexey Starovoytov fecanwaith JIT newydd ar gyfer BPF. Mewn gwirionedd, daeth y JIT newydd hwn yn bensaernïaeth newydd yn seiliedig ar BPF a chafodd ei alw'n eBPF. Rwy'n credu bod y ddau VM yn cydfodoli ers peth amser, ond ar hyn o bryd mae hidlo pecynnau yn cael ei weithredu yn seiliedig ar eBPF. Mewn gwirionedd, mewn llawer o enghreifftiau o ddogfennaeth fodern, deallir BPF fel eBPF, a gelwir BPF clasurol heddiw yn cBPF.

Mae eBPF yn ymestyn y peiriant rhithwir BPF clasurol mewn sawl ffordd:

  • Yn seiliedig ar bensaernïaeth 64-bit modern. Mae eBPF yn defnyddio cofrestrau 64-bit ac yn cynyddu nifer y cofrestrau sydd ar gael o 2 (cronadur ac X) i 10. Mae eBPF hefyd yn darparu codau op ychwanegol (BPF_MOV, BPF_JNE, BPF_CALL...).
  • Ar wahân i is-system haen y rhwydwaith. Roedd BPF ynghlwm wrth y model data swp. Ers iddo gael ei ddefnyddio ar gyfer hidlo pecynnau, roedd ei god wedi'i leoli yn yr is-system sy'n darparu cyfathrebiadau rhwydwaith. Fodd bynnag, nid yw'r peiriant rhithwir eBPF bellach yn gysylltiedig â'r model data a gellir ei ddefnyddio at unrhyw ddiben. Felly, nawr gellir cysylltu'r rhaglen eBPF â tracepoint neu kprobe. Mae hyn yn agor y ffordd i offeryniaeth eBPF, dadansoddi perfformiad, a llawer o achosion defnydd eraill yng nghyd-destun is-systemau cnewyllyn eraill. Nawr mae'r cod eBPF wedi'i leoli yn ei lwybr ei hun: cnewyllyn / bpf.
  • Storfeydd data byd-eang o'r enw Maps. Mae mapiau yn storfeydd gwerth allweddol sy'n galluogi cyfnewid data rhwng gofod defnyddwyr a gofod cnewyllyn. Mae eBPF yn darparu sawl math o fapiau.
  • Swyddogaethau eilaidd. Yn benodol, i ailysgrifennu pecyn, cyfrifwch siec, neu glonio pecyn. Mae'r swyddogaethau hyn yn rhedeg y tu mewn i'r cnewyllyn ac nid ydynt yn rhaglenni gofod defnyddiwr. Gallwch hefyd wneud galwadau system o raglenni eBPF.
  • Gorffen galwadau. Mae maint y rhaglen yn eBPF wedi'i gyfyngu i 4096 beit. Mae'r nodwedd galw cynffon yn caniatáu i raglen eBPF drosglwyddo rheolaeth i raglen eBPF newydd a thrwy hynny osgoi'r cyfyngiad hwn (gellir cysylltu hyd at 32 o raglenni fel hyn).

eBPF: enghraifft

Mae yna sawl enghraifft ar gyfer eBPF yn y ffynonellau cnewyllyn Linux. Maent ar gael mewn samplau / bpf /. I lunio'r enghreifftiau hyn, rhowch:

$ sudo make samples/bpf/

Ni fyddaf yn ysgrifennu enghraifft newydd ar gyfer eBPF fy hun, ond byddaf yn defnyddio un o'r samplau sydd ar gael mewn samplau/bpf/. Edrychaf ar rai rhannau o'r cod ac egluro sut mae'n gweithio. Fel enghraifft, dewisais y rhaglen tracex4.

Yn gyffredinol, mae pob un o'r enghreifftiau mewn samplau/bpf/ yn cynnwys dwy ffeil. Yn yr achos hwn:

  • tracex4_kern.c, yn cynnwys y cod ffynhonnell i'w weithredu yn y cnewyllyn fel bytecode eBPF.
  • tracex4_user.c, yn cynnwys rhaglen o ofod defnyddiwr.

Yn yr achos hwn, mae angen inni lunio tracex4_kern.c i god byte eBPF. Ar hyn o bryd yn gcc nid oes unrhyw gefn ar gyfer eBPF. Yn ffodus, clang yn gallu allbwn bytecode eBPF. Makefile defnyddiau clang ar gyfer llunio tracex4_kern.c i'r ffeil gwrthrych.

Soniais uchod mai un o nodweddion mwyaf diddorol eBPF yw mapiau. Mae tracex4_kern yn diffinio un map:

struct pair {
    u64 val;
    u64 ip;
};  

struct bpf_map_def SEC("maps") my_map = {
    .type = BPF_MAP_TYPE_HASH,
    .key_size = sizeof(long),
    .value_size = sizeof(struct pair),
    .max_entries = 1000000,
};

BPF_MAP_TYPE_HASH yw un o'r nifer o fathau o gardiau a gynigir gan eBPF. Yn yr achos hwn, dim ond hash ydyw. Efallai eich bod hefyd wedi sylwi ar hysbyseb SEC("maps"). Mae SEC yn facro a ddefnyddir i greu adran newydd o ffeil ddeuaidd. Mewn gwirionedd, yn yr enghraifft tracex4_kern Diffinnir dwy adran arall:

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;
}   

Mae'r ddwy swyddogaeth hyn yn eich galluogi i ddileu cofnod o'r map (kprobe/kmem_cache_free) ac ychwanegu cofnod newydd at y map (kretprobe/kmem_cache_alloc_node). Mae pob enw ffwythiant a ysgrifennir mewn prif lythrennau yn cyfateb i'r macros a ddiffinnir yn bpf_helpers.h.

Os byddaf yn dympio'r adrannau o'r ffeil gwrthrych, dylwn weld bod yr adrannau newydd hyn eisoes wedi'u diffinio:

$ objdump -h tracex4_kern.o

tracex4_kern.o: file format elf64-little

Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000000 0000000000000000 0000000000000000 00000040 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 kprobe/kmem_cache_free 00000048 0000000000000000 0000000000000000 00000040 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 kretprobe/kmem_cache_alloc_node 000000c0 0000000000000000 0000000000000000 00000088 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 maps 0000001c 0000000000000000 0000000000000000 00000148 2**2
CONTENTS, ALLOC, LOAD, DATA
4 license 00000004 0000000000000000 0000000000000000 00000164 2**0
CONTENTS, ALLOC, LOAD, DATA
5 version 00000004 0000000000000000 0000000000000000 00000168 2**2
CONTENTS, ALLOC, LOAD, DATA
6 .eh_frame 00000050 0000000000000000 0000000000000000 00000170 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

Mae yna hefyd tracex4_user.c, prif raglen. Yn y bôn, mae'r rhaglen hon yn gwrando ar ddigwyddiadau kmem_cache_alloc_node. Pan fydd digwyddiad o'r fath yn digwydd, gweithredir y cod eBPF cyfatebol. Mae'r cod yn cadw priodoledd IP y gwrthrych i mewn i fap, ac yna caiff y gwrthrych ei ddolennu trwy'r brif raglen. Enghraifft:

$ 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

Sut mae rhaglen ofod defnyddwyr a rhaglen eBPF yn gysylltiedig? Ar gychwyn tracex4_user.c yn llwytho ffeil gwrthrych tracex4_kern.o defnyddio'r swyddogaeth 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;
}

Wrth wneud load_bpf_file ychwanegir at chwilwyr a ddiffinnir yn y ffeil eBPF /sys/kernel/debug/tracing/kprobe_events. Nawr rydym yn gwrando am y digwyddiadau hyn a gall ein rhaglen wneud rhywbeth pan fyddant yn digwydd.

$ sudo cat /sys/kernel/debug/tracing/kprobe_events
p:kprobes/kmem_cache_free kmem_cache_free
r:kprobes/kmem_cache_alloc_node kmem_cache_alloc_node

Mae'r holl raglenni eraill yn sampl/bpf/ wedi'u strwythuro'n debyg. Maent bob amser yn cynnwys dwy ffeil:

  • XXX_kern.c: rhaglen eBPF.
  • XXX_user.c: prif raglen.

Mae'r rhaglen eBPF yn nodi mapiau a swyddogaethau sy'n gysylltiedig ag adran. Pan fydd y cnewyllyn yn cyhoeddi digwyddiad o fath penodol (er enghraifft, tracepoint), mae'r swyddogaethau rhwymedig yn cael eu cyflawni. Mae'r cardiau yn darparu cyfathrebu rhwng y rhaglen cnewyllyn a'r rhaglen gofod defnyddiwr.

Casgliad

Roedd yr erthygl hon yn trafod BPF ac eBPF yn gyffredinol. Gwn fod llawer o wybodaeth ac adnoddau am eBPF heddiw, felly byddaf yn argymell ychydig mwy o adnoddau ar gyfer astudiaeth bellach

Rwy'n argymell darllen:

Ffynhonnell: hab.com

Ychwanegu sylw