bcc Reference Guide(bcc的c标准库)

bcc Reference Guide

用于检索(Ctrl-F)和参考。对于教程,请从tutorial.md开始。

本指南不完整。如果感觉缺少了什么,请检查bcc和内核源代码。如果你确认我们遗漏了什么,请发送一个pull request来修复它,并帮助每个人。

Contents

BPF C

本节描述bcc程序的C部分。

Events & Arguments

1. kprobes

语法:kprobe__kernel_function_name

kprobe__是一个特殊的前缀,它为作为余数提供的内核函数名创建kprobe(内核函数调用的动态跟踪)。您也可以通过声明一个普通的C函数来使用kprobes,然后使用Python BPF.attach_kprobe()(稍后介绍)将其与内核函数相关联。

参数在函数声明中指定:kprobe__kernel_function_name(struct pt_regs *ctx [,argument1 …])

举例来说:

For example:

int kprobe__tcp_v4_connect(struct pt_regs *ctx, struct sock *sk) {
    [...]
}

这将使用kprobe对tcp_v4_connect()内核函数进行检测,参数如下:

  • struct pt_regs *ctx:寄存器和BPF上下文。
  • struct sock *sk:tcp_v4_connect()的第一个参数。

第一个参数总是struct pt_regs *,其余的是函数的参数(如果你不打算使用它们,就不需要指定它们)。

现场示例:
code (output),
code (output)

2. kretprobes

语法:kretprobe__kernel_function_name

kretprobe__是一个特殊的前缀,它为作为余数提供的内核函数名创建kretprobe(内核函数返回的动态跟踪)。您也可以通过声明一个普通的C函数来使用kretprobes,然后使用Python BPF.attach_kretprobe()(稍后介绍)将其与内核函数相关联。

返回值可作为PT_REGS_RC(ctx),给定函数声明:kretprobe__kernel_function_name(struct pt_regs *ctx)

举例来说:

int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
{
    int ret = PT_REGS_RC(ctx);
    [...]
}

这将使用kretprobe检测tcp_v4_connect()内核函数的返回,并将返回值存储在ret中。

现场示例:
code (output)

3. Tracepoints

语法:TRACEPOINT_PROBE(category,event)

这是一个宏,用于检测由category:event定义的跟踪点。

跟踪点名称为<category>:<event>
The probe function name is tracepoint__<category>__<event>.

参数在argsstruct中可用,它们是跟踪点参数。列出这些的一种方法是在/sys/kernel/debug/tracing/events/category/event/format下cat相关的格式文件。

在每个需要上下文作为参数的函数中,可以使用argsstruct代替ctx。这包括perf_submit()

举例来说:

TRACEPOINT_PROBE(random, urandom_read) {
    // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format
    bpf_trace_printk("%d\\n", args->got_bits);
    return 0;
}

这将检测跟踪点random:urandom_read tracepoint,并打印跟踪点参数got_bits
When using Python API, this probe is automatically attached to the right tracepoint target.
三号
注意,上面定义的探测函数的名称是BPF::attach_tracepoint("random:urandom_read", "tracepoint__random__urandom_read")
Note the name of the probe function defined above is tracepoint__random__urandom_read.

现场示例:
code (output),
search /examples,
search /tools

4. uprobes

通过在C中声明一个普通函数,然后通过BPF.attach_uprobe()(稍后介绍)将其关联为Python中的uprobe探测器来检测它们。
参数可以使用PT_REGS_PARM宏来检查。
举例来说:

int count(struct pt_regs *ctx) {
    char buf[64];
    bpf_probe_read_user(&buf, sizeof(buf), (void *)PT_REGS_PARM1(ctx));
    bpf_trace_printk("%s %d", buf, PT_REGS_PARM2(ctx));
    return(0);
}

它将第一个参数作为字符串读取,然后将第二个参数作为整数打印出来。

现场示例:
code

5. uretprobes

通过在C中声明一个普通函数,然后通过BPF.attach_uretprobe()(稍后介绍)将其关联为Python中的uretprobe探测器来检测这些。

返回值可作为PT_REGS_RC(ctx),给定函数声明:function_name(struct pt_regs *ctx)

举例来说:

BPF_HISTOGRAM(dist);
int count(struct pt_regs *ctx) {
    dist.increment(PT_REGS_RC(ctx));
    return 0;
}

这将递增dist直方图中由返回值索引的存储桶。

现场示例:
code (output),
code (output)

6. USDT probes

这些是用户静态定义跟踪(USDT)探测器,可以放置在某些应用程序或库中,以提供用户级的跟踪点等效物。为USDT支持方法提供的主要BPF方法是enable_probe()。USDT探测器是通过在C中声明一个普通函数,然后通过USDT.enable_probe()将其关联为Python中的USDT探测器来检测的。

参数可以通过以下方式读取:bpf_usdt_readarg(index,ctx,&addr)

举例来说:

int do_trace(struct pt_regs *ctx) {
    uint64_t addr;
    char path[128];
    bpf_usdt_readarg(6, ctx, &addr);
    bpf_probe_read_user(&path, sizeof(path), (void *)addr);
    bpf_trace_printk("path:%s\\n", path);
    return 0;
};

这将读取第六个USDT参数,然后将其作为字符串拉入path

当通过C API中BPF::init的第三个参数初始化USDT时,如果任何USDT失败到init,则整个BPF::init将失败。如果您对某些USDT无法执行init没有问题,请在调用BPF::init_usdt之前使用BPF::init

现场示例:
code,
search /examples,
search /tools

7. Raw Tracepoints

语法:RAW_TRACEPOINT_PROBE(event)

这是一个宏,用于检测由事件定义的原始跟踪点。

参数是指向结构bpf_raw_tracepoint_args的指针,它在bpf. h中定义。struct字段args包含原始跟踪点的所有参数,您可以在linux树include/trace/events中找到这些参数
目录。

举例来说:

RAW_TRACEPOINT_PROBE(sched_switch)
{
    // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)
    struct task_struct *prev = (struct task_struct *)ctx->args[1];
    struct task_struct *next= (struct task_struct *)ctx->args[2];
    s32 prev_tgid, next_tgid;

    bpf_probe_read_kernel(&prev_tgid, sizeof(prev->tgid), &prev->tgid);
    bpf_probe_read_kernel(&next_tgid, sizeof(next->tgid), &next->tgid);
    bpf_trace_printk("%d -> %d\\n", prev_tgid, next_tgid);
}

