Муқаддимаи мухтасар ба BPF ва eBPF

Салом, Хабр! Ба маълумоти шумо мерасонем, ки мо китоберо барои нашр омода карда истодаем».Мушоҳидаи Linux бо BPF".

Муқаддимаи мухтасар ба BPF ва eBPF
Азбаски мошини виртуалии BPF таҳаввулро идома медиҳад ва дар амал фаъолона истифода мешавад, мо барои шумо мақолаеро тарҷума кардем, ки қобилиятҳои асосӣ ва ҳолати кунунии онро тавсиф мекунад.

Дар солҳои охир, асбобҳо ва усулҳои барномасозӣ барои ҷуброн кардани маҳдудиятҳои ядрои Linux дар ҳолатҳое, ки коркарди бастаҳои баландсифат лозим аст, бештар маъмул гаштанд. Яке аз маъмултарин техникаи ин гуна номида мешавад гузариш ядро (байпаси ядро) ва имкон медиҳад, ки қабати шабакаи ядроиро гузар карда, тамоми коркарди бастаҳоро аз фазои корбар иҷро кунад. Гузаштан аз ядро ​​инчунин назорати корти шабакаро дар бар мегирад фазои корбар. Ба ибораи дигар, ҳангоми кор бо корти шабакавӣ мо ба ронанда такя мекунем фазои корбар.

Бо интиқол додани назорати пурраи корти шабака ба барномаи фазоии корбар, мо сарбории ядроро (комментарии контекст, коркарди қабати шабака, қатъҳо ва ғайра) кам мекунем, ки ҳангоми кор бо суръати 10 Гб/с ё бештар аз он хеле муҳим аст. Байпаси ядро ​​​​ва маҷмӯи дигар хусусиятҳо (коркарди партия) ва танзими дақиқи иҷроиш (Баҳисобгирии муҳосибӣ NUMA, Изолятсияи CPU, ва ғайра) ба асосҳои коркарди сермахсули шабака дар фазои корбар мувофиқат мекунанд. Шояд мисоли намунавии ин равиши нав ба коркарди бастаҳо бошад DPDK аз Intel (Маҷмӯаи таҳияи ҳавопаймои маълумот), гарчанде ки дигар асбобҳо ва усулҳои маъруф мавҷуданд, аз ҷумла VPP Cisco (Vector Packet Processing), Netmap ва, албатта, ҷабрдида.

Ташкили ҳамкории шабакавӣ дар фазои корбар як қатор камбудиҳо дорад:

  • Ядрои ОС як қабати абстраксия барои захираҳои сахтафзор мебошад. Азбаски барномаҳои фазоии корбар бояд захираҳои худро мустақиман идора кунанд, онҳо инчунин бояд сахтафзори худро идора кунанд. Ин аксар вақт маънои онро дорад, ки драйверҳои шахсии худро барномарезӣ кунед.
  • Азбаски мо аз фазои ядро ​​​​комилан даст мекашем, мо инчунин аз тамоми функсияҳои шабакавӣ, ки ядро ​​пешниҳод мекунад, даст мекашем. Барномаҳои фазои корбар бояд хусусиятҳоеро, ки аллакай аз ҷониби ядро ​​ё системаи оператсионӣ таъмин карда шудаанд, аз нав татбиқ кунанд.
  • Барномаҳо дар реҷаи қум кор мекунанд, ки таъсири мутақобилаи онҳоро ба таври ҷиддӣ маҳдуд мекунад ва онҳоро аз ҳамгироӣ бо қисмҳои дигари системаи оператсионӣ пешгирӣ мекунад.

Аслан, ҳангоми пайваст кардани шабака дар фазои корбар, ба дастовардҳои самаранокӣ тавассути интиқоли коркарди пакетҳо аз ядро ​​ба фазои корбар ба даст оварда мешавад. XDP комилан баръакс мекунад: он барномаҳои шабакавиро аз фазои корбар (филтрҳо, ҳалкунандаҳо, масир ва ғ.) ба фазои ядро ​​​​мебарад. XDP ба мо имкон медиҳад, ки вазифаи шабакаро дарҳол иҷро кунем, ки баста ба интерфейси шабака ворид шавад ва пеш аз он ки он ба зерсистемаи шабакаи ядро ​​​​гузарад. Дар натиҷа, суръати коркарди бастаҳо хеле меафзояд. Аммо, чӣ гуна ядро ​​ба корбар имкон медиҳад, ки барномаҳои худро дар фазои ядро ​​​​иҷро кунад? Пеш аз посух додан ба ин савол, биёед бубинем, ки BPF чист.

