BPF 之路:技术背景
目录
引言
什么是BPF
历史
组成
执行机制
BPF 和ebpf 的关系
BCC、bpftrace、IO Visor
BCC 项目的quick start
execsnoop
biolatency
动态插桩:kprobes和uprobes
概念
缺点
静态插桩:tracepoint 和USDT
概念
缺点
推荐的方案
初识bpftrace : 跟踪openat
技术背景
图解BPF
BPF辅助函数
bpf_probe_read()
为什么bpf_probe_read 要禁止缺页中断
bpf_probe_read是如何禁止缺页中断的
样例
BPF 系统调用命令
使用strace 分析execsnoop
BPF程序类型
BPF 映射表类型
BPF的并发控制
BPF sysfs 接口
BPF 类型格式
BPF CO-RE
BPF 的局限性
调用栈回溯
基于帧指针的调用栈回溯
基于调试信息来做debug
最后分支记录
ORC
符号
火焰图
事件源
Kprobes
uprobes
跟踪点 tracepoint
USDT
性能监控计数器
引言
什么是BPF
历史
是 Berkeley Packet Filter 的缩写,诞生于1992, 目的是提高网络包的过滤工具的性能。
2014年进入了Linux 内核的主线。
从使用的方式上来说,比较像是JavaScript。
组成
指令集、存储对象、辅助函数等几部分组成。
执行机制
一般有两种执行机制:
- 解释器
- 一个讲BPF指令动态转化成本地化指令的即时JIT 编译器
执行之前要通过验证器的安全性检查,可以保证BPF程序本身不会奔溃
BPF 和ebpf 的关系
现在的ebpf 为了和之前保持一致还是继续称呼为BPF。
BCC、bpftrace、IO Visor
直接通过BPF 指令编写BPF程序是比较繁琐的,因此有了高级语言去支持。
BCC(BPF编译器集合,BPF),最早用于开发BPF trace 程序,提供了一个C语言的环境,也提供了lua、python环境来实现用户端接口,是libbcc和libbpf库的前身,这两个库提供了使用BPF 程序对事件进行观测的库函数。
BCC 函数库提供了70多个tool。
bpftrace 是新出现的前端,提供了专门用于创建BPF工具的高级语言支持。bpftrace 也是基于libbcc和libpbf库进行构建的。
bpftrace 在编写功能强大的单行程序,短小的脚本比较在行,BCC 主要开发比较复杂的大型后端进程。
BCC 和bpftrace 不属于linux 内核,属于GITHUB上的一个 IO ViSor 的linux 基金会。
BCC 项目的quick start
execsnoop
来自bcc 项目,通过跟踪execve 系统调用来工作。
sudo execsnoop-bpfcc
作用
可以使用这个工具来检查业务负载,就是说可以看到进程是不是按照自己的想法,在一定的周期下被创建出来。
biolatency
概念
绘制块设备的延迟直方图(disk IO latency)
sudo biolatency-bpfcc
在虚拟机环境下,这个命令会在内核版本:5.19.0-35-generic 下执行失败。
动态插桩:kprobes和uprobes
概念
在生产环境中正在运行的程序的任意指令位置插入观测点
缺点
随着版本的更换,被插桩的函数可能会重新命名,或者直接被去掉,这样会有稳定性的问题,BPF工具可能会直接无法工作。
静态插桩:tracepoint 和USDT
概念
本质上就是为了解决上面动态插桩中接口稳定性的问题,直接把稳定的事件名硬编码到代码中,直接由开发者进行维护。
USDT(user level statically defined tracing),就是描述这个技术的。
缺点
硬编码会带来额外的维护成本
推荐的方案
首先使用静态的跟踪技术(tracpoint 或者USDT)如果不够用再使用动态插桩技术。
初识bpftrace : 跟踪openat
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat {printf("%s %s \n",comm,str(args->filename));}'
这里有一些小的tips:
- openat 在linux 中被调用的次数远远超过open
- bpftrace 必须要使用root 权限
- bpftrace 一般比较simple,支持一些比较小的命令工具,但是BCC tool功能比较强大
技术背景
图解BPF
BPF辅助函数
- Map操作函数:BPF_MAP_LOOKUP_ELEM、BPF_MAP_UPDATE_ELEM、BPF_MAP_DELETE_ELEM等
- 内存操作函数:BPF_MEMCPY、BPF_MEMCPY_STR、BPF_MEMSET等
- 网络操作函数:BPF_SOCK_OPS_TCP_SOCK等
- 时间操作函数:BPF_KTIME_GET_NS等
- 系统调用操作函数:BPF_TRACE_PRINTK、BPF_GET_CURRENT_PID_TID等
- 数学计算函数:BPF_ADD、BPF_SUB、BPF_MUL、BPF_DIV等
- 其他函数:BPF_DEBUG、BPF_EXIT等
bpf_probe_read()
BPF中的内存访问仅仅限于 BPF寄存器和栈空间(以及通过辅助函数访问BPF映射表),如果访问其他内存(比如说除了BPF之外的内存),就需要使用bpf_probe_read()。
这个函数会进程安全性的检查禁止出现缺页中断,以保证在probe 上下文中不会发生缺页中断(否则可能会引发内核问题)。
还有其他的辅助函数: bpf_probe_read_kernel、bpf_probe_read_user()
为什么bpf_probe_read 要禁止缺页中断
在Linux内核中,BPF程序可以在内核空间中访问用户空间内存。当BPF程序访问的用户空间地址空间中的页面不在物理内存中时,会触发缺页中断,然后内核会将对应页从磁盘中读入内存,完成物理内存的分配操作,这个过程是比较耗时的。
但是,在某些情况下,我们可能并不需要立即读入缺页中断所对应的页面,比如在处理高速网络数据包时,不能承受太大的时延。因此我们可以通过禁止缺页中断来避免耗时的物理内存分配,提高处理性能。
bpf_probe_read是如何禁止缺页中断的
需要设置当前线程的脱离缺页中断标志位
/* 禁用缺页中断 */ void disable_page_fault(void) {preempt_disable();current->flags |= PF_NOFREEZE;current->mm->def_flags |= VM_FAULT_NOPAGE; }
样例
使用bpf_probe_read 函数读取skb 中UDP 数据包
读取UDP数据包的过程需要先读取IP头部,然后再根据IP协议类型字段确定上层协议为UDP,接着读取UDP头部,然后再读取数据载荷。以下是一个使用bpf_probe_read()函数读取UDP数据包的示例代码
#include <linux/bpf.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/udp.h>int mybpf_prog(struct __sk_buff *skb) {void *data = (void *)(long)skb->data;void *data_end = (void *)(long)skb->data_end;struct ethhdr *eth = data;if (eth + 1 > data_end)return 0;// 读取IP头struct iphdr iph;if (bpf_probe_read(&iph, sizeof(iph), (void *)(eth + 1)) != 0)return 0;if (iph.protocol == IPPROTO_UDP) {// 读取UDP头struct udphdr uh;if (bpf_probe_read(&uh, sizeof(uh), (void *)((unsigned char *)iph + (iph.ihl * 4))) != 0)return 0;// 计算数据包总长度unsigned int len = ntohs(iph.tot_len) - (iph.ihl * 4) - sizeof(uh);if (len <= 0) // 数据包长度错误return 0;// 读取数据载荷unsigned char payload[len];if (bpf_probe_read(&payload, len, (void *)((unsigned char *)uh + sizeof(uh))) != 0)return 0;// 对读取的数据载荷进行处理...return 1;}return 0; }
BPF 系统调用命令
使用strace 分析execsnoop
sudo strace -ebpf execsnoop-bpfcc
bpf(BPF_BTF_LOAD, {btf="\237\353\1\0\30\0\0\0\0\0\0\0\274\4\0\0\274\4\0\0H\17\0\0\0\0\0\0\0\0\0\2"..., btf_log_buf=NULL, btf_size=5148, btf_log_size=0, btf_log_level=0}, 28) = 3 bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_PERF_EVENT_ARRAY, key_size=4, value_size=4, max_entries=128, map_flags=0, inner_map_fd=0, map_name="events", map_ifindex=0, btf_fd=0, btf_key_type_id=0, btf_value_type_id=0, btf_vmlinux_value_type_id=0, map_extra=0}, 72) = 4 bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_KPROBE, insn_cnt=510, insns=0x7ff36007d000, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(5, 19, 17), prog_flags=0, prog_name="syscall__execve", prog_ifindex=0, expected_attach_type=BPF_CGROUP_INET_INGRESS, prog_btf_fd=3, func_info_rec_size=8, func_info=0x5645a446e430, func_info_cnt=1, line_info_rec_size=16, line_info=0x5645a4a88f20, line_info_cnt=252, attach_btf_id=0, attach_prog_fd=0, fd_array=NULL}, 144) = 5 bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_KPROBE, insn_cnt=82, insns=0x7ff36021b7d0, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(5, 19, 17), prog_flags=0, prog_name="do_ret_sys_exec", prog_ifindex=0, expected_attach_type=BPF_CGROUP_INET_INGRESS, prog_btf_fd=3, func_info_rec_size=8, func_info=0x5645a446e430, func_info_cnt=1, line_info_rec_size=16, line_info=0x5645a348d760, line_info_cnt=28, attach_btf_id=0, attach_prog_fd=0, fd_array=NULL}, 144) = 7 PCOMM PID PPID RET ARGS bpf(BPF_MAP_UPDATE_ELEM, {map_fd=4, key=0x7ff35babc690, value=0x7ff35babc590, flags=BPF_ANY}, 144) = 0 bpf(BPF_MAP_UPDATE_ELEM, {map_fd=4, key=0x7ff35babc590, value=0x7ff35babc690, flags=BPF_ANY}, 144) = 0 ^Cstrace: Process 4540 detached
PS: 最好是可以避免直接使用strace 因为使用strace 本质上使用ptrace,会严重的降低目标进程的执行速度,性能可能会直接下降到原来的1%,但是他的好处是可以支持bpf 系统调用的翻译,比如说可以打印出BPF_PROG_LOAD
BPF程序类型
不同的bpf程序类型定义了BPF 程序可以挂载的事件类型,以及事件的参数,主要用于trace 用途的BPF程序的类型如下:
BPF 映射表类型
其中BPF_MAP_TYPE_PERF_EVENT_ARRAY 可以将内核中的捕捉到的信息传递到user ,execsnoop就使用了这个类型。
BPF的并发控制
BPF一直缺乏并发控制,直到Linux 5.1添加了自旋锁 spin lock帮助程序,尽管这些还不能用于跟踪程序。
通过跟踪,并行线程可以并行地查找和更新BPF映射字段,当一个线程从另一个线程覆盖更新时,就会导致破坏。这也称为丢失更新问题(lost update),即并发读写重叠导致丢失更新。跟踪前端BCC和bpftrace在可能的情况下使用每cpu哈希和数组映射类型来避免这种损坏。这将为每个逻辑CPU创建实例(比如说使用BPF_MAP_TYPE_PERCPU_HASH这种带有PERCPU 的type)
这是使用了BPF_MAP_TYPE_PERCPU_HASH type 的方式
strace -febpf bpftrace -e 'k:vfs_read { @ = count(); }'
这是没有使用并发控制的方式
strace -febpf bpftrace -e 'k:vfs_read { @++; }'
Comparing the counts shows that the normal hash undercounted events by 0.01%.
对比下来会有0.01% 的误差。
BPF sysfs 接口
在Linux 4.4中,BPF引入了通过虚拟文件系统公开BPF程序和映射的命令,通常挂载在/sys/fs/ BPF上。这被称为“钉住”,这可以有很多用途。它允许创建持久的BPF程序,类似于守护进程,并在加载它们的进程退出后继续运行。它还为用户级程序与正在运行的BPF程序进行交互提供了另一种方式:它们可以读写BPF映射。
BPF 类型格式
如果说我们缺少目标程序的源代码,导致写一些BPF tool 比较困难,那么这里有一种BTF 的技术可以解决这个问题。
但是BTF 技术还在开发过程中。
BPF CO-RE
BPF 的“一次编译,到处运行的”Compile Once - Run Everywhere 项目,旨在允许BPF程序一次性编译为BPF字节码,保存,然后在其他系统上分发和执行。这将避免在所有地方安装BPF编译器(LLVM和Clang),这对于空间受限的嵌入式Linux来说是一个挑战。它还可以避免在执行BPF可观察性工具时运行编译器所带来的运行时CPU和内存成本。
CO-RE 目前也在开发阶段。
BPF 的局限性
- 不可以随意的使用内核函数
- BPF 的栈的大小不可以超过512 (MAX_BPF_STACK),这个有解决方案:使用映射存储空间。
调用栈回溯
BPF 提供了存储调用栈信息的而专用映射表结构,可以保存基于帧指针或基于ORC的调用栈回溯信息。
基于帧指针的调用栈回溯
这个技术主要基于一个前提:函数调用栈帧链表的头部,始终保存在某一个寄存器中(RBP on x86_64),并且这个函数调用的返回地址永远位于RBP 的值指向的位置加上一个固定的偏移量(+8)。
这标志任何调试器都可以在中断程序执行之后,通过读取RBP后遍历以RBP的值为头部的链表,同时在固定偏移位置获取返回地址,从而轻松的进行栈回溯。
PS: 在gcc 编译器中默认是没有函数帧指针的,将RBP作为普通的寄存器来使用的,但是这个性能的提升其实没有很高,所以建议开启这个默认行为。
-fno-omit-frame-pointer
基于调试信息来做debug
也就是在gcc 的后面加 -g -wall 的意思。
这其中包含了DWARF的ELF 的调试信息
在ELF文件中调试相关的文件段是 .eh_frame 和.debug_frame
缺点就是这样会让这个可执行文件非常大
libjvm.so = 17M
libjvmd.so = 222M
最后分支记录
也就是LBR,是inter 的一个特别的技术,被记录在硬件缓冲区中,这个技术没有额外的开销。
但是支持记录的深度会存在限制。
BPF不支持LBR
ORC
针对栈回溯需求专门设计了一种新的调试信息格式——Oops 回滚能力(Oops rewind capability,ORC)。相比于DWARF格式,使用这种格式对于处理器的要求比较低,ORC使用的也是ELF的文件段,目前linux 内核以及给了一部分支持。
目前还没有开发用户态对ORC调用栈的支持。
在内核中基于ORC的调用栈回溯可以通过 perf_callchain_kernel 函数支持。
符号
调用栈信息目前在内核中以地址数据形式记录的,这些地址可以通过用户态的程序翻译成符号(比如函数的名字)。
但是这部分工作目前还没有完成。
火焰图
火焰图是一种用于显示程序 CPU 使用状况的可视化工具。下面给出一些火焰图的基本用法和解读方法:
- 坐标轴:火焰图的 y 轴标识调用栈,从上到下表示函数调用的深度。x 轴表示 CPU 时间(可以是毫秒、秒或其他单位),从左到右表示程序的运行时间。
- 颜色:火焰图的颜色表示函数调用在 CPU 时间或计数器空间上所占的相对值。
- 宽度:火焰图中的每个矩形宽度表示相关代码的 CPU 时间或者计数器测得值。
- 工具:例如Flamegraph,perf等火焰图生成工具都支持启用各种设定,如矩形排列顺序,颜色方案等等。
根据以上信息,接下来了解如何解读火焰图:
- 开头与结尾:火焰图的开始和结尾通常是程序的入口和退出点,在火焰图中通常具有相当宽的板块和颜色较浅。
- 宽度:火焰图中宽度较大的矩形表示在程序执行过程中的资源消耗比较高,其代码运行时间也相应较长。
- 颜色:火焰图中的颜色从浅绿色到深绿色选区,浅绿颜色区域是程序当中相对花费时间的低开销地方,深绿区域则追求更大的时间花费。
- 重复调用:在火焰图中,相同函数的重复调用会显示相同的框,框内的宽度指示它被调用的次数和执行时间的相对大小。
- 堆栈信息:火焰图中的每个函数调用的名称和调用堆栈信息也可用于鉴别和调整程序的性能问题。
事件源
Kprobes
kprobes 和kretprobes 的概念
kprobes 提供了针对内核的动态插桩支持,不需要重启内核。
kretprobes可以用来对内核函数返回时进行插桩以获取返回值,也可以用kprobes 和kretprobes 同时对一个函数进行插桩,来获取一个内核函数调用的时间。
原理
- 注册断点:Kprobes利用内核的动态内存分配技术,在内核 web 服务器代码的关键位置注册断点。
- 断点触发:当内核执行到已注册断点的位置时,则立即暂停执行,并在此时开始执行 Kprobes 注册的处理程序。
- 处理程序:Kprobes 的处理程序可以是一个用户自定义的函数,它可以在断点处挂钩处理内核函数调用,并记录性能数据或更改内核状态,以进行调试或分析。
- 处理完成:处理程序完成后,Kprobes 将继续执行中断位置的剩余代码,完成对内核代码的跟踪或处理。
Kprobes接口
之前必须使用c语言写入口处理函数和返回处理函数,然后调用register_kprobe()来注册。
现在主要使用BCC 或者bpftrace。
BCC提供了:
attach_kprobe() attach_kretprobe()
bpftrace 的一个demo 如下:
bpftrace -e 'kprobe:vfs_* {@[probe] = count()}'
uprobes
概念
提供了用户态程序的动态插桩。
和kprobes类似,原理和kprobes 也类似。
原理
通过在指定的地址处插入一条跳转指令来截获进入或离开该地址的CPU执行流程,并在其前后执行指定的处理程序。这种方法可以在不破坏二进制代码的情况下,实现对程序的运行状态进行监控和修改
接口
基于Ftrace ,向/sys/kernel/debug/tracing/uprobe_events: 通过对这个文件写入特定字符串的打开或者关闭uprobes
perf_event_open()
BCC 中提供了两个接口:
attach_uprobe attach_uretprobe
跟踪点 tracepoint
概念
静态插桩
原理
在编写内核代码时,开发者可以使用tracepoint宏定义来预定义跟踪点。这些跟踪点及其参数在编译时就已经被固定在内核二进制代码中了。
在程序运行时,如果开启了跟踪功能,当程序执行到相应的跟踪点时,会触发与之对应的tracepoint回调函数。这个回调函数可以在代码中定义,用来实现所需要的操作。由于跟踪点已经事先定义好了,因此可以避免插入额外的汇编指令,从而减少了对程序运行性能的影响。
接口
BCC提供了
tracepoint_probe()
USDT
概念
用户态预定义静态跟踪, 提供了一种用户空间的跟踪点机制
BPF 与USDT
USDT().enable_probe()
性能监控计数器
性能监控计数器是用于测量程序运行过程中各种系统硬件状态的计数器。它们通常由处理器底层硬件提供,在系统中大量运行的计数器可以用于监控系统的性能和瓶颈。性能监控计数器可以衡量诸如指令执行、CPU缓存性能、内存汇总等方面的各项指标,进而评估整个系统的性能和优化方案的效果。
在软件开发过程中,使用性能监控计数器可以比较精确地分析程序的性能瓶颈。开发人员可以结合不同的计数器指标,确定系统瓶颈所在,并通过优化代码和算法等方式提高程序的性能。
常见的性能监控计数器包括CPU运行周期(Clocks)、指令执行数(Instructions)、缓存命中率(Cache Hits)、内存访问延迟(Memory Latency)等。这些计数器通常可以通过专用工具或系统命令行接口获取,例如Linux系统提供的perf工具、Intel VTune、AMD CodeXL等性能监控工具。
相关文章:

