Tracefs 环形缓冲区内存映射

作者:

Vincent Donnefort <vdonnefort@google.com>

概述

Tracefs 环形缓冲区内存映射提供了一种高效的数据流式传输方法,因为不需要内存复制。映射环形缓冲区的应用程序然后成为该环形缓冲区的消费者,类似于 trace_pipe。

内存映射设置

映射通过 mmap() trace_pipe_raw 接口工作。

映射的第一个系统页面包含环形缓冲区统计信息和描述。它被称为元页面。元页面最重要的字段之一是 reader。它包含映射器可以安全读取的子缓冲区 ID(请参阅无锁环形缓冲区设计)。

元页面之后是所有子缓冲区,按升序 ID 排序。因此,很容易知道 reader 在映射中的起始位置

reader_id = meta->reader->id;
reader_offset = meta->meta_page_size + reader_id * meta->subbuf_size;

当应用程序完成当前的 reader 时,它可以使用 trace_pipe_raw ioctl() TRACE_MMAP_IOCTL_GET_READER 获取新的 reader。此 ioctl 还会更新元页面字段。

限制

当 Tracefs 环形缓冲区上存在映射时,无法调整其大小(通过增加环形缓冲区的总大小或每个子缓冲区的大小)。也无法使用快照,并且会导致 splice 复制环形缓冲区数据,而不是使用环形缓冲区的无复制交换。

允许并发读取器(另一个应用程序映射该环形缓冲区或内核使用 trace_pipe),但不建议这样做。它们将竞争环形缓冲区,并且输出是不可预测的,就像 trace_pipe 上的并发读取器一样。

示例

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <linux/trace_mmap.h>

#include <sys/mman.h>
#include <sys/ioctl.h>

#define TRACE_PIPE_RAW "/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw"

int main(void)
{
        int page_size = getpagesize(), fd, reader_id;
        unsigned long meta_len, data_len;
        struct trace_buffer_meta *meta;
        void *map, *reader, *data;

        fd = open(TRACE_PIPE_RAW, O_RDONLY | O_NONBLOCK);
        if (fd < 0)
                exit(EXIT_FAILURE);

        map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
        if (map == MAP_FAILED)
                exit(EXIT_FAILURE);

        meta = (struct trace_buffer_meta *)map;
        meta_len = meta->meta_page_size;

        printf("entries:        %llu\n", meta->entries);
        printf("overrun:        %llu\n", meta->overrun);
        printf("read:           %llu\n", meta->read);
        printf("nr_subbufs:     %u\n", meta->nr_subbufs);

        data_len = meta->subbuf_size * meta->nr_subbufs;
        data = mmap(NULL, data_len, PROT_READ, MAP_SHARED, fd, meta_len);
        if (data == MAP_FAILED)
                exit(EXIT_FAILURE);

        if (ioctl(fd, TRACE_MMAP_IOCTL_GET_READER) < 0)
                exit(EXIT_FAILURE);

        reader_id = meta->reader.id;
        reader = data + meta->subbuf_size * reader_id;

        printf("Current reader address: %p\n", reader);

        munmap(data, data_len);
        munmap(meta, meta_len);
        close (fd);

        return 0;
}