BPF और eBPF का संक्षिप्त परिचय

हेलो, हेब्र! हम आपको सूचित करते हैं कि हम " बीपीएफ के साथ लिनक्स अवलोकन " पुस्तक को जारी करने की तैयारी कर रहे हैं


जैसा कि BPF वर्चुअल मशीन विकसित करना जारी है और व्यवहार में सक्रिय रूप से लागू है, हमने आपके लिए एक लेख का अनुवाद किया है जो इसकी मुख्य विशेषताओं और वर्तमान स्थिति का वर्णन करता है।

हाल के वर्षों में, प्रोग्रामिंग टूल और तकनीकों ने लोकप्रियता हासिल करना शुरू कर दिया है, उन मामलों में लिनक्स कर्नेल की सीमाओं की भरपाई के लिए डिज़ाइन किया गया है जहां उच्च-प्रदर्शन पैकेज प्रसंस्करण की आवश्यकता होती है। इस तरह के सबसे लोकप्रिय तरीकों में से एक को कर्नेल (कर्नेल बाईपास) को दरकिनार कहा जाता है और नेटवर्क परत कोर को पारित करने देता है जो उपयोगकर्ता अंतरिक्ष से सभी पैकेट प्रसंस्करण को निष्पादित करता है। कर्नेल को दरकिनार करने में उपयोगकर्ता स्थान से नेटवर्क कार्ड का प्रबंधन भी शामिल है । दूसरे शब्दों में, जब नेटवर्क कार्ड के साथ काम करते हैं, तो हम उपयोगकर्ता अंतरिक्ष चालक पर भरोसा करते हैं

उपयोगकर्ता स्थान से प्रोग्राम पर नेटवर्क कार्ड पर पूर्ण नियंत्रण स्थानांतरित करके, हम कर्नेल (स्विचिंग संदर्भ, नेटवर्क स्तर, इंटरट्रप्ट, आदि) से जुड़ी लागत को कम करते हैं, जो कि 10Gb / s या अधिक की गति से काम करते समय काफी महत्वपूर्ण है। कर्नेल बाईपास प्लस अन्य सुविधाओं ( बैच प्रोसेसिंग ) और सटीक प्रदर्शन ट्यूनिंग ( NUMA लेखांकन , सीपीयू अलगाव , आदि) का एक संयोजन उपयोगकर्ता अंतरिक्ष में उच्च प्रदर्शन नेटवर्क प्रसंस्करण की मूल बातें के अनुरूप है। शायद पैकेज प्रोसेसिंग के लिए इस नए दृष्टिकोण का एक मॉडल उदाहरण इंटेल का DPDK ( डेटा प्लेन डेवलपमेंट किट) है), हालांकि सिस्को (वेक्टर पैकेट प्रोसेसिंग), नेटमैप और निश्चित रूप से, स्नैब से वीपीपी सहित अन्य प्रसिद्ध उपकरण और तकनीकें हैं

उपयोगकर्ता स्थान में नेटवर्क इंटरैक्शन के संगठन में कई नुकसान हैं:

  • ओएस का कर्नेल हार्डवेयर संसाधनों के लिए अमूर्तता का स्तर है। क्योंकि उपयोगकर्ता-अंतरिक्ष कार्यक्रमों को सीधे अपने संसाधनों का प्रबंधन करना पड़ता है, उन्हें अपने स्वयं के हार्डवेयर का प्रबंधन भी करना पड़ता है। इसका अक्सर अर्थ होता है कि आपको अपने स्वयं के ड्राइवरों को प्रोग्राम करने की आवश्यकता होती है।
  • , , . , , , .
  • , .

संक्षेप में, जब उपयोगकर्ता अंतरिक्ष में नेटवर्क इंटरैक्शन का आयोजन करते हैं, तो कर्नेल से उपयोगकर्ता स्थान पर पैकेट प्रसंस्करण को स्थानांतरित करके उत्पादकता प्राप्त की जाती है। XDP बिल्कुल विपरीत करता है: उपयोगकर्ता प्रोग्राम (फ़िल्टर, कन्वर्टर्स, राउटिंग, आदि) से नेटवर्क प्रोग्राम को कर्नेल क्षेत्र में ले जाता है। जैसे ही पैकेट नेटवर्क इंटरफ़ेस पर आता है और कर्नेल के नेटवर्क सबसिस्टम में जाने से पहले XDP हमें नेटवर्क फ़ंक्शन करने की अनुमति देता है। नतीजतन, पैकेट प्रसंस्करण गति काफी बढ़ जाती है। हालांकि, कर्नेल उपयोगकर्ता को कर्नेल स्थान में अपने कार्यक्रमों को निष्पादित करने की अनुमति कैसे देता है? इस प्रश्न का उत्तर देने से पहले, आइए देखें कि बीपीएफ क्या है।

BPF और EBPF