BPF 之路:技术背景
目录 引言 什么是BPF 历史 组成 执行机制 BPF 和ebpf 的关系 BCC、bpftrace、IO Visor BCC 项目的quick start execsnoop biolatency 动态插桩:kprobes和uprobes 概念 缺点 静态插桩:tracepoint 和USDT 概念 缺点 推荐的方案 初识bpft…...

C++—— set、map、multiset、multimap
目录 关联式容器 概念 键值对 树形关联式容器 set 介绍 定义方式 使用 map 介绍 使用 multiset 介绍 使用 multimap 介绍 使用 相关的OJ题 前K个高频单词 关联式容器 概念 我们之前接触过的一些容器,比如:vector、list、deque、forwa…...

Qlib使用
Qlib https://github.com/microsoft/qlib 将csv文件转化为Qlib的数据格式:https://qlib.readthedocs.io/en/latest/component/data.html#converting-csv-format-into-qlib-format 注意每支股票都要保存成单独一个文档,且文档名字与股票代号一致。 其中f…...

TL-WDR7660 httpProcDataSrv任意代码执行漏洞复现分析
01 漏洞简述 2023年1月31日,CNVD公开了一个由国内安全研究员提交的TL-WDR7660 httpProcDataSrv任意代码执行漏洞,编号为CNVD-2023-05404,同时公开了漏洞利用详情,poc代码链接为https://github.com/fishykz/TP-POC。从poc代码详情…...

