eBPF程序注入到内核中的流程,现在就带你研究(上)

news2024/10/5 21:14:32

系列目录

1. 疑惑

2. vfsstat_bpf__open

2.1 bpf_object__open_skeleton

2.2 bpf_object__open_mem/bpf_object_open

2.3 OPTS_VALID检查参数合法性

2.4 bpf_object__new新建bpf_object对象

2.5 bpf_object__elf_init初始化elf文件

2.6 bpf_object__elf_collect收集各个段落信息

2.7 bpf_object__init_maps初始化maps

2.8 bpf_object_init_progs初始化程序programs

2.9 open bpf总结

3. bpf_object__load_skeleton加载bpf

4. bpf_object__attach_skeleton附着bpf程序

5. 触发bpf程序

6 .总结

1. 疑惑

学习bpf过程中是带着问题去学习的:

1、我们写的bpf程序为什么可以对内核生效,代码是怎么注入到内核的?

2、libbpf相当于一个框架,那它又是怎么设计构建的呢?

3、什么是elf文件格式?vfsstat.bpf.o的内容包括什么信息?

在学习bpf之前,是不知道什么是elf文件的(也没关注)。

4、bpf可以用来干嘛,它有什么价值,到底能够做哪些事情?

问题很多,下面还是老老实实先看代码,先把一条线弄清楚,

我们以libbpf-tools的vfsstat为例子,继续深入探究一下这个bpf程序都干了些啥?

2. vfsstat_bpf__open

vfsstat_bpf__open打开bpf程序,这里我们主要关注的是libbpf框架和elf文件格式的处理,选择下面的流程来作为讲解内容:

8e326e52f27a565366e0c2e61ab9a8b6.png

2.1 bpf_object__open_skeleton

1、构建bpf_object_open_opts skel_opts,默认只有sz(bpf_object_open_opts结构体的大小)、object_name = "vfsstat_bpf"(skeleton的名字)

2、bpf_object__open_mem会从根据vfsstat.bpf.o的内容构建bpf_object(关键流程)

3、对于s->maps[0].mmaped进行赋值

e6cfe167f2beebb5196096a4ec6ef8f6.png

85e7e984e602ff29963554aa0f513965.png

079914bc632e2f7e2db4f9b0a0586e83.png

5af810a6dd0fae7af96861eddd4130e8.png

2.2 bpf_object__open_mem/bpf_object_open

=> bpf_object__open_mem

检查obj_buf、obj_buf_sz是否合法,同时调用bpf_object_open

cbbe3e39d943d7b32facc5d101e70084.png

=> bpf_object_open

1、OPTS_VALID检查参数是否合法,参数大小opts->sz不能小于size_t,额外长度需要都是0(超过bpf_object_open_opts__last_field的属于额外参数)

2、bpf_object__new新建bpf_object对象

3、bpf_object__elf_init初始化elf文件

4、bpf_object__elf_collect收集每个段落的信息

5、bpf_object__init_maps初始化map相关数据

6、bpf_object_init_progs遍历初始化每个bpf_program

e93ae3078f5c3a4da44ec828c7786cea.png

3b168051d25ab42ea1fa175a738fd3fb.png

d1499a0f624462c314fad1a1dcdc2eb0.png

4343e71e6be63da4add75ec7f3677f02.png

521f4cce5ca1513f8fefc49b3f60211d.png

2.3 OPTS_VALID检查参数合法性

OPTS_VALID这是一个宏,用于检查bpf各类参数是否合法,后续有额外参数传入需要注意这点

1、关于OPTS_VALID和offsetofend的宏定义如下

49079d9119c77fef2aba9f2c5a3f23df.png

2、 OPTS_VALID(opts, bpf_object_open_opts)扩展之后是

f9f624ded7787abfbab57e95e9dbf041.png

3、bpf_object_open_opts__last_field是bpf_object_open_opts中的kernel_log_level元素

6ae9021e10da489a0ba807a9c02f89fe.png

4、于是offsetofend(struct bpf_object_open_opts, bpf_object_open_opts__last_field)代表

bpf_object_open_opts从开始到kernel_log_level结尾的偏移(包括kernel_log_level的大小)

0a4e0cb75e08591325a12b97150dd4aa.png

5、libbpf_validate_opts会检查:

bpf_object_open_opts的sz必须大于等于sizeof(size_t)、超过元素type##__last_field的sz都必须是0

afe60d2122dc3915932ab0b85575ced4.png

2.4 bpf_object__new新建bpf_object对象

1、给bpf_object对象分配空间,

2、初始化结构体elf_state efile中的obj_buf(vfsstat.bpf.o的文件实际内容)、obj_buf_sz(vfsstat.bpf.o的文件实际内容的大小)

1922d66da4ed3479ef374d1d8285a663.png

50144b4539fef68ef6f56bcda1c1e036.png

2.5 bpf_object__elf_init初始化elf文件

1、efile.elf需要是NULL(没有初始化过)

2、elf_memory从obj_buf中读取elf文件(efile.elf),里面包括Elf64_Ehdr(elf文件头)、段落详细数据、段落简要数据数组

3、elf->kin必须是ELF_K_ELF类型

4、获取字符串表的id(elf_getshdrstrndx),并初始化字符串表的原始数据elf_rawdata

5、ebpf程序必须满足e_type = 1(ET_REL可重定向的文件), e_machine = 247(EM_BPF bpf的程序)

ca4e291769cd156960354dab00b9af19.png

4d807d9bbbb4f597e016f3669a4a399c.png

08f1759e7bb99ceb6ae073c49d6e758d.png

e15ff10052ff7a168206f85fa3bbd446.png

b53395bf2db5e9ee44309f2a1302e444.png

2.5.1 elf_memory/__libelf_read_mmaped_file

1、elf_memory这里已经转入libelf库中

146f2fa4412f2f10cbc2d4591b09863c.png

2、从内存中读取elf文件

1) determine_kind确定一下elf的文件类型

2) file_read_elf读取elf的文件

e9f1280df2b5ff3b66b2cb6e66d19c70.png

615e7cf8418e7a920e8961f38ebe8fbf.png

3、查找elf文件的类型

1) elf分为ELF_K_ELF(前面4个字节是"\177ELF"开头)、ELF_K_AR(前面8个字节是"!\n")

2) elf文件(后面指的elf文件都是ELF_K_ELF类型),文件开头是Elf64_Ehdr,

前面4个字节是"\177ELF"开头、第5个字节是eclass(文件类型,此处是0x02代表64bit)、

第6个字节是data(代表大端小端,此处是0x01代表小端格式)、第7个字节version(代表elf的版本号,此处是0x01代表版本号)。

elf的文件头具体如图:“ELF file header”

4d40a85ee784bed4550771fccae671e8.png

3fe84c4122d7f38635f68cb1d90c440f.png

7f6b88c432661b838ad30c7472e3071c.png

2.5.2 file_read_elf

1、先将vfsstat.bpf.o中的Elf64_Ehdr贴出来看一下,

=>如它的二进制数据如下图:

c7036f11999274431a56dede4e8641fa.png

=>转换成Elf64_Ehdr如下

45332fd73d6213c977d38806955bfe47.png

2、file_read_elf函数

1) 读取elf文件的时候,做32/64位、大端/小端检查

2) allocate_elf给elf分配内存和初始化话elf对象(extra额外需要分配的内存是44 * sizeof (Elf_Scn),

用来存储struct Elf_Scn data[0]的内容(elf->state.elf32.scns.data))

3) 根据map_address(obj_buf)和offset(0)获取Elf64_Ehdr *ehdr(elf的文件头)并赋值给elf->state.elf64.ehdr

4) ehdr(elf的文件头) + e_shoff(elf的文件头结束位置) => 获取elf->state.elf64.shdr(elf的section文件头)

5) 用shdr(elf的section文件头)初始化elf->state.elf64.scns.data[](section data)

dea12a87c3eb1e783edebd155adfc1ab.png

a18ee226c088cf59f48a846608ac628e.png

082508c02c3b30804aa978481316a7b8.png

f370a5816dfbc4b4ca76edea8cad7bcf.png

e01a64621623b3c246a55106ffd4111e.png

a384b54b540966193ad7f85b51ec8835.png

3c155607ca64877a0ed0f4e0e64283d3.png

76e7eb4fdcbf255cfd1bb85ea360539d.png

d03f024e37cfe2cff279a98781bb1dc1.png

