官术网_书友最值得收藏!

2.1.2 eBPF指令集

cBPF使用32位的經(jīng)典BPF虛擬機(jī),包含了限定的指令集,而eBPF則使用64位的eBPF虛擬機(jī),擁有更多的寄存器和指令,支持更豐富的操作和功能。BPF(默認(rèn)指eBPF非cBPF)程序指令都是64位,使用了11個(gè)64位寄存器和一個(gè)程序計(jì)數(shù)器,以及一個(gè)大小為512字節(jié)的BPF棧。

1.寄存器和調(diào)用規(guī)約

eBPF有10個(gè)通用寄存器和一個(gè)只讀的fp(frame pointer,幀指針)寄存器,它們都是64位。eBPF調(diào)用規(guī)約如下。

1)R0:保存函數(shù)調(diào)用的返回值,以及eBPF程序退出值。

2)R1~R5:函數(shù)調(diào)用入?yún)ⅰ?/p>

3)R6~R9:調(diào)用者保存寄存器。

4)R10:只讀的,棧幀寄存器。

2.指令編碼

eBPF有兩類(lèi)指令編碼:基礎(chǔ)指令編碼和寬指令編碼。

1)基礎(chǔ)指令編碼:?jiǎn)螚l指令長(zhǎng)度為64位。指令構(gòu)成如表2-3所示。

表2-3 eBPF基礎(chǔ)指令編碼

說(shuō)明:

?操作碼:指令的具體操作,如BPF_ADD、BPF_LD等。

?目的寄存器:R0~R9中的一個(gè)。

?源寄存器:R0~R10中的一個(gè)。

?偏移:16位,主要用于進(jìn)行指針類(lèi)型的數(shù)學(xué)運(yùn)算,可記為off16。

?立即數(shù):32位有符號(hào)的立即數(shù),可記為imm32。

Linux內(nèi)核使用struct bpf_insn結(jié)構(gòu)體表示eBPF的指令格式,struct bpf_insn結(jié)構(gòu)體的具體定義如下:

每條指令可能只用了一部分,并非全部。接下來(lái)將著重介紹一下操作碼的格式,如表2-4所示。

表2-4 操作碼的格式

操作碼的每個(gè)字段說(shuō)明如下:

?編碼:細(xì)分的操作碼,比如運(yùn)算指令BPF_ALU下面有相加(BPF_ADD)、相減(BPF_SUB)等細(xì)分指令。

?標(biāo)識(shí)位:包含BPF_K和BPF_X。BPF_K表示使用32位的立即數(shù)作為源操作數(shù);BPF_X表示使用源寄存器作為源操作數(shù)。

?指令類(lèi)型:包含三大類(lèi)指令,加載與存儲(chǔ)指令、運(yùn)算指令、跳轉(zhuǎn)指令。如表2-5所示。

表2-5 cBPF和eBPF的指令類(lèi)型與值

說(shuō)明:

eBPF把BPF_RET和BPF_MISC指令去掉了,換成了BPF_JMP32和BPF_ALU64,以提供更大范圍的跳轉(zhuǎn)和64位場(chǎng)景下的運(yùn)算操作。

?BPF_LDX和BPF_LD:兩個(gè)都用于加載操作,從而將數(shù)據(jù)從存儲(chǔ)器加載到寄存器中。BPF_LDX表示從內(nèi)存中加載數(shù)據(jù)到dst_reg;BPF_LD表示從imm64中加載數(shù)據(jù)到寄存器。

?BPF_ST和BPF_STX:兩個(gè)都用于存儲(chǔ)操作,從而將數(shù)據(jù)從寄存器存儲(chǔ)到存儲(chǔ)器中。BPF_ST表示把src_reg寄存器數(shù)據(jù)保存到內(nèi)存中;BPF_STX表示把imm32數(shù)據(jù)保存到內(nèi)存中。

?BPF_ALU和BPF_ALU64:分別是32位和64位下的ALU運(yùn)算操作。

?BPF_JMP和BPF_JMP32:跳轉(zhuǎn)指令。JMP32的跳轉(zhuǎn)范圍是0~32位(一個(gè)字)。