基于DDS的SOA测试方案实现
随着以太网技术在车载网络中的应用,各种基于以太网的中间件也相继被应用在车内,如果对车载网络有过相关了解的小伙伴,对于作为中间件之一的DDS(数据分发服务Data Distribution Service)可能并不陌生;若没有…...

LibTorch中Windows系统环境配置及CUDA不可用问题解决
前言:本文对在Windows系统上进行LibTorch开发环境配置及相关问题解决做一个较为详细的记录,以便后续查询使用。 使用环境版本: Windows 11 Visual Studio 2022 CUDA 12.0 LibTorch 1.13.1_cu11.7 目录一、LibTorch简介二、LibTorch下载安装三…...

Java并发编程实战二
线程间的通讯方式 1.volitate(缓存一致性协议),synchronize,lock(都保证可见性) 2.wait.notify,await(),signal(前两个是Object,后两个属于lock) 3.管道输入、输出流 (示例代码:PipeInOut.java)(目前几乎没人使用) 管道输入/输…...

Linux中最基本的命令ls的用法有哪些?
Linux是一种流行的操作系统,被广泛应用于服务器和个人电脑。Linux命令行界面是使用Linux操作系统的关键。其中一个最基本的命令是"ls"命令,该命令用于列出指定目录中的所有文件和子目录。在这篇文章中,我们将探讨ls命令及其各种用途…...