3、关于Elf64_Shdr *shdr的格式如下图:

a7b7c3c4101aba5f2bf71bef716dd906.png

4、关于shdr段落头的二进制如下图:

5b8b7a8d4f2cf580e709f912819e0cb9.png

=> 实际前面4个段落头的内容如下

6a3b717f2ae5bb7abd0e882eca94a4ea.png

c4a149893def8c437aba0e0eeba4a560.png

2.5.3 elf_rawdata

elf_getscn函数其实就是取出的file_read_elf中的elf->state.elf64.scns.data[cnt],

其中cnt就是elf_getscn传入的idx(对应此处的字符串表的id:obj->efile.shstrndx = 1)

3dc83a8ac8f59f588bba29ddbf2bf663.png

1、elf_rawdata

如果该段落没有读取过,则调用__libelf_set_rawdata进行原始数据rawdata的读取

b17fe16ca39f115489c9a928f0db07d7.png

2、__libelf_set_rawdata_wrlock读取段落

以字符串表格为例

1) 读取原始数据的基地址scn->rawdata_base = scn->rawdata.d.d_buf = obj_buf + 0 + 10600(shdr[1]中的offset)

2) 设置原始数据Elf_Data的大小scn->rawdata.d.d_size = 607,类型scn->rawdata.d.d_type = ELF_T_BYTE(0),

偏移量scn->rawdata.d.d_off = 0,版本scn->rawdata.d.d_version = 1

3) scn->rawdata.s指向Elf_Scn *scn自己,设置已经读取了data_read = 1,修改Elf_Scn的flags = ELF_F_FILEDATA = 0x100

a83f09bcc3a2d50f1dd53e8dbfe33871.png

d5acfe78eefec0c9bd8289d9528c80e3.png

7cb902f7fb1a787b7da35e2b5024ebd8.png

fecc7c073ec463453451e0b855076a27.png

422e4edcf4bc01e8c0f653f33c9f153e.png

d34a8a7950a43417c2c2d5a1ab1fb64e.png

9e7dac08a4a03f6315f6604b88e42b5a.png

2.6 bpf_object__elf_collect收集各个段落信息

1、elf_nextscn先遍历一次elf的section找到对应的符号表的段落,如本例子中的shdr[43]

2、通过elf_sec_data获取字符串段落转换后的数据,然后初始化obj->efile的symbols(elf_sec_data取得的数据)、

symbols_shndx(符号表的段落ID)、strtabidx(符号表中字符串表的段落ID)

3、有了符号表之后,再次遍历所有的段落(ignore_elf_section跳过.strtab、.text(section size = 0)、.debug_、.rel.debug_***、".rel.BTF" ".rel.BTF.ext"),

elf_sec_data读取每个段落数据,根据不同的段落名字name(使用elf_sec_str读取)、段落类型sh_type做处理

4、针对程序段落,通过bpf_object__add_programs初始化程序段落

c60e61f727560fb5ead618efd8dec82a.png

021a513cd65af463680389416de6a31a.png

768b409b77e4579ddb2d920360b2efc6.png

2d08ae8d12fc3cacd5656260a3816aa4.png

3be00a0c5003d836d747e25b7c202e91.png

fb6059ce36884857c02849154c5e5149.png

2c654c6f5700f23a99a9d94faf3f9415.png

72bfed8eecc63ee3fa33a31c01f606d4.png

223087b3a067054c801eced9e94f4d3c.png

20f444cd738d69ae8758efbe4d067b10.png

2a7e77bc4340ac46a5d7eddc06bcd504.png

ee6dbc06286cdf8eca2a3f6a4a033241.png

8fccfe4dd7443d486faabfeab0382b92.png

0d058a368388725910b1dd7326400ba2.png

4d51cafa6c3978a80718f8ca1b12b763.png

2.6.1 elf_sec_data获取段落数据

1、传入的是scn = scns.data[43],读取的是符号表段落(".symtab")的信息

大致流程如下

elf_sec_data(libbpf.c) -> elf_getdata(elf_getdata.c) -> __elf_getdata_rdlock-> __libelf_set_rawdata_wrlock/__libelf_set_data_list_rdlock

__libelf_set_data_list_rdlock -> convert_data

385647f775348146818028cd3c16986d.png