2)寬指令編碼:由基礎(chǔ)指令+64位立即數(shù)組成,寬指令是在基礎(chǔ)指令后增加了一個(gè)64位的立即數(shù)(imm64),即指令長(zhǎng)度為128位。寬指令編碼如表2-6所示。

64位立即數(shù)(imm64)的構(gòu)成方式:(imm32<<32)|imm32。其中,imm32為基礎(chǔ)指令中的立即數(shù)。

表2-6 寬指令編碼

3.加載指令

加載指令共分為4種類(lèi)型,分別是加載內(nèi)存數(shù)據(jù)指令、加載64位立即數(shù)指令、網(wǎng)絡(luò)報(bào)文訪問(wèn)指令和間接訪問(wèn)指令。

?加載內(nèi)存數(shù)據(jù)指令:一般形式是dst_reg=*(uint*)(src_reg+off16),對(duì)應(yīng)的宏定義如下所示:

?加載64位立即數(shù)指令:只能用于寬指令,從imm64中加載數(shù)據(jù)到寄存器,一般形式是dst_reg=imm64,對(duì)應(yīng)的宏定義如下所示:

?網(wǎng)絡(luò)報(bào)文訪問(wèn)指令:一般形式是R0=*(uint*)(skb->data+imm32),對(duì)應(yīng)的宏定義如下所示:

?間接訪問(wèn)指令:一般形式是R0=*(uint*)(skb->data+src_reg+imm32),對(duì)應(yīng)的宏定義如下所示:

4.存儲(chǔ)指令

存儲(chǔ)指令共分為3種類(lèi)型:寄存器數(shù)據(jù)寫(xiě)回內(nèi)存指令、32位立即數(shù)寫(xiě)回內(nèi)存指令和原子操作指令。

?寄存器數(shù)據(jù)寫(xiě)回內(nèi)存指令:一般形式是*(uint*)(dst_reg+off16)=src_reg,對(duì)應(yīng)的宏定義如下所示:

?32位立即數(shù)寫(xiě)回內(nèi)存指令:一般形式是*(uint *)(dst_reg+off16)=imm32,對(duì)應(yīng)的宏定義如下所示:

?原子操作指令:原子操作通常在需要同步訪問(wèn)或修改共享數(shù)據(jù)的并發(fā)編程中使用。在eBPF中,原子操作指令可以用來(lái)安全地更新eBPF程序共享的map值或其他數(shù)據(jù)結(jié)構(gòu),而無(wú)須擔(dān)心多個(gè)CPU核心或線程之間的競(jìng)爭(zhēng)條件。原子操作指令對(duì)編寫(xiě)多線程安全的eBPF程序至關(guān)重要,尤其是在網(wǎng)絡(luò)數(shù)據(jù)包處理或性能監(jiān)控等需要進(jìn)行高并發(fā)處理的場(chǎng)景中。指令形式類(lèi)似于寄存器數(shù)據(jù)寫(xiě)回內(nèi)存指令,對(duì)應(yīng)的宏定義如下所示:

5.邏輯運(yùn)算指令

邏輯運(yùn)算指令共分為6種類(lèi)型:寄存器運(yùn)算指令、立即數(shù)運(yùn)算指令、大小端轉(zhuǎn)換指令、寄存器mov指令、立即數(shù)mov指令和擴(kuò)展mov指令。

1)寄存器運(yùn)算指令:一般形式是dst_reg+=src_reg,對(duì)應(yīng)的宏定義如下所示:

2)立即數(shù)運(yùn)算指令:一般形式是dst_reg+=imm32,對(duì)應(yīng)的宏定義如下所示:

3)大小端轉(zhuǎn)換指令:進(jìn)行大小端轉(zhuǎn)換,比如將網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序。

4)寄存器mov指令:一般形式是dst_reg=src_reg,對(duì)應(yīng)的宏定義如下所示:

5)立即數(shù)mov指令:一般形式是dst_reg=imm32,對(duì)應(yīng)的宏定義如下所示:

6)擴(kuò)展mov指令:特殊形式的mov32指令,該指令專(zhuān)門(mén)用于對(duì)目標(biāo)寄存器進(jìn)行顯式的零擴(kuò)展操作。零擴(kuò)展是指將一個(gè)較小的帶符號(hào)或者無(wú)符號(hào)數(shù)值擴(kuò)展為一個(gè)較大的無(wú)符號(hào)數(shù)值,并用0填充新增的位。在編寫(xiě)eBPF程序時(shí),有時(shí)只用到寄存器的部分位(比如只用到了低32位),對(duì)于某些操作,我們需要確保寄存器的高位處于清零狀態(tài),以避免不可預(yù)知的錯(cuò)誤發(fā)生。BPF_ZEXT_REG可確保我們?cè)?4位寄存器中操作的是一個(gè)無(wú)符號(hào)的32位數(shù),而不被高位“污染”。對(duì)應(yīng)的宏定義如下所示:

6.跳轉(zhuǎn)指令

跳轉(zhuǎn)指令可分為4種類(lèi)型:條件跳轉(zhuǎn)指令、無(wú)條件跳轉(zhuǎn)指令、函數(shù)調(diào)用指令,以及程序退出指令。

1)條件跳轉(zhuǎn)指令。依據(jù)比對(duì)的操作數(shù)類(lèi)型,條件跳轉(zhuǎn)指令分為兩類(lèi):一類(lèi)是基于寄存器值的條件跳轉(zhuǎn),即BPF_JMP_REG;另一類(lèi)是基于立即數(shù)的條件跳轉(zhuǎn),即BPF_JMP_IMM。BPF_JMP_REG指令的一般形式為if(dst_reg 'op' src_reg)goto pc+off16,其中dst_reg和src_reg是寄存器,op是比較運(yùn)算符,off16為跳轉(zhuǎn)的偏移量。BPF_JMP_IMM指令的形式為if(dst_reg 'op' imm32)goto pc+off16,這里imm32表示一個(gè)立即數(shù)。此外,還有專(zhuān)為處理32位操作數(shù)設(shè)計(jì)的條件跳轉(zhuǎn)指令,即BPF_JMP32_REG和BPF_JMP32_IMM。相關(guān)的宏定義如下所示:

2)無(wú)條件跳轉(zhuǎn)指令。一般的形式是goto pc+off16。此類(lèi)處理一般對(duì)應(yīng)于C語(yǔ)言中的goto語(yǔ)言,或者編譯器隱含生成的跳轉(zhuǎn)語(yǔ)句。相關(guān)的宏定義如下所示:

3)函數(shù)調(diào)用指令。該指令可分為兩大類(lèi):第一類(lèi)是自定義函數(shù)調(diào)用,在傳統(tǒng)的eBPF程序中,所有子函數(shù)都應(yīng)該使用__always_inline屬性聲明,這將指示編譯器對(duì)函數(shù)進(jìn)行內(nèi)聯(lián)處理,而不是生成普通的函數(shù)調(diào)用代碼。第二類(lèi)是輔助函數(shù)調(diào)用,這涉及調(diào)用內(nèi)核提供的輔助函數(shù),它們?yōu)閑BPF程序執(zhí)行特定的操作或訪問(wèn)內(nèi)核數(shù)據(jù)提供了接口。其對(duì)應(yīng)的宏是BPF_EMIT_CALL,其定義如下所示:

4)程序退出指令。該指令一般對(duì)應(yīng)的C語(yǔ)言是return語(yǔ)句。相關(guān)的宏定義如下所示:

主站蜘蛛池模板: 绥芬河市| 桐乡市| 陵水| 丹巴县| 石泉县| 雷州市| 鹤庆县| 秭归县| 富川| 托里县| 阳新县| 沛县| 渭源县| 韶关市| 清流县| 临猗县| 毕节市| 灵石县| 当阳市| 察隅县| 三都| 崇明县| 新丰县| 达拉特旗| 兰考县| 玉溪市| 府谷县| 海晏县| 五原县| 石景山区| 兴化市| 鸡东县| 章丘市| 会宁县| 伽师县| 顺平县| 锡林郭勒盟| 海盐县| 瑞昌市| 赣榆县| 景东|