第 100002(十万零二)个素数是多少?
题目描述 素数就是不能再进行等分的整数。比如7,11。而 9 不是素数,因为它可以平分为 3 等份。一般认为最小的素数是2,接着是 3,5,... 请问,第 100002(十万零二)个素数是多少? 请注意࿱…...

Lua迭代器
Lua迭代器 迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。 在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。 泛型 f…...

同步与互斥之信号量
目录 1、信号量用于线程的互斥 验证 2、信号量用于线程的同步 验证 3、无名信号量用于进程间互斥 代码一 代码二 验证 4、有名信号量 用于进程间同步和互斥 验证 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它…...

如何当个优秀的文档工程师?从 TC China 看技术文档工程师的自我修养
本文系 NebulaGraph Community Academic 技术文档工程师 Abby 的参会观感,讲述了她在中国技术传播大会分享的收获以及感悟。 据说,技术内容领域、传播领域的专家和决策者们会在中国技术传播大会「tcworld China 2022」大会上分享心得。作为一名技术文档工…...

如何学习k8s
学习Kubernetes可以遵循以下步骤: 了解Kubernetes的基本概念和架构。学习Kubernetes前,需要了解它的基本概念和组成部分,包括Pod、Service、ReplicaSet、Deployment、Namespace等等,同时也需要了解Kubernetes的整体架构和工作原理…...