2、__elf_getdata_rdlock

1) 如果该段路还没有初始化过原始数据rawdata,则调用__libelf_set_rawdata_wrlock进行初始化

2) 接着调用设置段落数据函数__libelf_set_data_list_rdlock

c8c063a3cadb22c87d41f19a73c0d3af.png

fc96d56985f9336f3976bf7c91a65515.png

3、__libelf_set_data_list_rdlock

如果存在段落数据则通过convert_data读取段落数据

686c5fea383971c9acb306c41a4a16f5.png

c5ff21bdfd8185347180b4a016e57686.png

4、convert_data

根据大小端对齐初始化转换后的数据scn->data_list.data.d

724c184c7c9a688adf9fa07724a7d37a.png

232354b439dd6017c639e27ef7c535ef.png

db0721929e44d1630e8a17e01170f424.png

579ffed8d7ba38951a3ced818d8ea719.png

2.6.2 elf_sec_str通过名字的偏移地址获得对应字符串段落中的名字

关于段落名字是从哪里来的,这里解释一下

1、elf_sec_str传入的是sh_name(段落头的名字的偏移地址,是一个数字)

其中bpf_object__elf_init的elf_getshdrstrndx获取shstrndx(字符串的段落ID) = e_shstrndx(elf文件头中的字符串段落id元素) = 1

c91269d986012373f3a776f2efff1d05.png

2、根据偏移offset从字符串段落中读取对应的字符串地址,如&strscn->rawdata_base[offset]

3fac384146bf307bdcca3135cab7f23c.png

46f503a9be59798212cbb2d8791df916.png

68bad420b3b7b4b70a833b89474bb413.png

713679b66895ce4a7fff5e91a8dffc22.png

9d988b782be0e61e361c75e491d22a9f.png

2639ac9f73760ad69bb7e645e7cea0e9.png

3、关于找到的如字符串段落本身shdr[1]的sh_name = 574(字符串段落中的偏移位置),

而字符串段落本身shdr[1]的sh_offset = 10600(字符串段落本身在bfp.o中偏移位置),

那么这个字符串对应*.bpf.o的位置是10600 + 574 = 11174‬ = 0x2BA6,

我们来看一下*.bpf.o 0x2BA6这个位置的是什么内容,如下图"字符串表的名字.strstab":

91066df73c08305baa451f49935caa3b.png

=>

从上面可以知道

sh_name = {int} 574代表的就是.strtab,也就是字符串段落shdr[1]的名字就是“.strtab”,

其它段落名字也是一样的,可以从这里的偏移找到字符串

2.6.3 bpf_object__add_programs初始化程序段落

1、该函数主要做了这些事情:

1) 遍历所有的符号Elf64_Sym数组(此处32个),找到shdr[3]对应的符号Elf64_Sym d_buf[20]

(st_shndx需要等于sec_idx,st_info后4位需要是STT_FUNC)

2) 根据符号Elf64_Sym d_buf[20]的st_name查找字符串段落找到该符号的函数名字,并打印如:

“libbpf: sec 'kprobe/vfs_read': found program 'kprobe_vfs_read' at insn offset 0 (0 bytes), code size 6 insns (48 bytes)”

上面的意思是找到段落'kprobe/vfs_read'程序,名字是kprobe_vfs_read,指令集偏移地址是0,一共有6条指令。

其中符号Elf64_Sym中st_size代表指令集总大小、st_value代表指令集的偏移量(此处是0,则代表段落原始数据shdr[3]开始就是指令数据)

3) 指令集大概如下面形式,每个指令8个字节:

f49995b64b4e1604414642a818b19b75.png

4) 所有bpf程序的都依次存放在obj->programs中

5) data(shdr[3]的sh_offset) + sec_off(d_buf[20]的st_value)得到的就是insn_data(该程序的指令集的基地址)

6) 调用bpf_object__init_prog初始化bpf_program(bpf程序)

5bdb0b79a28b44e0bcd79ac0a1137ddf.png

da8426a9a4518eef10c774fd53cee626.png

efb0633972b8d667d929bc5dc96b085d.png

34ef0b6941fb4514990f10f6b549eeb8.png

c1c56c25fd310587211f9a4c8edfef7d.png