बीपीएफ (पैकेट फ़िल्टरिंग, बर्कले) के तथाकथित-स्पष्ट नाम के बावजूद, यह वास्तव में, एक वर्चुअल मशीन मॉडल है। यह वर्चुअल मशीन मूल रूप से पैकेट फ़िल्टरिंग को संभालने के लिए डिज़ाइन की गई थी, इसलिए यह नाम है।

BPF का उपयोग करने वाले सबसे अच्छे ज्ञात उपकरणों में से एक है tcpdumpपैकेट को कैप्चर करते समय, tcpdumpउपयोगकर्ता पैकेट फ़िल्टर करने के लिए एक अभिव्यक्ति निर्दिष्ट कर सकता है। केवल इस अभिव्यक्ति से मेल खाने वाले पैकेट कैप्चर किए जाएंगे। उदाहरण के लिए, अभिव्यक्ति " tcp dst port 80" पोर्ट पर पहुंचने वाले सभी टीसीपी पैकेट पर लागू होती है। कंपाइलर इस अभिव्यक्ति को बीपीएफ बायोटेक में परिवर्तित करके छोटा कर सकता है। यहाँ उपरोक्त कार्यक्रम मूल रूप से क्या है:

$ 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): बैटरी में 16-बिट शब्द के रूप में ऑफसेट 12 पर एक पैकेट डाउनलोड करता है। 12 की एक ऑफसेट ईथरटाइप पैकेट से मेल खाती है।
  • (001): 0x86dd, , ethertype- IPv6. true, (002), – (006).
  • (006): 0x800 (ethertype- IPv4). true, (007), – (015).

और इसी तरह, जब तक पैकेट फ़िल्टरिंग कार्यक्रम एक परिणाम नहीं देता। यह आमतौर पर एक बुलियन है। एक गैर-शून्य मान (निर्देश (014)) वापस करने का अर्थ है कि पैकेट से संपर्क किया गया है, और शून्य (निर्देश (015)) लौटने का मतलब है कि पैकेट नहीं आया है।

BPF वर्चुअल मशीन और इसके बाइटकोड को 1992 के उत्तरार्ध में स्टीव मैक्कैन और वैन जैकबसन द्वारा प्रस्तावित किया गया था जब उनके लेख BSD पैकेट फ़िल्टर: यूजर लेवल पर पैकेट कैप्चर के लिए एक नया आर्किटेक्चर , पहली बार 1993 की सर्दियों में एक यूज़निक्स सम्मेलन में पेश किया गया था।

क्योंकि BPF एक वर्चुअल मशीन है, यह पर्यावरण को परिभाषित करता है जिसमें प्रोग्राम चलते हैं। बाइटेकोड के अलावा, यह एक बैच मेमोरी मॉडल को भी परिभाषित करता है (बूट निर्देश अंतर्निहित पैकेज पर लागू होते हैं), रजिस्टर (ए और एक्स; बैटरी और इंडेक्स रजिस्टर), मेमोरी मेमोरी और एक अंतर्निहित प्रोग्राम काउंटर। दिलचस्प है, मोटोरोला 6502 आईएसए के बाद बीपीएफ बायटेकोड को मॉडल किया गया था। जैसा कि स्टीव मैककेन ने शार्कफेस्ट '11 में अपनी प्लेनरी बात में याद करते हुए कहा, वह हाईस्कूल के बाद से 6502 के निर्माण से परिचित थे जब उन्होंने Apple II में प्रोग्राम किया था, और इस ज्ञान ने BPF बायोटेक को डिजाइन करने के उनके काम को प्रभावित किया।

बीपीएफ के लिए समर्थन लिनक्स कर्नेल में संस्करण v2.5 और उच्चतर में लागू किया गया है, मुख्य रूप से जे स्कुलर के प्रयासों द्वारा जोड़ा गया है। 2011 तक बीपीएफ कोड अपरिवर्तित रहा, जब एरिक डुमाज़ेट ने बीपीएफ दुभाषिया को जेआईटी मोड ( पैकेट: फिल्टर के लिए जेआईटी ) में काम करने के लिए रिड्यूस किया । उसके बाद, कर्नेल, BPF बाइट कोड की व्याख्या करने के बजाय, BPF प्रोग्राम को सीधे लक्ष्य वास्तुकला में बदल सकता है: x86, ARM, MIPS, आदि।

बाद में, 2014 में, एलेक्सी स्टारोवितोव ने BPF के लिए एक नया JIT तंत्र प्रस्तावित किया। वास्तव में, यह नया JIT नई BPF आधारित वास्तुकला बन गया है और इसे eBPF कहा जाता है। मुझे लगता है कि कुछ समय के लिए दोनों वर्चुअल मशीनें सह-अस्तित्व में हैं, लेकिन वर्तमान में पैकेट फ़िल्टरिंग को EBPF के आधार पर लागू किया गया है। वास्तव में, आधुनिक दस्तावेज़ीकरण के कई उदाहरणों में, बीपीएफ का अर्थ है कि ईपीपीएफ और शास्त्रीय बीपीएफ को अब सीबीपीएफ के रूप में जाना जाता है।