【SSM】MyBatis(十.动态sql)
文章目录1.if2.where3.trim4.set5. choose when otherwise6.foreach6.1 批量删除6.2 批量增加7.sql1.if <select id"selectByMultiCondition" resultType"Car">select * from t_car where 1 1<if test"brand ! null and brand ! ">…...

最近很多人都在说 “前端已死”,讲讲我的看法
转自 : 掘金 作者 : Ethan_Zhou 现状 我记得去年脉脉的论调还都是 客户端已死,前后端还都是一片祥和,有秀工资的,有咨询客户端转前端的,怎么最近打开脉脉一看,风向变了? 随便刷几下,出来的信息…...

大家好,我是火旺技术
大家好,我是火旺技术 在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用。这其中,家乡特色推荐的网络应用已经成为外国家乡推荐系统的一种很普遍的方式。不过,在国内,管理网站可能还处于起步阶段。 …...

【Java并发编程系列】全方位理解多线程几乎包含线程的所有操作哦
文章目录一、概述及目录二、实现多线程的方式2.1 继承Tread类,重写run方法。start方法2.2 实现Runnable方法,并实现run接口方法2.3 实现Callable接口重写call方法,Feature.get()获取返回值三、线程的执行流程3.1 执行流程3.2 start方法和 run…...