d1de5903f0d89a4d21680a90c1ce343b.png

2、bpf_object__init_prog初始化bfp程序

=> 设置段落ID(sec_idx)、指令偏移(sec_insn_off)、指令个数(sec_insn_cnt)、是否加载标签(load)、段落名字(sec_name)、

函数名字(name)、报错指令集合的位置insns

df96a5b4a07f247c4497db85349f6d32.png

9fc77c0bef8411bb4561cd51aa7565d8.png

43209e64229d37870c991c6f2761addb.png

3、来看一下shdr[3]: sec_name = kprobe/vfs_read的指令数据是怎么样的

=> 指令原始数据如下图:

04df0f116eca726c24bdde6aa59d164e.png

=> 再来看一下这个函数(宏定义转换过后的):

75d33852fdc6d64c624a3da42e892aa3.png

=> 从sec_data->d_buf = sh_offset = 64 = 0x40开始的48个字节如下,这就是函数的指令,

下面是用llvm-objdump-11 -d /data/vfsstat.bpf.o截取出来的信息(可以看到部分指令的操作):

a59b3262abf92f76f6ad23b23ee89e1c.png

=> 转换成指令集的形式:

3ff12a72194fa62af5be4b60f7bd8815.png

334cfa1efc14c0b56a9b22f54ffa62ff.png

4、各个指令的含义

1) 第一条指令:

544a8199ed3a965f95ca17a23abf1a10.png

=>使用strace -e bpf -v -s 256 /data/vfsstat_bin 2 3指令查看指令操作码

{code=BPF_ALU64(64 bit)|BPF_K(32位立即数)|BPF_MOV(移动), dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0x1},

意思是:

BPF_ALU64: 0x07,64位计算指令(指令详情可以查看https://www.kernel.org/doc/html/latest/bpf/instruction-set.html)

BPF_K: 0x00,基于32位立即数作为源操作数

BPF_MOV: 0xb0,移动指令dst(目的操作寄存器) = src(源操作寄存器/数)

code = 0xb7 = BPF_ALU64(0x07)|BPF_K(0x00)|BPF_MOV(0xb0)

=> 于是上面的意思就变成了: dst_reg(r1) = imm(1) => r1 = 1

2、第二、三条指令

c75f0cc67100b2686dd41ebc65f4666d.png

=> 指令操作码如下:

{code=BPF_LD(0x00)|BPF_DW(0x18)|BPF_IMM(0x00), dst_reg=BPF_REG_2, src_reg=BPF_REG_2, off=0, imm=0x6},

{code=BPF_LD(0x00)|BPF_W(0x00)|BPF_IMM(0x00), dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0},

BPF_LD: 0x00装载操作,code的格式是:mode(3 bits) + size(2 bits) + instruction class(3 bits)

(

BPF_LD, BPF_LDX, BPF_ST, and BPF_STX这几个存储加载寄存器都是这个格式,

其中mode必须是BPF_IMM(0x00立即数)、BPF_ABS(0x20绝对的)、BPF_IND(0x40间接的)、BPF_MEM(0x60常规加载存储)、BPF_ATOMIC(0xc0自动操作)之一,

其中size必须是BPF_W(0x00一个字节)、BPF_H(0x08半个字节)、BPF_B(0x10一个byte)、BPF_DW(0x18双字节)

)

=> 于是上面的意思就变成了

dst_reg(r2) = imm64(这个值是在运行的时候生成的立即数)

3、第4条指令

91b438128013c0935d603246ad438eb0.png

=> 指令操作码如下:

{code=BPF_STX(0x03)|BPF_DW(64 bit的操作)(0x18)|BPF_XADD(加)(0xc0), dst_reg=BPF_REG_2, src_reg=BPF_REG_1, off=0, imm=0},

BPF_STX: 0x03,存储寄存器的值

(

LDR, [

]: load是将源数据address装入目的寄存器Destination

STR, [

]: store是将寄存器Destination的内容,存储在内存里面address

)

BPF_XADD:0xc0,在内核类似于atomic_add(),原子(lock)的相加

BPF_DW:0x18,双字节64位操作

=> 上面的意思是:

lock *(u64 *)(dst_reg(目标寄存器r2) + off(0)) += src_reg(源寄存器r1)

//如果是BPF_ADD,则不带lock:*(u64 *)(dst_reg + off16) += src_reg

4、第5条指令

3eaa8b90529f9662727b07d7586651e1.png

=> 指令操作码如下:

{code=BPF_ALU64|BPF_K|BPF_MOV, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0},

=> 上面的意思是:

BPF_ALU64|BPF_K(32位立即数)|BPF_MOV(移动)

dst_reg(r0) = imm(0)

//r0也是保存返回值的寄存器

5、第6条指令

f6c9efb4962093d025cdaa9f7c750448.png

=> 指令操作码如下:

{code=BPF_JMP|BPF_K|BPF_EXIT, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0}],