BPF ва eBPF

Сарфи назар аз номи печида, BPF (Filtering Packet Berkeley) воқеан як модели мошини виртуалӣ аст. Ин мошини маҷозӣ дар ибтидо барои коркарди филтркунии бастаҳо тарҳрезӣ шуда буд, аз ин рӯ ном дорад.

Яке аз воситаҳои машҳури истифодаи BPF мебошад tcpdump. Ҳангоми гирифтани пакетҳо бо истифода аз tcpdump корбар метавонад ифодаеро барои филтр кардани бастаҳо муайян кунад. Танҳо бастаҳое, ки ба ин ифода мувофиқанд, гирифта мешаванд. Масалан, ибораи «tcp dst port 80” ба ҳамаи бастаҳои TCP, ки ба порти 80 меоянд, ишора мекунад. Компилятор метавонад ин ифодаро тавассути табдил додани он ба байткоди 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

Ин аст он чизе ки барномаи дар боло зикршуда асосан иҷро мекунад:

  • Дастур (000): Бастаро дар офсети 12 ҳамчун калимаи 16-битӣ ба аккумулятор бор мекунад. Офсети 12 ба ethertype пакет мувофиқат мекунад.
  • Дастур (001): арзиши дар аккумулятор бо 0x86dd, яъне бо арзиши ethertype барои IPv6 муқоиса мекунад. Агар натиҷа дуруст бошад, пас ҳисобкунаки барнома ба дастур (002) ва агар не, ба (006) мегузарад.
  • Дастур (006): арзишро бо 0x800 муқоиса мекунад (қимати ethertype барои IPv4). Агар ҷавоб дуруст бошад, пас барнома ба (007), агар не, пас ба (015) мегузарад.

Ва ғайра то он даме, ки барномаи филтркунии баста натиҷаро баргардонад. Ин одатан булӣ аст. Баргардонидани арзиши ғайрисифр (дастур (014)) маънои қабули бастаро дорад ва баргардонидани арзиши сифр (дастур (015)) маънои онро дорад, ки баста қабул нашудааст.

Мошини виртуалии BPF ва байткоди он аз ҷониби Стив МакКенн ва Ван Ҷейкобсон дар охири соли 1992 ҳангоми нашри ҳуҷҷати онҳо пешниҳод карда шуда буданд. Филтри бастаи BSD: Архитектураи нав барои гирифтани пакети сатҳи корбар, ин технология бори аввал дар конфронси Usenix дар зимистони соли 1993 муаррифӣ шуд.

Азбаски BPF мошини маҷозӣ аст, он муҳитеро, ки дар он барномаҳо кор мекунанд, муайян мекунад. Илова ба байткод, он инчунин модели хотираи партияро муайян мекунад (дастурҳои сарборӣ ба таври ғайримустақим ба партия татбиқ карда мешаванд), регистрҳо (A ва X; регистрҳои аккумулятор ва индекс), нигаҳдории хотираи скретч ва ҳисобкунаки барномавӣ. Ҷолиб он аст, ки байткоди BPF пас аз Motorola 6502 ISA модел шудааст. Чунон ки Стив МакКенн дар худ ба хотир овард маърузаи пленум дар Sharkfest '11, вай бо сохти 6502 аз замони барномасозии мактаби миёна дар Apple II шинос буд ва ин дониш ба кори ӯ дар тарҳрезии байткоди BPF таъсир расонд.

