在kernel中,用户层与内核层 如何通信,有那些手段

用户层与内核层通信手段详解 + 从零实现案例 + 实际应用场景

一、用户层与内核层通信的核心手段

Linux 提供了多种用户层(User Space)与内核层(ernel Space)通信的方式,主要包括:

通信方式 通信方向 特点 适用场景

系统调用(Syscall) 用户→内核 同步、高效、标准化 文件操作、进程管理、网络通信

/proc 文件系统 双向(读/写) 虚拟文件接口 查询系统状态、调整内核参数

/sys 文件系统 双向(读/写) 结构化设备管理 设备驱动配置、电源管理

设备文件(/dev) & ioctl 用户→内核 设备控制接口 硬件设备操作(如串口、显卡)

Netlink Socket 双向、异步 基于Socket,支持多播 网络配置、内核事件通知

内存映射(mmap) 双向(共享内存) 零拷贝、高效 大文件访问、进程间通信

二、通信原理 + 从零实现案例

  1. 系统调用(Syscall)

原理

• 用户程序调用 glibc 封装的函数(如 open())。

• glibc 触发 int 0x80(x86)或 syscall 指令(x64),切换到内核态。

• 内核根据系统调用号(eax 寄存器)执行对应服务。

• 结果返回用户态。

从零实现

(1)编写内核模块(添加新系统调用)
// kernel/my_syscall.c
#include <linux/kernel.h>
#include <linux/syscalls.h>

SYSCALL_DEFINE2(my_syscall, int, arg1, char __user *, buf) {
printk(KERN_INFO “my_syscall: arg1=%d\n”, arg1);
if (copy_to_user(buf, “Hello from kernel!”, 20) != 0)
return -EFAULT;
return 0;
}

(2)注册系统调用
• 修改 arch/x86/entry/syscalls/syscall_64.tbl,添加:

548 common my_syscall __x64_sys_my_syscall

• 编译内核并加载模块。

(3)用户层调用
// user_program.c
#include <unistd.h>
#include <sys/syscall.h>

#define MY_SYSCALL_NR 548

int main() {
char buf[20];
long ret = syscall(MY_SYSCALL_NR, 123, buf);
printf(“Kernel says: %s\n”, buf);
return 0;
}

编译运行:
gcc user_program.c -o user_program
./user_program

输出:

Kernel says: Hello from kernel!

  1. /proc 文件系统(procfs)

原理

• /proc 是虚拟文件系统,文件内容由内核动态生成。

• 用户程序通过 cat、echo 或 read()/write() 系统调用交互。

从零实现

(1)内核模块创建 /proc 文件
// kernel/my_proc.c
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static int my_proc_show(struct seq_file *m, void *v) {
seq_printf(m, “Current kernel time: %llu ns\n”, ktime_get_ns());
return 0;
}

static int my_proc_open(struct inode *inode, struct file *file) {
return single_open(file, my_proc_show, NULL);
}

static const struct proc_ops my_proc_fops = {
.proc_open = my_proc_open,
.proc_read = seq_read,
};

static int __init my_proc_init(void) {
proc_create(“my_proc_entry”, 0, NULL, &my_proc_fops);
return 0;
}

static void __exit my_proc_exit(void) {
remove_proc_entry(“my_proc_entry”, NULL);
}

module_init(my_proc_init);
module_exit(my_proc_exit);

(2)加载模块并测试
insmod my_proc.ko
cat /proc/my_proc_entry

输出:

Current kernel time: 169876543210 ns

  1. Netlink Socket(双向通信)

原理

• 类似网络Socket(AF_NETLINK),支持异步、多播通信。

• 内核可主动推送消息给用户层(如设备插拔事件)。

从零实现

(1)内核模块(接收/发送消息)
// kernel/netlink_kernel.c
#include <linux/netlink.h>
#include <net/sock.h>

#define NETLINK_MY_PROTOCOL 31

static struct sock *nl_sk;

static void nl_recv_msg(struct sk_buff *skb) {
struct nlmsghdr *nlh = nlmsg_hdr(skb);
printk(KERN_INFO “Netlink: Received: %s\n”, (char *)NLMSG_DATA(nlh));
}

static int __init nl_init(void) {
struct netlink_kernel_cfg cfg = {
.input = nl_recv_msg,
};
nl_sk = netlink_kernel_create(&init_net, NETLINK_MY_PROTOCOL, &cfg);
return 0;
}

static void __exit nl_exit(void) {
netlink_kernel_release(nl_sk);
}

module_init(nl_init);
module_exit(nl_exit);

(2)用户层程序(发送/接收消息)
// user_netlink.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>

#define NETLINK_MY_PROTOCOL 31

int main() {
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_MY_PROTOCOL);

memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); // 用户进程PID
bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));

memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; // 0表示内核

nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(1024));
nlh->nlmsg_len = NLMSG_SPACE(1024);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello Kernel!");

sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
printf("Sent to kernel: %s\n", (char *)NLMSG_DATA(nlh));

close(sock_fd);
return 0;

}

(3)运行测试
insmod netlink_kernel.ko
gcc user_netlink.c -o user_netlink
./user_netlink

内核日志(dmesg):

Netlink: Received: Hello Kernel!

三、实际应用场景

  1. 系统调用(Syscall)

• 场景:open() 打开文件、fork() 创建进程。

• 案例:strace 工具通过拦截系统调用调试程序。

  1. /proc & /sys

• 场景:

• /proc/cpuinfo:查询CPU信息。

• /sys/class/net/eth0/operstate:检查网卡状态。

• 案例:sysctl -w net.ipv4.ip_forward=1 启用IP转发。

  1. Netlink

• 场景:

• iproute2(ip 命令)配置网络接口。

• udev 监听设备插拔事件(如USB插入)。

• 案例:ip link set eth0 up 通过Netlink启用网卡。

  1. mmap

• 场景:

• 数据库(如Redis)使用 mmap 加速文件访问。

• X Server 共享显存。

总结

场景 推荐方式 原因

高性能系统调用 Syscall 直接、高效

查询/配置内核参数 /proc 或 /sys 标准化文件接口

设备控制 设备文件 + ioctl 硬件专属操作

内核主动通知 Netlink 异步、双向、支持多播

零拷贝大数据传输 mmap 避免内存复制,高效

通过这些方式,用户层和内核层可以安全、高效地交互,满足不同场景的需求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踏马潜行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值