BPF_JMP: 0x05,64位的跳转指令

BPF_K: 0x00,32位立即数操作

BPF_EXIT:0x90,函数或者程序返回

=> 上面的意思是:

return函数返回

6、再回头解释一下上面指令对应的代码

5c911ee9e46884ab0ffdef00468d7c31.png

2.6.4 bpf_object__init_btf初始化btf段落

1、btf段落(".BTF")是shdr[34]、btf ext段落(".BTF.ext")是shdr[36]

7c3cb247edf44e70049d27359ae13346.png

2、bpf_object__init_btf

根据btf/btf_ext的源数据地址和大小新建btf对象(btf__new)、btf_ext对象(btf_ext__new)

af8ad021463939854e5dd5a428fc6abb.png

3、btf__new新建btf对象

1) btf_parse_hdr解析btf的头

2) btf_parse_str_sec判断btf的string段落是否合法

3) btf_parse_type_sec解析的type类型

fc491039db69486ac6168b72e2b32bd6.png

d99ee90b52eb38c7ad667ab100d6338a.png

f3b6eae573831b8d27825f3c6a0e4bef.png

4、btf_parse_hdr解析btf的头

=> btf头的格式是下面形式:

fb8cb7ec05d5a32edff572a8e9d54620.png

=> 本例中的btf头如下:

e97743ba81c786e3e96a0fc27f85529d.png

=> 解析btf数据头,查看是否合法,如`btf魔术头必须是0xeB9F`

f462aef57aa6da53966dad33c1ca8388.png

a9ad7bb0c75b34b1f43d89e2b99636ad.png

5、btf_parse_type_sec解析btf的type

=> 每个btf type的类型是,其中btf类型判断使用的就是info

094e9a5f59bed1341c4013fc92953b95.png

=> btf_parse_type_sec遍历每一个btf type(btf_type_size),并将数据保存起来

ecc3f80ab95df062e37a8195ea1e426d.png

b2dadea7703a6ddadf664b05b52840c2.png

=> btf_type_size根据info中的不同内容获取btf type的size的大小

098af11020b96f22859c751ddd44c6f2.png

6bc8a912d40bf76076b3f27e90af4514.png

如本例中前面2个btf type如下:

20ce7abe0a0bb62c244ecd9a0b09670f.png

2.7 bpf_object__init_maps初始化maps

在bpf程序中,maps是非常重要的,这个是bpf程序传输数据的通道,这里简单提一下

1) bpf_object__init_user_maps根据符号表初始化bpf_map

2) bpf_object__init_user_btf_maps初始化btf的map相关的

3) bpf_object__init_global_data_maps初始化SEC_DATA、SEC_RODATA、SEC_BSS段落的map数据(如全局变量__u64 stats[]就在SEC_BSS中),

构建bpf_map,对象mmaped存储的是maps的数据

4) bpf_object__init_kconfig_map初始化EXT_KCFG(.kconfig)相关的map

5) bpf_object__init_struct_ops_maps初始化.struct_ops相关的maps

202f47c01d71bbb8a9275006506aa1f4.png

2.8 bpf_object_init_progs初始化程序programs

这里将会出现SEC("kprobe/vfs_read")的处理流程

1、bpf_object_init_progs遍历所有的programs(bpf_program数组)

find_sec_def主要是通过sec_name(段落名字)去找到对应的程序处理函数、程序类型prog_type

95bfcdacab44b830b2ffaad5b166f307.png

6d8ffcbcf050fbf2d1a9acb1ec64fa8a.png

2、find_sec_def从section_defs找到和段落名字匹配的bpf_sec_def(bfp段落默认处理结构体)

