Tracefs 環形緩衝區記憶體對映

作者:

Vincent Donnefort <vdonnefort@google.com>

概述

Tracefs 環形緩衝區記憶體對映提供了一種高效的資料流傳輸方法,因為不需要記憶體複製。對映環形緩衝區的應用程式因此成為該環形緩衝區的消費者,類似於 trace_pipe。

記憶體對映設定

該對映透過 mmap() trace_pipe_raw 介面來實現。

對映的第一個系統頁面包含環形緩衝區統計資訊和描述。 它被稱為元頁面。 元頁面最重要的欄位之一是讀取器。 它包含可由對映器安全讀取的子緩衝區 ID(參見 無鎖環形緩衝區設計)。

元頁面之後是所有子緩衝區,按升序 ID 排序。 因此,很容易知道讀取器在對映中的起始位置

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

當應用程式完成當前讀取器的操作後,可以使用 trace_pipe_raw ioctl() TRACE_MMAP_IOCTL_GET_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;
}