Дастгирии BPF дар ядрои Linux дар версияҳои v2.5 ва болотар амалӣ карда мешавад, ки асосан бо кӯшишҳои Ҷей Шуллис илова карда шудааст. Рамзи BPF то соли 2011 бетағйир монд, вақте ки Эрик Дюмасет тарҷумони BPF-ро барои кор дар реҷаи JIT аз нав тарҳрезӣ кард (Манбаъ: JIT барои филтрҳои пакетӣ). Пас аз ин, ядро ​​​​ба ҷои тафсири байткоди BPF, метавонад барномаҳои BPF-ро мустақиман ба меъмории ҳадаф табдил диҳад: x86, ARM, MIPS ва ғайра.

Баъдтар, дар соли 2014, Алексей Старовойтов механизми нави JIT-ро барои BPF пешниҳод кард. Дар асл, ин JIT нав ба меъмории нави BPF асос ёфтааст ва eBPF номида шуд. Ман фикр мекунам, ки ҳарду VM чанд вақт якҷоя зиндагӣ мекарданд, аммо дар айни замон филтркунии бастаҳо дар асоси eBPF амалӣ карда мешавад. Дар асл, дар бисёр мисолҳои ҳуҷҷатҳои муосир, BPF eBPF аст ва BPF классикӣ имрӯз ҳамчун cBPF маълум аст.

eBPF мошини виртуалии классикии BPF-ро бо чанд роҳ васеъ мекунад:

  • Дар асоси меъмории муосири 64-бит. eBPF регистрҳои 64-битаро истифода мебарад ва шумораи регистрҳои дастрасро аз 2 (аккумулятор ва X) то 10 афзоиш медиҳад. eBPF инчунин opcodes иловагиро таъмин мекунад (BPF_MOV, BPF_JNE, BPF_CALL...).
  • Аз зерсистемаи қабати шабака ҷудо карда шудааст. BPF ба модели маълумоти партия пайваст карда шуд. Азбаски он барои филтркунии пакетҳо истифода мешуд, рамзи он дар зерсистемае ҷойгир буд, ки алоқаи шабакавиро таъмин мекунад. Бо вуҷуди ин, мошини виртуалии eBPF дигар ба модели додаҳо вобаста нест ва онро барои ҳама гуна мақсад истифода бурдан мумкин аст. Ҳамин тавр, ҳоло барномаи eBPF метавонад ба tracepoint ё kprobe пайваст карда шавад. Ин роҳро ба асбобҳои eBPF, таҳлили иҷроиш ва бисёр ҳолатҳои дигари истифода дар заминаи зерсистемаҳои дигари ядро ​​​​кушода мекунад. Акнун рамзи eBPF дар роҳи худ ҷойгир аст: ядро/bpf.
  • Мағозаҳои глобалии маълумот бо номи Харитаҳо. Харитаҳо мағозаҳои калидӣ мебошанд, ки мубодилаи маълумотро байни фазои корбар ва фазои ядро ​​имкон медиҳанд. eBPF якчанд намуди харитаҳоро пешниҳод мекунад.
  • Функсияҳои дуюмдараҷа. Аз ҷумла, барои аз нав навиштани баста, ҳисоб кардани маблағи чек ё клон кардани баста. Ин функсияҳо дар дохили ядро ​​​​кор мекунанд ва барномаҳои фазоии корбар нестанд. Шумо инчунин метавонед аз барномаҳои eBPF зангҳои системавӣ кунед.
  • Хотимаи зангҳо. Андозаи барнома дар eBPF бо 4096 байт маҳдуд аст. Хусусияти занги дум ба барномаи eBPF имкон медиҳад, ки назоратро ба барномаи нави eBPF интиқол диҳад ва ба ин васила ин маҳдудиятро гузарад (то 32 барномаро бо ин роҳ пайваст кардан мумкин аст).

eBPF: мисол

Дар сарчашмаҳои ядрои Linux якчанд мисолҳо барои eBPF мавҷуданд. Онҳо дар намунаҳо/bpf/ дастрасанд. Барои тартиб додани ин мисолҳо, танҳо ворид кунед:

$ sudo make samples/bpf/

Ман худам барои eBPF мисоли нав наменависам, аммо яке аз намунаҳои дар намунаҳо/bpf/ мавҷудбударо истифода хоҳам кард. Ман баъзе қисмҳои кодро дида мебароям ва мефаҳмонам, ки он чӣ гуна кор мекунад. Масалан, ман барномаро интихоб кардам tracex4.