5de6ec2acebb0d68620fca992365a54d.png

3、section_defs数组目前支持如下:

其中kprobe函数对应的是attach_kprobe

ac0295ea4c3a98aa3d7d605b1af2b641.png

d1ef470c0e3bdf3ec83035aa8a6e905f.png

ab793413a20e735ce02fc62bab219db3.png

b8539cc2e829b08adecd71437e039e79.png

4、bpf_sec_def(bfp段落默认处理结构体)

dde0076ac682fc6767ed8ab8b8e4b7d7.png

5、kprobe宏定义展开

#define SEC_DEF("kprobe/", KPROBE, 0, SEC_NONE, attach_kprobe)

相当于 =>

955b7811e31e3d263dbe88e69ec045f3.png

2.9 open bpf总结

上面讲完了open部分,open主要是libbpf从vfsstat.bpf.o源数据(读取使用libelf)中构建bpf程序、bpf maps等,

这部分不涉与内核沟通,只是准备环境

下章将在下周五发布,请持续关注~

一文搞定Android VSync机制来龙去脉

一文了解Vulkan在移动端渲染中的带宽与同步

AMD高保真超分算法1.0解密

ed107ec427d445675521a9fbff5e7289.gif

长按关注内核工匠微信

Linux内核黑科技| 技术文章| 精选教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/933069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

攻防世界-embarrass

原题 解题思路 搜索flag,结果搜不到。 换到kali里看。

STM32f103入门(2)流水灯蜂鸣器

流水灯 /* #define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */ #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */ #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */ #de…

pnpm安装包管理工具

pnpm安装包管理工具 pnpm代表performant npm&#xff08;高性能的npm&#xff09;&#xff0c;同npm和Yarn&#xff0c;都属于Javascript包管理安装工具&#xff0c;它较npm和Yarn在性能上得到很大提升&#xff0c;被称为快速的&#xff0c;节省磁盘空间的包管理工具。 pnpm…

QT6编译的文件分布情况

工程文件和编译文件位置分布 工程文件是自己建立的源文件位置&#xff0c;而同等级的位置在工程构建时会重新生成一个编译后的文件夹&#xff0c;里面包含了可执行的exe文件。而这个文件夹的内容也是QT ide运行时读取的文件&#xff0c;但这个文件的内容在IDE中如果不重新构建…

darknet yolo make报错,缺少instance-segmenter.o的规则

文章目录 darknet yolo make报错&#xff0c;缺少instance-segmenter.o的规则报错原因解决办法新问题解决办法 补充g编译选项Makefile编译规则 darknet yolo make报错&#xff0c;缺少instance-segmenter.o的规则 报错原因 Makefile没有识别到对于instance-segmenter.o的编译…

论文解读:高质量物体追踪

本文介绍了HQTrack&#xff0c;一种新的高质量视频物体追踪框架。HQTrack结合了视频多目标分割器&#xff08;VMOS&#xff09;和蒙版细化器&#xff08;MR&#xff09;&#xff0c;可追踪视频初始帧中指定的物体&#xff0c;并对追踪结果进行细化&#xff0c;以获得更高的准确…

【C++】4、Preprocessor 预处理:条件编译、源文件包含、宏替换、重定义行号、错误信息、编译器预留指令

文章目录 一、概述二、格式2.1 条件编译2.2 源文件包含2.3 宏替换2.3.1 语法2.3.2 C标准内置的预定义宏 2.4 重定义行号和文件名2.5 错误信息2.6 编译器预留指令 三、应用场景 C的 Build 可分为4个步骤&#xff1a;预处理、编译、汇编、链接。 预处理就是本文要详细说的宏替换…

跳跃游戏【贪心算法】

跳跃游戏 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。在这里插入图片…

Windows 10【压缩卷】操作报错【无法将卷压缩到超出任何不可移动的文件所在的点】的解决方法

目录 一、背景 二、原因 三、解决方法 3.1 Windows自带的碎片清理工具 3.1.1 操作步骤 3.1.2 操作结果 3.2 MyDefrag工具清理磁盘碎片 3.2.1 操作步骤 3.2.2 操作结果 3.3 Windows自带的事件查看器 3.3.1 操作步骤 3.3.2 操作结果 3.4 关闭虚拟内存并删除虚拟内存…

