Skip to main content

内核模块与用户空间交互方式


1. 概览

内核模块与用户空间程序的交互,本质是通过 Linux 提供的统一抽象接口完成的,核心目标包括:

  1. 用户向内核传递控制与配置
  2. 内核向用户暴露状态与事件
  3. 内核与用户进行高性能数据交换

Linux 提供了多种交互机制,每种机制适用于不同场景。

交互方式类型主要用途
procfs文件系统抽象调试 / 状态展示
sysfs文件系统抽象设备属性 / 参数配置
字符设备设备抽象流式数据
ioctl设备抽象控制指令
netlinkSocket 抽象系统级通信
mmap内存抽象零拷贝共享
eBPF map现代内核机制现代安全交互

2. procfs 方式

2.1 原理说明

procfs 是内核暴露内部状态的一种虚拟文件系统:

  • 不存在真实磁盘文件
  • read/write 触发内核回调函数
  • 主要用于状态展示与调试

2.2 数据流模型

用户态 read/write → VFS → proc_ops → 内核函数


2.3 Demo

2.3.1 内核模块代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>

#define PROC_NAME "my_proc"

static ssize_t my_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
return simple_read_from_buffer(buf, count, ppos,
"hello from kernel\n", 18);
}

static const struct proc_ops ops = {
.proc_read = my_read,
};

static int __init my_init(void)
{
proc_create(PROC_NAME, 0444, NULL, &ops);
return 0;
}

static void __exit my_exit(void)
{
remove_proc_entry(PROC_NAME, NULL);
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

2.3.2 用户空间访问

cat /proc/my_proc

2.4 适用场景

  • 调试接口
  • 临时状态输出
  • 教学与实验

3. sysfs 方式

3.1 原理说明

sysfs 以 kobject 为核心,每个文件代表一个内核对象属性。

适合场景:

  • 配置参数
  • 开关控制

3.2 数据模型

kobject → attribute → sysfs file


3.3 Demo

3.3.1 内核模块代码

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

static int my_value;

static ssize_t value_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", my_value);
}

static ssize_t value_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
kstrtoint(buf, 10, &my_value);
return count;
}

static struct kobj_attribute value_attr =
__ATTR(value, 0664, value_show, value_store);

static struct kobject *my_kobj;

static int __init my_init(void)
{
my_kobj = kobject_create_and_add("my_sysfs", kernel_kobj);
sysfs_create_file(my_kobj, &value_attr.attr);
return 0;
}

static void __exit my_exit(void)
{
kobject_put(my_kobj);
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

3.3.2 用户空间访问

echo 42 > /sys/kernel/my_sysfs/value
cat /sys/kernel/my_sysfs/value

3.3 适用场景

  • 模块参数
  • 设备配置
  • 功能开关

4. 字符设备 read/write

4.1 原理说明

通过 VFS 抽象设备为文件( /dev/xxx 暴露设备文件,支持流式读写):

  • open/read/write
  • file_operations 回调

4.2 Demo

4.2.1 内核模块代码

#include <linux/module.h>
#include <linux/fs.h>

#define DEV_NAME "mydev"
static int major;

static ssize_t my_read(struct file *f, char __user *buf,
size_t len, loff_t *off)
{
return simple_read_from_buffer(buf, len, off,
"hello dev\n", 10);
}

static struct file_operations fops = {
.read = my_read,
};

static int __init my_init(void)
{
major = register_chrdev(0, DEV_NAME, &fops);
return 0;
}

static void __exit my_exit(void)
{
unregister_chrdev(major, DEV_NAME);
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

4.2.2 用户空间访问

mknod /dev/mydev c <major> 0
cat /dev/mydev

5. ioctl 控制接口

5.1 原理说明

ioctl 用于设备控制命令扩展。


5.2 Demo

5.2.1 公共头文件

#define MY_IOCTL_SET _IOW('k', 1, int)

5.2.2 内核模块核心代码

static long my_ioctl(struct file *f,
unsigned int cmd,
unsigned long arg)
{
int val;
switch (cmd) {
case MY_IOCTL_SET:
copy_from_user(&val, (int __user *)arg, sizeof(val));
printk("val=%d\n", val);
break;
}
return 0;
}

5.2.3 用户空间调用

int val = 100;
ioctl(fd, MY_IOCTL_SET, &val);

6.1 原理说明

Netlink 是内核与用户空间的消息总线,支持异步通信和多播。


6.2 Demo

6.2.1 内核模块

#include <net/sock.h>
#include <linux/netlink.h>

#define NETLINK_TEST 31
static struct sock *nl_sk;

static int __init my_init(void)
{
nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, NULL);
return 0;
}

6.2.2 用户空间

socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);

7. mmap 共享内存

7.1 原理说明

用户态映射内核内存页:

  • 零拷贝
  • 高性能
  • 共享地址空间

7.2 Demo

7.2.1 内核模块核心逻辑

static int my_mmap(struct file *f, struct vm_area_struct *vma)
{
return remap_pfn_range(vma,
vma->vm_start,
virt_to_phys(buffer) >> PAGE_SHIFT,
PAGE_SIZE,
vma->vm_page_prot);
}

7.2.2 用户空间

char *p = mmap(NULL, PAGE_SIZE,
PROT_READ, MAP_SHARED, fd, 0);

8. eBPF 方式

8.1 原理说明

eBPF 允许在不加载内核模块的情况下安全扩展内核行为。


8.2 Demo

8.2.1 BPF Map 定义

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
} my_map SEC(".maps");

8.2.2 用户空间访问

bpf_map_lookup_elem(fd, &key, &value);

9. 选型建议

需求类型推荐方式
参数配置sysfs
设备控制ioctl
状态输出procfs
异步事件netlink
大数据mmap
安全扩展eBPF

10. 总结

内核与用户空间通信机制的演进路径为:

procfs → sysfs/ioctl → netlink → mmap → eBPF

体现了三个趋势:

  1. 接口结构化
  2. 通信事件化
  3. 数据零拷贝化

最终目标是:

高性能、安全性、可维护性、系统级可观测性