Умуман, ҳар як намунаи намунаҳо/bpf/ аз ду файл иборат аст. Дар ин маврид:

  • tracex4_kern.c, дорои рамзи сарчашмаест, ки бояд дар ядро ​​ҳамчун байткоди eBPF иҷро карда шавад.
  • tracex4_user.c, дорои як барнома аз фазои корбар аст.

Дар ин ҳолат, мо бояд ҷамъоварӣ кунем tracex4_kern.c ба байткоди eBPF. Айни замон дар gcc барои eBPF пуштибонӣ вуҷуд надорад. Хушбахтона, clang байткоди eBPF-ро бароварда метавонад. Makefile истифода мебарад clang барои тартиб додан tracex4_kern.c ба файли объект.

Ман дар боло зикр кардам, ки яке аз хусусиятҳои ҷолибтарини eBPF харитаҳо мебошанд. tracex4_kern як харитаро муайян мекунад:

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 яке аз намудҳои зиёди кортҳои eBPF мебошад. Дар ин ҳолат, он танҳо як ҳаш аст. Шумо инчунин шояд таблиғро мушоҳида карда бошед SEC("maps"). SEC макросест, ки барои сохтани бахши нави файли дуӣ истифода мешавад. Дар асл, дар мисол tracex4_kern боз ду бахш муайян шудааст:

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

Ин ду функсия ба шумо имкон медиҳанд, ки сабтро аз харита тоза кунед (kprobe/kmem_cache_free) ва ба харита сабти нав илова кунед (kretprobe/kmem_cache_alloc_node). Ҳама номҳои функсияҳои бо ҳарфҳои калон навишташуда ба макросҳои дар bpf_helpers.h.

Агар ман қисмҳои файли объектро партоям, ман бояд бубинам, ки ин қисмҳои нав аллакай муайян шудаанд:

$ 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

ҳол доранд tracex4_user.c, программам асосй. Асосан, ин барнома воқеаҳоро мешунавад kmem_cache_alloc_node. Вақте ки чунин ҳодиса рух медиҳад, рамзи мувофиқи eBPF иҷро карда мешавад. Рамз аттрибути IP-и объектро дар харита захира мекунад ва баъд объект тавассути барномаи асосӣ ҳал карда мешавад. Мисол:

$ 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

Барномаи фазои корбар ва барномаи eBPF чӣ гуна алоқаманд аст? Дар бораи оғозёбӣ tracex4_user.c файли объектро бор мекунад tracex4_kern.o бо истифода аз функсия 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;
}

Бо амалй load_bpf_file зондҳои дар файли eBPF муайяншуда илова карда мешаванд /sys/kernel/debug/tracing/kprobe_events. Ҳоло мо ин рӯйдодҳоро гӯш мекунем ва барномаи мо ҳангоми рух додани онҳо коре карда метавонад.

$ 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

Ҳама барномаҳои дигар дар sample/bpf/ ҳамин тавр сохта шудаанд. Онҳо ҳамеша ду файлро дар бар мегиранд:

  • XXX_kern.c: Барномаи eBPF.
  • XXX_user.c: барномаи асосй.

Барномаи eBPF харитаҳо ва вазифаҳои марбут ба бахшро муайян мекунад. Вақте ки ядро ​​​​ҳодисаи як намуди муайянро медиҳад (масалан, tracepoint), вазифаҳои басташуда иҷро мешаванд. Кортҳо алоқаи байни барномаи ядро ​​​​ва барномаи фазои корбарро таъмин мекунанд.

хулоса

Ин мақола BPF ва eBPF-ро ба таври умумӣ баррасӣ кардааст. Ман медонам, ки имрӯз дар бораи eBPF маълумот ва захираҳои зиёде мавҷуданд, аз ин рӯ ман барои омӯзиши минбаъда чанд захираи дигарро тавсия медиҳам

Ман тавсия медиҳам хондан:

Манбаъ: will.com

Илова Эзоҳ