docker harbor私有库

目录 一.Harbor介绍 二.Harbor的特性 三.Harbor的构成 四.Harbor构建Docker私有仓库 4.2在Server主机上部署Harbor服务&#xff08;192.168.158.25&#xff09; 4.2.1 这时候这边就可以去查看192.168.158.25网页 4.3此时可真机访问serverIP 4.4通过127.0.0.1来登陆和推送镜…

一分钟学会用pygame制作棋盘背景

一分钟一个Pygame案例&#xff0c;这一集我们来学习一下如何生成一个视频中的棋盘背景效果&#xff0c;非常非常简单。 视频教程链接&#xff1a;https://www.bilibili.com/video/BV17G411d7Ah/ 当然我们这里是用来做页面的背景&#xff0c;你也可以拿来做别的效果&#xff0…

【随笔】- 程序员的40岁后健身计划

【随笔】- 40岁后程序员的健身计划 文章目录 【随笔】- 40岁后程序员的健身计划一、树立健身信心&#xff0c;制订坚持计划二、挑选让你舒适的方式三、调整速度&#xff0c;以间歇式训练为主四、刚开始锻炼&#xff0c;别求太快五、增加力量、柔韧性和平衡练习六、运动多样化七…

Nginx正向代理与反向代理及Minio反向代理实操(三)

本文是对: Nginx安装及Minio集群反向动态代理配置(二) 文的进一步完善: 多台服务器间免密登录|免密拷贝 Cenos7 搭建Minio集群部署服务器(一) Cenos7 搭建Minio集群Nginx统一访问入口|反向动态代理(二) Spring Boot 与Minio整合实现文件上传与下载(三) CentOS7的journa…

【80天学习完《深入理解计算机系统》】第十一天 3.4 跳转指令

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

清华大学龙明盛:人工智能工程化软件研发

今年以来&#xff0c;一系列通用大模型和领域大模型相继发布&#xff0c;将人工智能&#xff08;AI&#xff09;推向一个崭新的高度。AI开始得到人们开始普遍关注、并渗透至各行各业&#xff0c;引发各行各业发生巨大变化&#xff0c;软件研发行业自然也随之发生巨变。 基于对大…

6.1英寸屏幕?新款iPhone15系列在印度获得认证

据报道&#xff0c;A3904和A3090是最新的iPhone 15系列机型在印度获得监管机构BIS的认证。虽然目前BIS认证文件中没有包含相关的规格和参数信息&#xff0c;但可以确认这两款机型是iPhone 15系列中的一部分。 根据之前的报道&#xff0c;iPhone 15和iPhone 15 Pro将配备6.1英寸…

数据结构(Java实现)-java对象的比较

元素的比较 基本类型的比较 在Java中&#xff0c;基本类型的对象可以直接比较大小。 对象比较的问题 Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较 默认情况下调用的就是equal方法&#xff0c;但是该方法的比较规则是&#xff1a;没有比较引用变量引用对象的…

深度学习7:生成对抗网络 – Generative Adversarial Networks | GAN

生成对抗网络 – GAN 是最近2年很热门的一种无监督算法&#xff0c;他能生成出非常逼真的照片&#xff0c;图像甚至视频。我们手机里的照片处理软件中就会使用到它。 目录 生成对抗网络 GAN 的基本原理 大白话版本 非大白话版本 第一阶段&#xff1a;固定「判别器D」&#x…

数据结构(Java实现)LinkedList与链表(下)

** ** 结论 让一个指针从链表起始位置开始遍历链表&#xff0c;同时让一个指针从判环时相遇点的位置开始绕环运行&#xff0c;两个指针都是每次均走一步&#xff0c;最终肯定会在入口点的位置相遇。 LinkedList的模拟实现 单个节点的实现 尾插 运行结果如下&#xff1a; 也…

React 使用 useRef() 获取循环中所有子组件实例

目录 背景思考实现完整代码&#xff1a;成功运行后的界面如下&#xff1a; 知识点总结uesRef() 作对象处理useImperativeHandle() 父组件操作引入子组件的内部方法最后 背景 之前项目中使用了antd pro 中的 可编辑表格 (EditableProTable)&#xff0c;在页面中表格要经过多层遍…