Relay 是 Linux 核心空間向使用者空間高效、可靠傳輸大量資料的關鍵機制。透過使用者定義的 relay 通道,核心模組能以極低開銷將除錯、追蹤等海量資料轉發給使用者空間程式處理。本文系統梳理 Relay 的演進歷程、核心架構、主要 API 並提供完整程式範例,助您快速掌握這項核心級資料傳輸技術。
核心程式開發者長期面臨一個棘手問題:如何將核心空間產生的大量除錯、效能分析資料高效傳至使用者空間?傳統 printk() 效能低落、緩衝有限;若各子系統各自開發緩存傳輸代碼,將造成嚴重程式碼冗餘與維護負擔。這迫切需要一套獨立於具體子系統、高效可靠的核心至使用者空間資料轉發系統——Relay 應運而生。
fs/ 移至 kernel/relay.c,更名為 Relay,成為核心內建基礎設施。一個 Relay 通道由一組與 CPU 一一對應的核心緩衝區組成。每個 CPU 上的程式呼叫 relay_write() 將資料寫入專屬緩衝區,避免鎖競爭。使用者空間視角下,每個緩衝區映射為一個普通檔案,支援 read()、mmap()、sendfile() 等標準操作。Relay 將寫入與讀取解耦,核心端高速寫入不受使用者空間讀取速度限制,大幅提升突發資料處理能力。
open() / close():開啟/關閉通道緩衝區檔案mmap():將整個緩衝區映射至使用者位址空間(必須完整映射)read():消費式讀取,資料讀出即移除sendfile():零拷貝直接輸出至其他檔案描述符poll():支援 POLLIN/POLLRDNORM 事件通知relay_open():建立通道與每 CPU 緩衝區relay_close():關閉通道,確保資料不遺失relay_write():寫入當前 CPU 緩衝區(支援中斷上下文)relay_reserve():預留連續空間供直接寫入(高效能場景)/* hello-mod.c */
#include <linux/module.h>
#include <linux/relay.h>
static struct rchan *chan;
int init_module(void) {
const char *msg = "Hello from kernel\n";
chan = relay_open("demo", NULL, 8192, 2, NULL);
if (!chan) return -ENOMEM;
relay_write(chan, msg, strlen(msg));
return 0;
}
void cleanup_module(void) {
if (chan) relay_close(chan);
}
MODULE_LICENSE("GPL");
/* audience.c */
#include <sys/mount.h> #include <fcntl.h> #include <stdio.h>
#define BASE "/mnt/relay/cpu"
int main() {
if (mount("relayfs", "/mnt/relay", "relayfs", 0, NULL) && errno != EBUSY) return 1;
int cpus = sysconf(_SC_NPROCESSORS_ONLN);
for (int i = 0; i < cpus; i++) {
char path[64]; sprintf(path, "%s%d", BASE, i);
int fd = open(path, O_RDONLY); if (fd < 0) continue;
char buf[256]; int n;
while ((n = read(fd, buf, sizeof(buf)-1)) > 0) {
buf[n] = 0; puts(buf);
}
close(fd);
}
umount("/mnt/relay");
return 0;
}
Relay 以 per-CPU 緩衝區、寫讀分離、標準檔案介面三大設計,解決了核心大量資料高效轉發的核心難題。掌握其 API 與運作機制,對開發核心除錯工具、效能分析框架、嵌入式系統日誌收集具有重要實戰價值。