天宝S6测量机器人/天宝S6全站仪参数/教程/Trimble 天宝全站仪
TRIMBLE DR PLUS技术 Trimble DR Plus™距离测量技术实现更大范围的直接反射测量,不使用棱镜也能进行长距离测量。难以抵达或不安全的测 量目标,对Trimble S6来说不再是问题。Trimble DR Plus结合 了MagDrive™磁驱伺服技术,使测量的快捷和…...

c++基础知识汇总
目录 1、基础 1.2 注释 1.3 变量 1.4 常量 1.5 关键字 1.6 标识符命名规则 2 数据类型 2.1 整型 2.2 sizeof关键字 2.3 实型(浮点型) 2.4 字符型 2.5 转义字符 2.6 字符串型 2.7 布尔类型 bool 2.8 数据的输入 1、基础 1.2 注释 作用&a…...

重磅!基于GPT-4的全新智能编程助手 GitHub Copilot X 来了!
GitHub Copilot相信大家一定不陌生了,强大的智能代码补全功能一度让媒体直呼程序员要被替代。随着OpenAI推出全新的GPT-4,GitHub Copilot也在3月22日,推出了全新一代产品:GitHub Copilot X 。最新的GitHub Copilot X 不仅可以自动…...

第04章_运算符
第04章_运算符 🏠个人主页:shark-Gao 🧑个人简介:大家好,我是shark-Gao,一个想要与大家共同进步的男人😉😉 🎉目前状况:23届毕业生,目前在某公…...