这将检测sched:sched_switch跟踪点,并打印上一个和下一个tgid。

现场示例:
search /tools

8. system call tracepoints

语法:syscall__SYSCALLNAME

syscall__是一个特殊的前缀,它为作为余数提供的系统调用名称创建一个kprobe。你可以通过声明一个普通的C函数来使用它,然后使用Python BPF.get_syscall_fnname(SYSCALLNAME)BPF.attach_kprobe()来关联它。

参数在函数声明中指定:syscall__SYSCALLNAME(struct pt_regs *ctx, [, argument1 ...]).

举例来说:

int syscall__execve(struct pt_regs *ctx,
    const char __user *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    [...]
}

这将检测execve系统调用。

第一个参数总是struct pt_regs *,其余的是函数的参数(如果你不打算使用它们,就不需要指定它们)。

对应的Python代码:

b = BPF(text=bpf_text)
execve_fnname = b.get_syscall_fnname("execve")
b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")

Examples in situ:
code (output)

9. kfuncs

语法:KFUNC_PROBE(function,typeof(arg1)arg1,typeof(arg2)arge …)
MODULE_KFUNC_PROBE(module,function,typeof(arg1)arg1,typeof(arg2)arge …)

这是一个通过trampoline实现内核函数的宏
在函数执行之前。它由函数名和
定义为argX的函数参数。

举例来说:

KFUNC_PROBE(do_sys_open, int dfd, const char *filename, int flags, int mode)
{
    ...

这将检测do_sys_open内核函数并生成其参数
可作为标准参数值访问。

现场示例:
search /tools

10. kretfuncs

语法:KRETFUNC_PROBE(event,typeof(arg1)arg1,typeof(arg2)arge …,int ret)
MODULE_KRETFUNC_PROBE(module,function,typeof(arg1)arg1,typeof(arg2)arge …)

这是一个通过trampoline实现内核函数的宏
在函数执行之后。它由函数名和
定义为argX的函数参数。

探测器的最后一个参数是检测函数的返回值。

举例来说:

KRETFUNC_PROBE(do_sys_open, int dfd, const char *filename, int flags, int mode, int ret)
{
    ...

这将检测do_sys_open内核函数并生成其参数
可作为标准参数值与其返回值一起访问。

现场示例:
search /tools

11. LSM Probes

语法:LSM_PROBE(hook,typeof(arg1)arg1,typeof(arg2)arg2 …)

这是一个将LSM钩子作为BPF程序进行检测的宏。它可以是
用于审核安全事件并在BPF中实施MAC安全策略。
It is defined by specifying the hook name followed by its arguments.

挂钩名称可以在
include/linux/security.h
通过使用像security_hookname这样的函数,只使用hookname部分。
For example, security_bpf would simply become bpf.

与其他BPF程序类型不同,LSM探测器中指定的返回值
很重要返回值0允许钩子成功,而
任何非零的返回值都将导致钩子失败并拒绝
安全操作。

以下示例检测拒绝所有未来BPF操作的挂接:

LSM_PROBE(bpf, int cmd, union bpf_attr *attr, unsigned int size)
{
    return -EPERM;
}

这将对security_bpf钩进行器械操作,并使其返回-EPERM
以允许该操作代替。
to allow the operation instead.

LSM探测器至少需要一个5.7+内核,并设置以下配置选项:

  • CONFIG_BPF_LSM=y
  • CONFIG_LSM逗号分隔的字符串必须包含“bpf”(例如,
    #2)

现场示例:
search /tests

12. BPF ITERATORS

语法:BPF_ITER(target)

这是一个为bpf迭代器程序定义程序签名的宏。参数target指定程序要迭代的内容。

目前,内核没有接口来发现支持哪些目标。要查找支持的内容,最好在tools/testing/selftests/bpf/prog_test/bpf_iter.c中,一些示例bpf iter程序在tools/testing/selftests/bpf/progs中,文件名前缀为bpf_iter。

下面的示例为目标任务定义了一个程序,它遍历内核中的所有任务。

BPF_ITER(task)
{
  struct seq_file *seq = ctx->meta->seq;
  struct task_struct *task = ctx->task;

  if (task == (void *)0)
    return 0;

  ... task->pid, task->tgid, task->comm, ...
  return 0;
}

在5.8内核中引入了BPF迭代器,用于task、task_file、bpf_map、netlink_sock和ipv6_route。在5.9中,增加了对tcp/udp套接字和bpf映射元素(hashmap、arraymap和sk_local_storage_map)遍历的支持。

Data

1. bpf_probe_read_kernel()

语法:syscall__SYSCALLNAME

返回:成功时返回0

这将大小字节从内核地址空间复制到BPF堆栈,以便BPF稍后可以对其进行操作。为了安全起见,所有内核内存读取必须通过bpf_probe_read_kernel()。这在某些情况下会自动发生,例如解除内核变量的引用,因为bcc会重写BPF程序以包含必要的bpf_probe_read_kernel()。

现场示例:
search /examples,
search /tools

2. bpf_probe_read_kernel_str()

语法:syscall__SYSCALLNAME

退货:

  • >0字符串长度,成功时包括尾随NULL
  • < 0错误

这会将一个以NULL结尾的字符串从内核地址空间复制到BPF堆栈,以便BPF稍后可以对其进行操作。在字符串长度小于size的情况下,不使用另外的NULL字节填充目标。如果字符串长度大于size,则仅复制size - 1字节,并将最后一个字节设置为NULL

现场示例:
search /examples,
search /tools

3. bpf_ktime_get_ns()

语法:syscall__SYSCALLNAME

返回:u64纳秒数。在系统靴子时启动,但在挂起期间停止。

现场示例:
search /examples,
search /tools

4. bpf_get_current_pid_tgid()

语法:syscall__SYSCALLNAME

返回:current->tgid << 32 | current->pid

返回低32位的进程ID(内核的PID视图,在用户空间中通常显示为线程ID),以及高32位的线程组ID(用户空间通常认为是PID)。通过直接将其设置为u32,我们丢弃了高32位。

现场示例:
search /examples,
search /tools

5. bpf_get_current_uid_gid()

语法:syscall__SYSCALLNAME

返回:current->tgid << 32 | current->pid

返回用户ID和组ID。

现场示例:
search /examples,
search /tools

6. bpf_get_current_comm()

语法:syscall__SYSCALLNAME

返回:成功时返回0

用当前进程名填充第一个参数地址。它应该是一个指向至少大小为TASK_COMM_LEN的字符数组的指针,该数组在linux/sched. h中定义。举例来说:

#include <linux/sched.h>

int do_trace(struct pt_regs *ctx) {
    char comm[TASK_COMM_LEN];
    bpf_get_current_comm(&comm, sizeof(comm));
[...]

Examples in situ:
search /examples,
search /tools

7. bpf_get_current_task()

语法:syscall__SYSCALLNAME

返回:当前任务作为指向结构task_struct的指针。

返回指向当前任务的task_struct对象的指针。这个帮助器可以用来计算进程的CPU时间、识别内核线程、获取当前CPU的运行队列或检索许多其他信息。

在Linux 4.13中,由于字段随机化的问题,您可能需要在includes之前使用两个#define指令:

#define randomized_struct_fields_start  struct {
#define randomized_struct_fields_end    };
#include <linux/sched.h>

int do_trace(void *ctx) {
    struct task_struct *t = (struct task_struct *)bpf_get_current_task();
[...]

Examples in situ:
search /examples,
search /tools

8. bpf_log2l()

语法:syscall__SYSCALLNAME

返回所提供值的log-2。这通常用于创建直方图的索引,以构建2的幂直方图。

现场示例:
search /examples,
search /tools

9. bpf_get_prandom_u32()

语法:syscall__SYSCALLNAME

返回伪随机u32。

原位示例:
search /examples,
search /tools

10. bpf_probe_read_user()

语法:syscall__SYSCALLNAME

返回:成功时返回0

这会尝试将大小字节从用户地址空间安全地读取到BPF堆栈,以便BPF稍后可以对其进行操作。为了安全起见,所有用户地址空间内存读取都必须通过bpf_probe_read_user()。

现场示例:
search /examples,
search /tools

11. bpf_probe_read_user_str()

语法:syscall__SYSCALLNAME

退货:

  • >0字符串长度,成功时包括尾随NULL
  • < 0错误

这会将一个以NULL结尾的字符串从用户地址空间复制到BPF堆栈,以便BPF稍后可以对其进行操作。在字符串长度小于size的情况下,不使用另外的NULL字节填充目标。如果字符串长度大于size,则仅复制size - 1字节,并将最后一个字节设置为NULL

现场示例:
search /examples,
search /tools

12. bpf_get_ns_current_pid_tgid()

语法:syscall__SYSCALLNAME

从当前命名空间看到的pid和tgid的值将在nsdata中返回。

成功时返回0,失败时返回以下之一:

  • -EINVAL,如果提供的dev和inum与当前任务的nsfs不匹配dev_t和inode号,或者如果dev转换为dev_t丢失了高位。
  • -ENOENT,如果当前任务不存在pidns。

现场示例:
search /examples,
search /tools

Debugging

1. bpf_override_return()

语法:syscall__SYSCALLNAME

返回:成功时返回0

在附加到函数项kprobe的程序中使用时,将导致
执行要跳过的函数,立即返回rc
This is used for targeted error injection.

bpf_override_return仅在kprobed函数被列入白名单时才起作用
允许错误注入。白名单需要用
ALLOW_ERROR_INJECTION()在内核源代码树中;参见io_ctl_init
举个例子。如果kprobed函数没有被列入白名单,那么bpf程序将
无法与 ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument连接

int kprobe__io_ctl_init(void *ctx) {
	bpf_override_return(ctx, -ENOMEM);
	return 0;
}

Output

1. bpf_trace_printk()

语法:syscall__SYSCALLNAME

返回:成功时返回0

一个简单的内核工具,用于printf()到公共trace_pipe(/sys/kernel/debug/tracing/trace_pipe)。这对于一些快速的示例来说是可以的,但有局限性:最多3个参数,仅1% s,并且trace_pipe是全局共享的,因此并发程序将具有冲突输出。更好的接口是通过BPF_PERF_OUTPUT()。请注意,调用这个helper比原始内核版本更简单,原始内核版本的第二个参数是fmt_size

现场示例:
search /examples,
search /tools

2. BPF_PERF_OUTPUT

语法:syscall__SYSCALLNAME

创建一个BPF表,用于通过性能缓冲区将自定义事件数据推送到用户空间。这是将每个事件的数据推送到用户空间的首选方法。

举例来说:

struct data_t {
    u32 pid;
    u64 ts;
    char comm[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(events);

int hello(struct pt_regs *ctx) {
    struct data_t data = {};

    data.pid = bpf_get_current_pid_tgid();
    data.ts = bpf_ktime_get_ns();
    bpf_get_current_comm(&data.comm, sizeof(data.comm));

    events.perf_submit(ctx, &data, sizeof(data));

    return 0;
}

输出表名为events,数据通过events.perf_submit()推送到该表。

现场示例:
search /examples,
search /tools

3. perf_submit()

语法:syscall__SYSCALLNAME

返回:成功时返回0

BPF_PERF_OUTPUT表的一种方法,用于向用户空间提交自定义事件数据。参见BPF_PERF_OUTPUT条目。(This最终调用bpf_perf_event_output()。

kprobeskretprobes中提供了ctx参数。对于SCHED_CLSSOCKET_FILTER程序,必须使用struct __sk_buff *skb

现场示例:
search /examples,
search /tools

4. perf_submit_skb()

语法:syscall__SYSCALLNAME

返回:成功时返回0

网络程序类型中可用的BPF_PERF_OUTPUT表的一种方法,用于向用户空间提交自定义事件数据沿着数据包缓冲区的前packet_size字节。参见BPF_PERF_OUTPUT条目。(This最终调用bpf_perf_event_output()。

现场示例:
search /examples,
search /tools

5. BPF_RINGBUF_OUTPUT

语法:syscall__SYSCALLNAME

创建一个BPF表,用于通过ringbuf环形缓冲区将自定义事件数据推送到用户空间。
BPF_RINGBUF_OUTPUThas several advantages over BPF_PERF_OUTPUT, summarized as follows:

  • 缓冲区在所有CPU之间共享,这意味着没有每个CPU分配
  • 支持BPF程序的两个API
  • BPF API不需要访问CPU ctx参数
  • 共享的环形缓冲区管理器带来上级的性能和用户空间延迟
  • 支持在用户空间中使用数据的两种方式

从Linux 5.8开始,这应该是将每个事件数据推送到用户空间的首选方法。

两个API的示例:

struct data_t {
    u32 pid;
    u64 ts;
    char comm[TASK_COMM_LEN];
};

// Creates a ringbuf called events with 8 pages of space, shared across all CPUs
BPF_RINGBUF_OUTPUT(events, 8);

int first_api_example(struct pt_regs *ctx) {
    struct data_t data = {};

    data.pid = bpf_get_current_pid_tgid();
    data.ts = bpf_ktime_get_ns();
    bpf_get_current_comm(&data.comm, sizeof(data.comm));

    events.ringbuf_output(&data, sizeof(data), 0 /* flags */);

    return 0;
}

int second_api_example(struct pt_regs *ctx) {
    struct data_t *data = events.ringbuf_reserve(sizeof(struct data_t));
    if (!data) { // Failed to reserve space
        return 1;
    }

    data->pid = bpf_get_current_pid_tgid();
    data->ts = bpf_ktime_get_ns();
    bpf_get_current_comm(&data->comm, sizeof(data->comm));

    events.ringbuf_submit(data, 0 /* flags */);

    return 0;
}

输出表名为events。数据通过events.ringbuf_reserve()分配,并通过events.ringbuf_submit()推送。

现场示例:
search /examples,

6. ringbuf_output()

语法:syscall__SYSCALLNAME

返回:成功时返回0

标志:

  • BPF_RB_NO_WAKEUP:不发送新数据可用性通知
  • BPF_RB_FORCE_WAKEUP:无条件发送新数据可用性通知

BPF_RINGBUF_OUTPUT表的一种方法,用于将自定义事件数据提交到用户空间。这个方法的工作原理类似于perf_submit()
尽管它不需要CTX变元。

现场示例:
search /examples,

7. ringbuf_reserve()

语法:syscall__SYSCALLNAME

返回:成功时指向数据结构的指针,失败时为NULL

BPF_RINGBUF_OUTPUT表的一种方法,用于在环形缓冲区中预留空间,同时
分配用于输出的数据结构。必须与ringbuf_submitringbuf_discard之一配合使用。

现场示例:
search /examples,

8. ringbuf_submit()

语法:syscall__SYSCALLNAME

回报:没事,总是成功

标志:

  • BPF_RB_NO_WAKEUP:不发送新数据可用性通知
  • BPF_RB_FORCE_WAKEUP:无条件发送新数据可用性通知

BPF_RINGBUF_OUTPUT表的一种方法,用于将自定义事件数据提交到用户空间。必须在调用
#1为数据保留空间。

现场示例:
search /examples,

9. ringbuf_discard()

语法:syscall__SYSCALLNAME

回报:没事,总是成功

标志:

  • BPF_RB_NO_WAKEUP:不发送新数据可用性通知
  • BPF_RB_FORCE_WAKEUP:无条件发送新数据可用性通知

BPF_RINGBUF_OUTPUT表的方法,用于丢弃自定义事件数据;用户空间
忽略与被丢弃的事件相关联的数据。必须在调用
#1为数据保留空间。

现场示例:
search /examples,

10. ringbuf_query()

语法:syscall__SYSCALLNAME

返回:请求的值,或者0,如果标志不被识别

标志:

  • BPF_RB_AVAIL_DATA:尚未使用的数据量
  • BPF_RB_RING_SIZE:环形缓冲区的大小
  • BPF_RB_CONS_POS:消费者立场
  • BPF_RB_PROD_POS:制片人职位

BPF_RINGBUF_OUTPUT表的一个方法,用于获取环形缓冲区的各种属性。返回的值是环形缓冲区状态的瞬时快照,并且在帮助器返回时可能关闭,因此这应该仅用于调试/报告原因或用于实现各种启发式方法,这些方法考虑了其中一些特征的高度可变性质。

现场示例:
search /examples,

Maps

映射是BPF数据存储,并且是包括表、散列和直方图在内的更高级别对象类型的基础。

1. BPF_TABLE

语法:syscall__SYSCALLNAME

创建名为_name的贴图。大多数情况下,这将通过更高级别的宏来使用,如BPF_HASH,BPF_ARRAY,BPF_HISTOGRAM等。

BPF_F_TABLE是在最后一个参数中带有标志的变体。BPF_TABLE(...)实际上是BPF_F_TABLE(..., 0 /* flag */)的包装器。

方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。

现场示例:
search /examples,
search /tools

Pinned Maps

语法:syscall__SYSCALLNAME

如果映射不存在,则创建一个新映射,并将其作为FILE固定到bpffs,否则使用固定到bpffs的映射。类型信息不强制执行,实际的贴图类型取决于固定到该位置的贴图。

举例来说:

BPF_TABLE_PINNED("hash", u64, u64, ids, 1024, "/sys/fs/bpf/ids");

2. BPF_HASH

语法:syscall__SYSCALLNAME

创建一个名为name的散列映射(关联数组),并带有可选参数。

默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)

举例来说:

BPF_HASH(start, struct request *);

这将创建一个名为start的散列,其中键是struct request *,值默认为u64。disksnoop.py示例使用此散列来保存每个I/O请求的时间戳,其中键是指向struct请求的指针,值是时间戳。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。

现场示例:
search /examples,
search /tools

3. BPF_ARRAY

这将创建一个名为countswhere的数组,其中包含32个桶和64位整数值。此数组由funccount.py示例用于保存每个函数的调用计数。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.lookup()、map.update()、map.increment()。请注意,所有数组元素都预先分配了零值,因此无法删除。

现场示例:

BPF_ARRAY(counts, u64, 32);

这将创建一个名为countswhere的数组,其中包含32个桶和64位整数值。此数组由funccount.py示例用于保存每个函数的调用计数。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.lookup()、map.update()、map.increment()。请注意,所有数组元素都预先分配了零值,因此无法删除。

现场示例:
search /examples,
search /tools

4. BPF_HISTOGRAM

语法:syscall__SYSCALLNAME

使用可选参数创建名为name的直方图。

默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)

举例来说:

BPF_HISTOGRAM(dist);

这将创建一个名为dist的直方图,它默认为64个由int类型的键索引的桶。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.increment().

现场示例:
search /examples,
search /tools

5. BPF_STACK_TRACE

语法:syscall__SYSCALLNAME

创建名为name的堆栈跟踪映射,并提供最大条目计数。这些映射用于存储堆栈跟踪。

举例来说:

BPF_STACK_TRACE(stack_traces, 1024);

这将创建名为stack_traces的堆栈跟踪映射,最大堆栈跟踪条目数为1024。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.get_stackid().

现场示例:
search /examples,
search /tools

6. BPF_PERF_ARRAY

语法:syscall__SYSCALLNAME

创建名为name的perf数组,并提供最大条目计数,该计数必须等于系统cpu的数量。这些映射用于获取硬件性能计数器。

举例来说:

text="""
BPF_PERF_ARRAY(cpu_cycles, NUM_CPUS);
"""
b = bcc.BPF(text=text, cflags=["-DNUM_CPUS=%d" % multiprocessing.cpu_count()])
b["cpu_cycles"].open_perf_event(b["cpu_cycles"].HW_CPU_CYCLES)

这将创建一个名为cpu_cycles的perf数组,其条目数等于cpu/核心数。这个数组被配置为,稍后调用map.perf_read()将返回一个硬件计算的计数器,表示从过去某个时间点经过的周期数。一次只能为每个表配置一种类型的硬件计数器。

方法(稍后介绍):map.perf_read().

现场示例:
search /tests

7. BPF_PERCPU_HASH

语法:syscall__SYSCALLNAME

创建名为name的NUM_CPU整数索引哈希映射(关联数组),并带有可选参数。每个CPU都将有此阵列的单独副本。副本不会以任何方式保持同步。

请注意,由于内核中定义的限制(在linux/mm/percpu. c中),leaf_type的大小不能超过32 KB。
In other words, BPF_PERCPU_HASHelements cannot be larger than 32KB in size.

默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)

举例来说:

BPF_PERCPU_HASH(start, struct request *);

这将创建名为start的NUM_CPU散列,其中键是struct request *,值默认为u64。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。

现场示例:
search /examples,
search /tools

8. BPF_PERCPU_ARRAY

语法:syscall__SYSCALLNAME

创建NUM_CPU整数索引数组,该数组已针对最快的查找和更新进行优化,名为name,具有可选参数。每个CPU都将有此阵列的单独副本。副本不会以任何方式保持同步。

请注意,由于内核中定义的限制(在linux/mm/percpu. c中),leaf_type的大小不能超过32 KB。
In other words, BPF_PERCPU_HASHelements cannot be larger than 32KB in size.

默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)

举例来说:

BPF_PERCPU_ARRAY(counts, u64, 32);

这将创建名为countswhere的NUM_CPU数组,其中包含32个桶和64位整数值。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.lookup()、map.update()、map.increment()。请注意,所有数组元素都预先分配了零值,因此无法删除。

现场示例:
search /examples,
search /tools

9. BPF_LPM_TRIE

语法:syscall__SYSCALLNAME

创建一个名为name的最长前缀匹配trie映射,并带有可选参数。

默认值:BPF_HASH(name, key_type=u64, leaf_type=u64, size=10240)

举例来说:

BPF_LPM_TRIE(trie, struct key_v6);

这将创建一个名为trie的LPM trie映射,其中键是struct key_v6,值默认为u64。

这是一个包装器宏BPF_F_TABLE("lpm_trie", ..., BPF_F_NO_PREALLOC)

方法(稍后介绍):map.lookup()、map.lookup_or_try_init()、map.delete()、map.update()、map.insert()、map.increment()。

现场示例:
search /examples,
search /tools

10. BPF_PROG_ARRAY

语法:syscall__SYSCALLNAME

这将创建一个名为name的程序数组,其中有size个条目。数组的每个条目要么是bpf程序的文件描述符,要么是NULL。该数组充当跳转表,以便bpf程序可以“尾调用”其他bpf程序。

这是一个用于BPF_TABLE("hash", ...)的包装器宏。

方法(稍后介绍):map.call()。

现场示例:
search /examples,
search /tests,
assign fd

11. BPF_DEVMAP

语法:syscall__SYSCALLNAME

这将创建一个名为name的设备映射,其中包含size个条目。映射的每个条目是到网络接口的ifindex。此贴图仅在XDP中使用。

举例来说:

BPF_DEVMAP(devmap, 10);

方法(稍后介绍):map.redirect_map().

现场示例:
search /examples,

12. BPF_CPUMAP

语法:syscall__SYSCALLNAME

这将创建一个名为name的cpu映射,其中包含size个条目。映射的索引表示CPU id,并且每个条目是为CPU分配的环形缓冲区的大小。此贴图仅在XDP中使用。

举例来说:

BPF_CPUMAP(cpumap, 16);

方法(稍后介绍):map.redirect_map().

现场示例:
search /examples,

13. BPF_XSKMAP

语法:syscall__SYSCALLNAME

这将创建一个名为name的xsk映射,其中包含size个条目,并将其作为FILE固定到bpffs。每个条目表示一个NIC的队列ID。此映射仅在XDP中用于将数据包重定向到AF_XDP套接字。如果AF_XDP套接字绑定到与当前数据包的队列ID不同的队列,则数据包将被丢弃。对于内核v5.3和更高版本,可以使用lookup方法来检查AF_XDP套接字是否可用于当前数据包的队列ID。更多详情请访问AF_XDP

举例来说:

BPF_XSKMAP(xsks_map, 8);

方法(稍后介绍):map.redirect_map(). map.lookup()

现场示例:
search /examples,

14. BPF_ARRAY_OF_MAPS

语法:syscall__SYSCALLNAME

这将创建一个数组映射,其映射类型为映射中映射(BPF_MAP_TYPE_HASH_OF_MAPS),名为name,具有size个条目。内部映射Meta数据由映射inner_map_name提供,并且可以是除了BPF_MAP_TYPE_PROG_ARRAYBPF_MAP_TYPE_CGROUP_STORAGEBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE之外的大多数阵列或散列映射。

举例来说:

BPF_TABLE("hash", int, int, ex1, 1024);
BPF_TABLE("hash", int, int, ex2, 1024);
BPF_ARRAY_OF_MAPS(maps_array, "ex1", 10);

15. BPF_HASH_OF_MAPS

语法:syscall__SYSCALLNAME

这将创建一个散列映射,其映射类型为(BPF_MAP_TYPE_HASH_OF_MAPS)映射,名为name,具有size个条目。内部映射Meta数据由映射inner_map_name提供,并且可以是除了BPF_MAP_TYPE_PROG_ARRAYBPF_MAP_TYPE_CGROUP_STORAGEBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE之外的大多数阵列或散列映射。

举例来说:

BPF_ARRAY(ex1, int, 1024);
BPF_ARRAY(ex2, int, 1024);
BPF_HASH_OF_MAPS(maps_hash, struct custom_key, "ex1", 10);

16. BPF_STACK

语法:syscall__SYSCALLNAME

创建一个名为name的堆栈,值类型为leaf_type,最大条目数为max_entries
Stack and Queue maps are only available from Linux 4.20+.

举例来说:

BPF_STACK(stack, struct event, 10240);

这将创建一个名为stack的堆栈,其中值类型为struct event,最多可容纳10240个条目。

方法(稍后介绍):map.push()、map.pop()、map.peek()。

现场示例:
search /tests,

17. BPF_QUEUE

语法:syscall__SYSCALLNAME

创建一个名为name的队列,值类型为leaf_type,最大条目数为max_entries
Stack and Queue maps are only available from Linux 4.20+.

举例来说:

BPF_QUEUE(queue, struct event, 10240);

这将创建一个名为queue的队列,其中值类型为struct event,最多可容纳10240个条目。

方法(稍后介绍):map.push()、map.pop()、map.peek()。

现场示例:
search /tests,

18. BPF_SOCKHASH

语法:syscall__SYSCALLNAME

创建一个名为name的哈希,并带有可选参数。sockhash仅在Linux 4.18+中可用。

默认值:BPF_SOCKHASH(name, key_type=u32, max_entries=10240)

举例来说:

struct sock_key {
  u32 remote_ip4;
  u32 local_ip4;
  u32 remote_port;
  u32 local_port;
};
BPF_HASH(skh, struct sock_key, 65535);

这将创建一个名为skh的散列,其中键是struct sock_key

sockhash是一种BPF映射类型,它保存对sock结构的引用。然后使用新的sk/msg redirect bpf helper BPF程序可以使用映射在套接字之间重定向skbs/msgs(map.sk_redirect_hash()/map.msg_redirect_hash())。

BPF_SOCKHASHBPF_SOCKMAP之间的区别在于BPF_SOCKMAP是基于数组实现的,并且强制密钥为四个字节。而BPF_SOCKHASH基于哈希表实现,可以自由指定键的类型。

方法(稍后介绍):map.sock_hash_update(),map.msg_redirect_hash(),map.sk_redirect_hash()。

search /tests

19. map.lookup()

语法:syscall__SYSCALLNAME

在映射中查找键,如果存在,则返回指向其值的指针,否则返回NULL。我们把键作为一个地址传递给一个指针。

现场示例:
search /examples,
search /tools

20. map.lookup_or_try_init()

语法:syscall__SYSCALLNAME

在映射中查找键,如果存在,则返回指向其值的指针,否则将键的值初始化为第二个参数。这通常用于将值初始化为零。如果无法插入密钥(例如映射已满)则返回NULL。

现场示例:
search /examples,
search /tools

注意:旧的map.lookup_or_init()可能会导致函数返回,因此建议使用lookup_or_try_init(),因为它没有这种副作用。

21. map.delete()

语法:syscall__SYSCALLNAME

从哈希中删除键。

现场示例:
search /examples,
search /tools

22. map.update()

语法:syscall__SYSCALLNAME

将第二个参数中的值与键关联,覆盖之前的任何值。

现场示例:
search /examples,
search /tools

23. map.insert()

语法:syscall__SYSCALLNAME

将第二个参数中的值关联到键,仅当没有以前的值时。

现场示例:
search /examples,
search /tools

24. map.increment()

语法:syscall__SYSCALLNAME

将键的值按increment_amount递增,默认值为1。用于直方图。

#1不是原子。在并发的情况下。如果您想要更准确的结果,请使用#2而不是#3。map.increment()map.atomic_increment()的开销类似。

注意了。当使用map.atomic_increment()BPF_MAP_TYPE_HASH类型的BPF映射进行操作时,当指定的键不存在时,map.atomic_increment()不能保证操作的原子性。

现场示例:
search /examples,
search /tools

25. map.get_stackid()

语法:syscall__SYSCALLNAME

这将遍历通过ctx中的结构pt_regs找到的堆栈,将其保存在堆栈跟踪映射中,并返回堆栈跟踪的唯一ID。

现场示例:
search /examples,
search /tools

26. map.perf_read()

语法:syscall__SYSCALLNAME

这将返回5中配置的硬件性能计数器。BPF_性能_阵列

现场示例:
search /tests

27. map.call()

语法:syscall__SYSCALLNAME

这将调用bpf_tail_call()来尾调用BPF_PROG_ARRAY中的index条目所指向的bpf程序。尾呼叫不同于正常呼叫。它在跳转到另一个BPF程序后重新使用当前堆栈帧,并且永远不会返回。如果indexentry为空,它不会跳转到任何地方,程序继续正常执行。

举例来说:

BPF_PROG_ARRAY(prog_array, 10);

int tail_call(void *ctx) {
    bpf_trace_printk("Tail-call\n");
    return 0;
}

int do_tail_call(void *ctx) {
    bpf_trace_printk("Original program\n");
    prog_array.call(ctx, 2);
    return 0;
}
b = BPF(src_file="example.c")
tail_fn = b.load_func("tail_call", BPF.KPROBE)
prog_array = b.get_table("prog_array")
prog_array[c_int(2)] = c_int(tail_fn.fd)
b.attach_kprobe(event="some_kprobe_event", fn_name="do_tail_call")

这将tail_call()分配给prog_array[2]。在do_tail_call()结束时,prog_array.call(ctx, 2)尾调用tail_call()并执行它。

注意:为了防止无限循环,尾调用的最大数量为32(MAX_TAIL_CALL_CNT)。

现场示例:
search /examples,
search /tests

28. map.redirect_map()

语法:syscall__SYSCALLNAME

这将基于index条目重定向传入数据包。如果映射为BPF_DEVMAP,则数据包将被发送到入口指向的网络接口的传输队列。如果映射为BPF_CPUMAP,则将该数据包发送到indexCPU的环形缓冲区,并由CPU稍后处理。如果映射是BPF_XSKMAP,则数据包将被发送到附加到队列的AF_XDP套接字。

如果数据包成功重定向,函数将返回XDP_REDIRECT。否则,它将返回XDP_ABORTED以丢弃数据包。

举例来说:

BPF_DEVMAP(devmap, 1);

int redirect_example(struct xdp_md *ctx) {
    return devmap.redirect_map(0, 0);
}
int xdp_dummy(struct xdp_md *ctx) {
    return XDP_PASS;
}
ip = pyroute2.IPRoute()
idx = ip.link_lookup(ifname="eth1")[0]

b = bcc.BPF(src_file="example.c")

devmap = b.get_table("devmap")
devmap[c_uint32(0)] = c_int(idx)

in_fn = b.load_func("redirect_example", BPF.XDP)
out_fn = b.load_func("xdp_dummy", BPF.XDP)
b.attach_xdp("eth0", in_fn, 0)
b.attach_xdp("eth1", out_fn, 0)

Examples in situ:
search /examples,

29. map.push()

语法:syscall__SYSCALLNAME

将元素推入堆栈或队列表。
Passing BPF_EXIST as a flag causes the Queue or Stack to discard the oldest element if it is full.
Returns 0 on success, negative error on failure.

现场示例:
search /tests,

30. map.pop()

语法:syscall__SYSCALLNAME

从Stack或Queue表中弹出元素。*val#
Unlike peeking, popping removes the element.
Returns 0 on success, negative error on failure.

现场示例:
search /tests,

31. map.peek()

语法:syscall__SYSCALLNAME

查看堆栈或队列表头部的元素。*val#
Unlike popping, peeking does not remove the element.
Returns 0 on success, negative error on failure.

现场示例:
search /tests,

32. map.sock_hash_update()

语法:syscall__SYSCALLNAME

向引用套接字的sockhash映射添加条目或更新引用套接字的sockhash映射。skops被用作与键相关联的条目的新值。flags是其中之一:

BPF_NOEXIST: The entry for key must not exist in the map.
BPF_EXIST: The entry for key must already exist in the map.
BPF_ANY: No condition on the existence of the entry for key.

如果映射具有eBPF程序(解析器和判决器),则这些程序将被添加的套接字继承。如果套接字已连接到eBPF程序,则会导致错误。

成功返回0,失败返回负错误。

现场示例:
search /tests,

33. map.msg_redirect_hash()

语法:syscall__SYSCALLNAME

此帮助器用于在套接字级别实现策略的程序中。如果消息msg被允许通过(即如果判决eBPF程序返回SK_PASS),则使用散列键将其重定向到map(类型为BPF_MAP_TYPE_SOCKHASH)引用的套接字。入口和出口接口都可以用于重定向。标志中的BPF_F_INGRESS值用于进行区分(如果标志存在,则选择入口路径,否则选择出口路径)。这是目前唯一支持的标志。

成功时返回SK_PASS,错误时返回SK_DROP。

现场示例:
search /tests,

34. map.sk_redirect_hash()

语法:syscall__SYSCALLNAME

此帮助器用于在skb套接字级别实现策略的程序中。如果sk_buff skb被允许通过(即,如果判决eBPF程序返回SK_PASS),则使用散列键将其重定向到map(类型为BPF_MAP_TYPE_SOCKHASH)引用的套接字。入口和出口接口都可以用于重定向。标志中的BPF_F_INGRESS值用于进行区分(如果标志存在,则选择入口路径,否则选择出口路径)。这是目前唯一支持的标志。

成功时返回SK_PASS,错误时返回SK_DROP。

现场示例:
search /tests,

Licensing

根据所使用的BPF帮助程序,需要GPL兼容许可证。

特殊BCC宏BPF_LICENSE指定BPF程序的许可证。您可以在源代码中将许可证设置为注释,但是内核有一个特殊的接口以编程方式指定它。如果你需要使用纯GPL的帮助器,建议在你的C代码中指定宏,以便内核能够理解它:

// SPDX-License-Identifier: GPL-2.0+
#define BPF_LICENSE GPL

否则,内核可能会拒绝加载程序(请参阅下面的错误描述)。请注意,它支持多个单词,不需要引号:

// SPDX-License-Identifier: GPL-2.0+ OR BSD-2-Clause
#define BPF_LICENSE Dual BSD/GPL

检查BPF助手引用,看看哪些助手是纯GPL的,以及内核认为哪些助手是GPL兼容的。

如果未指定宏,BCC将自动将程序的许可证定义为GPL。

Rewriter

重写器的工作之一是使用内核助手将隐式内存访问转换为显式内存访问。最近的内核引入了一个配置选项ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE,该配置选项将被设置为用户地址空间和内核地址不相交的架构。x86和arm设置了此配置选项,而s390没有。如果未设置ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE,则bpf旧助手bpf_probe_read()将不可用。一些现有用户可能具有隐式内存访问来访问用户内存,因此使用bpf_probe_read_kernel()将导致其应用程序失败。因此,对于非s390,重写器将使用bpf_probe_read()进行这些隐式存储器访问。对于s390,bpf_probe_read_kernel()被用作默认值,并且用户在访问用户存储器时应该显式地使用bpf_probe_read_user()

BPF Errors

请参阅内核源代码中Documentation/networking/filter.txt下的“Understanding eBPFverifier messages”一节。

1. Invalid mem access

当从非GPL BPF程序调用仅GPL帮助程序时,会发生此错误。若要修复此错误,请不要使用专有BPF程序中的仅限GPL的帮助程序,或在GPL兼容许可证下重新许可BPF程序。检查哪些BPF helper是仅限GPL的,以及哪些许可证被认为是GPL兼容的。

从专有程序(bpf_get_stackid())调用#define BPF_LICENSE Proprietary(仅限GPL的BPF帮助程序)的示例:

bpf: Permission denied
0: (bf) r6 = r1
1: (79) r7 = *(u64 *)(r6 +80)
2: (85) call 14
3: (bf) r8 = r0
[...]
23: (69) r1 = *(u16 *)(r7 +16)
R7 invalid mem access 'inv'

Traceback (most recent call last):
  File "./tcpaccept", line 179, in <module>
    b = BPF(text=bpf_text)
  File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 172, in __init__
    self._trace_autoload()
  File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 612, in _trace_autoload
    fn = self.load_func(func_name, BPF.KPROBE)
  File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 212, in load_func
    raise Exception("Failed to load BPF program %s" % func_name)
Exception: Failed to load BPF program kretprobe__inet_csk_accept

2. Cannot call GPL only function from proprietary program

当从非GPL BPF程序调用仅GPL帮助程序时,会发生此错误。若要修复此错误,请不要使用专有BPF程序中的仅限GPL的帮助程序,或在GPL兼容许可证下重新许可BPF程序。检查哪些BPF helper是仅限GPL的,以及哪些许可证被认为是GPL兼容的。

从专有程序(bpf_get_stackid())调用#define BPF_LICENSE Proprietary(仅限GPL的BPF帮助程序)的示例:

bpf: Failed to load program: Invalid argument
[...]
8: (85) call bpf_get_stackid#27
cannot call GPL only function from proprietary program

Environment Variables

1. Kernel source directory

eBPF程序编译需要内核源代码或带有头的内核头编译。如果您的内核源代码位于BCC找不到,则可以向BCC提供位置的绝对路径BCC_KERNEL_SOURCE1##

2. Kernel version overriding

默认情况下,BCC将LINUX_VERSION_CODE存储在生成的eBPF对象中然后在加载eBPF程序时将其沿着到内核。
更新,
例如LTS内核版本。updated such as an LTS kernel release.这不太可能不匹配将导致加载的eBPF程序出现任何问题。
通过设置BCC_LINUX_VERSION_CODE到正在运行的内核版本,检查用于验证内核版本的验证可以被绕过。这是程序所需要的使用kprobes。
这需要以以下格式进行编码:(VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL.例如,如果运行的内核是4.9.10,然后可以设置export BCC_LINUX_VERSION_CODE=264458来覆盖内核版本检查成功。rnel-versions.md#helpers) are GPL-only, and what licenses are considered GPL-compatible.

从专有程序(bpf_get_stackid())调用#define BPF_LICENSE Proprietary(仅限GPL的BPF帮助程序)的示例:

bpf: Failed to load program: Invalid argument
[...]
8: (85) call bpf_get_stackid#27
cannot call GPL only function from proprietary program

Environment Variables

1. Kernel source directory

eBPF程序编译需要内核源代码或带有头的内核头编译。
如果您的内核源代码位于BCC找不到,则可以向BCC提供位置的绝对路径BCC_KERNEL_SOURCE1##

2. Kernel version overriding

默认情况下,BCC将LINUX_VERSION_CODE存储在生成的eBPF对象中然后在加载eBPF程序时将其沿着到内核。
更新,例如LTS内核版本。
updated such as an LTS kernel release.这不太可能不匹配将导致加载的eBPF程序出现任何问题。
通过设置BCC_LINUX_VERSION_CODE到正在运行的内核版本,检查用于验证内核版本的验证可以被绕过。
这是程序所需要的使用kprobes。
这需要以以下格式进行编码:(VERSION * 65536) + (PATCHLEVEL * 256) + SUBLEVEL.例如,如果运行的内核是4.9.10,然后可以设置export BCC_LINUX_VERSION_CODE=264458来覆盖内核版本检查成功。

### 关于 GY-906-BCC标准库驱动程序 #### 1. **GY-906-BCC 基本参数** GY-906-BCC 是一款红外测温模块,其最大测距为 10 cm[^1]。该模块通常用于短距离内的温度检测应用。 #### 2. **硬件组成与功能划分** 根据设计需求,基于 STC89C52 单片机的 GY-906 智能温度计可以分为三个主要部分:数据采集模块、数据分析模块以及 OLED 显示接口模块。其中,数据采集模块的核心组件即为 GY-906-BCC 和 MLX90614 温度传感器。这些设备能够通过内部状态机实现目标物体温差测量并支持 PWM 或 SMBus 输出模式[^2]。 #### 3. **标准库下载地址及相关资源获取方式** 对于寻找 GY-906-BCC 的具体标准库或者配套驱动文件而言,官方文档建议访问如下链接来获得最新版本的支持材料: - 百度云盘共享路径: [https://pan.baidu.com/s/1V4IX0PKG8vDOionICghSTw](https://pan.baidu.com/s/1V4IX0PKG8vDOionICghSTw) 请注意,在实际操作过程中可能还需要额外安装一些依赖项才能顺利运行所提供的固件包中的例子代码片段。 #### 4. **典型应用场景下的编程实例** 以下是利用 C 语言编写的一个简单示例程序,演示如何读取来自 GY-906-BCC 设备的数据并通过串口打印出来: ```c #include <reg52.h> #define uchar unsigned char #define uint unsigned int sbit SCL=P2^7; //定义SMBUS通信协议所需的时钟线引脚位置 sbit SDA=P2^6; //定义SMBUS通信协议所需的数据传输线引脚位置 void delayms(uint z); //延时函数声明 uchar iic_start(); //启动信号发送子程序声明 uchar iic_stop(); //停止信号发送子程序声明 uchar iic_send_byte(uchar dat); //单字节写入子程序声明 uchar iic_read_byte(bit ack); //单字节读取子程序声明 // 主循环逻辑结构体 void main(){ while (1){ uchar temp_high, temp_low; if(iic_start()){ iic_send_byte(0x7E); iic_send_byte(0x07); iic_start(); iic_send_byte(0x7F); temp_high=iic_read_byte(1); temp_low=iic_read_byte(0); iic_stop(); float temperature=(temp_high<<8 | temp_low)*0.02 - 273.15; printf("Current Temperature:%f\n",temperature); } } } // 实现细节省略... ``` 上述代码展示了基本框架下如何初始化 I²C 总线并与指定寄存器交互从而提取当前环境温度数值的过程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值