You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核模块监控系统调用:如何传递日志结构体到用户态?

问题描述

我正在编写一个Linux内核模块,用于监控所有系统调用,并将包含调用进程PID、传递参数等信息的日志结构体发送到用户态程序。尝试过IOCTL、PROCFS、字符设备(READ/WRITE)和SYSFS实现该功能,但遇到关键问题:copy_to_user()函数需要用户态的结构体地址,但我无法为所有系统调用传入该地址。

我不想为所有系统调用都实现copy_to_user(),因为这需要每个系统调用都传入buf参数。以下是我的错误实现示例:

错误代码示例

module.c

asmlinkage long my_sys_read(int fd, char *buf, size_t count)
{
  long ret;
  query_arg_t my_struct_test; // 要传递到用户态的结构体
  my_struct_test.Pid = task_pid_nr(current);
  my_struct_test.syscallName = 2; 
  copy_to_user(buf, my_struct_test, sizeof(query_arg_t)); // 错误:第二个参数应为指针,且不应占用原read的buf
  return ret;
}

user.c

ret = read(devFile, receive_struct , BUFFER_LENGTH);  // 从LKM读取my_sys_read的输出
if (ret < 0){
  perror("user : Failed to read the message from the device.");
  return errno;
}
printf("user : The received message is: [%d]\n", receive_struct.Pid);
可行解决方案

下面是几种不需要修改每个系统调用传参的实现方案,均能解决你的核心问题:

1. 内核环形缓冲区(kfifo)+ 字符设备

这是最常用的内核-用户态异步通信方案,核心思路是内核先暂存日志数据,用户态通过字符设备统一读取:

  • 在内核模块中创建一个环形缓冲区(kfifo),用自旋锁或互斥体保证并发安全;
  • 系统调用钩子函数中,将日志结构体序列化为字节流,写入kfifo;
  • 字符设备的read接口中,调用kfifo_to_user()将缓冲区数据批量拷贝到用户态,无需每个系统调用传入用户态地址。

核心代码片段:

// 内核模块中定义kfifo和锁
static struct kfifo log_fifo;
static DEFINE_SPINLOCK(fifo_lock);

// 系统调用钩子中写入日志
void log_syscall(query_arg_t *log) {
    unsigned long flags;
    spin_lock_irqsave(&fifo_lock, flags);
    // 将结构体写入kfifo
    kfifo_in(&log_fifo, log, sizeof(query_arg_t));
    spin_unlock_irqrestore(&fifo_lock, flags);
}

// 字符设备read函数
ssize_t dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
    return kfifo_to_user(&log_fifo, buf, count, &count);
}

2. Netlink套接字

Netlink是内核与用户态通信的专用机制,支持内核主动向用户态推送消息,适合实时监控场景:

  • 内核模块创建Netlink套接字,用户态程序绑定对应Netlink端口;
  • 系统调用钩子中,填充struct sk_buff封装日志结构体,调用netlink_unicast()发送到用户态;
  • 用户态通过Netlink套接字接收消息,无需轮询。

核心优势:不需要用户态主动发起read请求,内核可以实时推送系统调用日志,避免数据丢失。

3. Tracepoint/Perf事件(轻量级方案)

如果你的监控需求偏向轻量级,可以复用内核的tracepoint机制:

  • 定义自定义trace event,包含日志结构体的所有字段;
  • 系统调用钩子中触发该trace event,内核会自动处理数据传递到用户态;
  • 用户态可以用perf工具直接读取,或编写简单程序通过perf接口获取数据。

这种方案无需自己实现通信逻辑,复用内核现有框架,开发成本低。

关键注意事项
  • 避免在系统调用钩子中做耗时操作:系统调用路径对性能敏感,写入缓冲区或发送Netlink消息要尽量高效;
  • 结构体兼容性:内核与用户态的结构体要保证内存布局一致(比如用__attribute__((packed))避免对齐差异);
  • 并发安全:多CPU核心下,必须用自旋锁(原子操作场景)或互斥体保护共享数据结构。

内容的提问来源于stack exchange,提问作者LINux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:50:26