Excel 文件比较工具:xlCompare 11.0 Crack
(Excel 文件比较工具)xlCompare 11.0 下载并安装最新版本的 xlCompare。下载是一个功能齐全的版本。 筛选匹配的行 筛选不同的行 仅显示两个 Excel 文件中存在的行,并排除新(已删除)行 隐藏在另一张工作表上具有相应行…...

802.1x认证原理
802.1x认证原理802.1X认证简介802.1X认证协议802.1X认证流程802.1X认证简介 定义:802.1x协议是一种基于端口的网络接入控制协议。基于端口的网络接入控制,是指在局域网接入设备的端口这一级验证用户身份,并且控制其访问权限。优点࿱…...

GPIO的八种模式分析
GPIO是general purpose input output,即通用输入输出端口,作用是负责外部器件的信息和控制外部器件工作。 GPIO有如下几个特点:1.不同型号的IO口数量不同;2,反转快速,每次翻转最快只需要两个时钟周期,以ST…...

携职教育:财会人常用必备,203个EXCEL快捷键汇总
会用快捷键的人早下班! 作为财务人员如果你想要提高工作效率,不掌握一些Excel快捷键怎么行? 老板看见了,也会赞一声,“看,这就是专业。” 立马给你加薪(划掉),工作效率这…...

【美赛】2023年ICM问题Z:奥运会的未来(思路、代码)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

CSS基础入门
CSS基础之语法 介绍 CSS(层叠样式表)是一门用来设计网页样式的语言,如网页的布局、字体、颜色搭配、视觉特效。作为WEB开发的基础技术之一,掌握CSS的语法和API对于我们构建丰富的网页是必须的。 基础语法 <style>div …...

可重入锁、读写锁、邮戳锁 详解
文章目录1、可重入锁(递归锁)2、读写锁2.1、读写分离2.2、从写锁到读锁,ReentrantReadWriteLock可以降级2.3、写锁和读锁是互斥的3、邮戳锁StampedLock3.1、是什么3.2、锁饥饿3.3、如何缓解锁饥饿问题呢3.3.1、使用“公平”策略3.3.2、Stampe…...

HBase客户端、服务器端、列簇设计、HDFS相关优化,HBase写性能优化切入点,写异常问题检查点
HBase客户端、服务器端、列簇设计、HDFS相关优化,HBase写性能优化切入点,写异常问题检查点HBase读优化1.1 HBase客户端优化1) scan缓存是否设置合理?2) get请求是否可以使用批量请求?3) 请求是否可以显示指定列簇或者列࿱…...

DINO-DETR在COCO缩减数据集上实验结果分析
博主在进行DINO-DETR模型实验时,使用缩减后的COCO数据集进行训练,发现其mAP值只能达到0.27作用,故而修改了下pycocotool的代码,令其输出每个类别的AP值,来看看是由于什么原因导致这个问题。 之所以这样是因为博主认为各…...