EBPF क्लासिक BPF वर्चुअल मशीन को कई तरीकों से बढ़ाता है:

  • आधुनिक 64-बिट आर्किटेक्चर के आधार पर। eBPF 64-बिट रजिस्टर का उपयोग करता है और 2 (बैटरी और X) से उपलब्ध रजिस्टरों की संख्या को बढ़ाता है। 10. eBPF अतिरिक्त ऑपरेशन कोड (BPF_MOV, BPF_JNE, BPF_CALL ...) भी प्रदान करता है।
  • . BPF . , , . , eBPF . , eBPF tracepoint kprobe. eBPF, . eBPF : kernel/bpf.
  • . – «-», . eBPF .
  • . , , . . , eBPF .
  • . eBPF 4096 . eBPF eBPF- ( 32 ).

eBPF: एक उदाहरण

लिनक्स कर्नेल स्रोतों में eBPF के लिए कई उदाहरण हैं। वे नमूने / बीपीएफ / पर उपलब्ध हैं। इन उदाहरणों को संकलित करने के लिए, बस दर्ज करें:

$ sudo make samples/bpf/

मैं खुद ईपीपीएफ के लिए एक नया उदाहरण नहीं लिखूंगा, लेकिन नमूनों / बीपीएफ / में उपलब्ध नमूनों में से एक का उपयोग करूंगा। मैं कोड के कुछ अनुभागों की समीक्षा करूंगा और बताऊंगा कि यह कैसे काम करता है। एक उदाहरण के रूप में, मैंने एक कार्यक्रम चुना tracex4

सामान्य तौर पर, नमूनों में प्रत्येक उदाहरण / bpf / में दो फाइलें होती हैं। इस मामले में:

  • tracex4_kern.c, उस स्रोत कोड को शामिल करता है, जिसे कर्नेल में eBPF बाइटकोड के रूप में निष्पादित किया जाना चाहिए।
  • tracex4_user.c, उपयोगकर्ता अंतरिक्ष से एक कार्यक्रम शामिल हैं।

इस मामले में, हमें tracex4_kern.ceBPF को बायटेकोड में संकलित करने की आवश्यकता है। वर्तमान में gcceBPF के लिए कोई सर्वर हिस्सा नहीं है। सौभाग्य से, यह clangeBPF बायटेकोड जारी कर सकता है। ऑब्जेक्ट फ़ाइल संकलित करने के लिए Makefileउपयोग करता clangहै tracex4_kern.c

मैंने ऊपर उल्लेख किया है कि eBPFs की सबसे दिलचस्प विशेषताओं में से एक कार्ड है। ट्रेस xx_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

यदि मैं ऑब्जेक्ट फ़ाइल के वर्गों का एक डंप आउटपुट करता हूं, तो मुझे यह देखना चाहिए कि ये नए अनुभाग पहले से ही परिभाषित हैं: फिर भी , मुख्य कार्यक्रम। मूल रूप से, यह कार्यक्रम घटनाओं को सुनता है । जब ऐसी कोई घटना होती है, तो संबंधित ईजीपीएफ कोड निष्पादित किया जाता है। कोड मानचित्र में ऑब्जेक्ट की आईपी विशेषता को संग्रहीत करता है, और फिर इस कार्यक्रम को मुख्य कार्यक्रम में चक्रीय रूप से प्रदर्शित किया जाता है। उदाहरण: उपयोगकर्ता अंतरिक्ष कार्यक्रम और eBPF कार्यक्रम कैसे संबंधित हैं? प्रारंभ में, यह एक फ़ंक्शन का उपयोग करके एक ऑब्जेक्ट फ़ाइल को लोड करता है

$ 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.ckmem_cache_alloc_node

$ 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


tracex4_user.ctracex4_kern.oload_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किया जाता है, तो ईबीपीएफ फ़ाइल में परिभाषित जांच को जोड़ा जाता है /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




  • XXX_kern.c: EBPF कार्यक्रम।
  • XXX_user.c: मुख्य कार्यक्रम।

EBPF प्रोग्राम कार्ड और फ़ंक्शंस से जुड़े कार्यों को परिभाषित करता है। जब कर्नेल एक निश्चित प्रकार (उदाहरण के लिए tracepoint) की एक घटना को फेंकता है , तो बाध्य फ़ंक्शन निष्पादित होते हैं। मैप कर्नेल प्रोग्राम और यूजर स्पेस प्रोग्राम के बीच डेटा विनिमय प्रदान करते हैं।

निष्कर्ष

यह लेख BPF और eBPF को रेखांकित करता है। मुझे पता है कि आज ईएक्सपीएफ के बारे में बहुत सारी जानकारी और संसाधन हैं, इसलिए मैं आगे के अध्ययन के लिए कुछ और सामग्रियों

की सलाह देता हूं। मैं पढ़ने की सलाह देता हूं :


All Articles