linux | 使用proc节点实现内核↔用户态双向完整通信
时间:2026年7月24日12:03:09
使用 proc 节点实现内核↔用户态双向完整通信
一、核心原理
proc 是 Linux 虚拟文件系统,驱动创建自定义 /proc/xxx 节点,注册read、write 回调:
- 用户态读节点 → 触发内核
proc_read,内核把数据发给用户; - 用户态写节点 → 触发内核
proc_write,用户把数据传给内核; 依靠这两个回调,完成双向数据收发。
两种通信架构(推荐方案 2:一问一答,无数据冲突)
- 单缓冲区单向收发(简易):一个缓冲区存内核下发指令,用户写完应答覆盖;
- 双缓冲区分离收发(标准商用,你业务用):
tx_buf:内核→用户 指令缓冲区(内核写,用户读)rx_buf:用户→内核 应答 / 参数缓冲区(用户写,内核读)
二、完整内核驱动代码(双向通信,支持指令、参数读写)
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/delay.h>
#define PROC_NAME "udisk_comm"
#define BUF_MAX 256
// 双缓冲区:分离内核下发、用户应答,避免覆盖冲突
static char g_kernel_tx[BUF_MAX] = {
0}; // 内核发给用户的指令
static char g_user_rx[BUF_MAX] = {
0}; // 用户发给内核的应答/参数
static struct proc_dir_entry *g_proc_node;
// ===================== 用户态读取节点时触发:内核 → 用户 =====================
static ssize_t proc_read_cb(struct file *file, char __user *usr_buf, size_t len, loff_t *pos)
{
char *send_data = g_kernel_tx;
size_t data_len = strlen(send_data);
// 偏移超过数据长度,无数据返回
if (*pos >= data_len)
return 0;
data_len -= *pos;
if (data_len > len)
data_len = len;
// 内核数据拷贝到用户空间
if (copy_to_user(usr_buf, send_data + *pos, data_len))
return -EFAULT;
*pos += data_len;
return data_len;
}
// ===================== 用户态写入节点时触发:用户 → 内核 =====================
static ssize_t proc_write_cb(struct file *file, const char __user *usr_buf, size_t len, loff_t *pos)
{
char tmp_buf[BUF_MAX] = {
0};
if (len >= BUF_MAX)
len = BUF_MAX - 1;
// 用户空间数据拷贝到内核临时缓存
if (copy_from_user(tmp_buf, usr_buf, len))
return -EFAULT;
tmp_buf[len] = '\0';
// 用户写入的数据存入应答缓冲区,内核可轮询读取
strncpy(g_user_rx, tmp_buf, BUF_MAX - 1);
printk("内核收到用户态数据:%s\n", g_user_rx);
return len;
}
// proc文件操作绑定读写回调
static const struct proc_ops proc_fops = {
.proc_read = proc_read_cb,
.proc_write = proc_write_cb,
};
// 对外API1:内核下发指令给用户态(写tx缓冲区)
void kernel_send_cmd_to_user(const char *cmd_str)
{
memset(g_kernel_tx, 0, sizeof(g_kernel_tx));
strncpy(g_kernel_tx, cmd_str, BUF_MAX - 1);
printk("内核下发指令:%s\n", g_kernel_tx);
}
// 对外API2:内核读取用户态上报的应答数据
int kernel_read_user_reply(char *out_buf, int buf_len)
{
if (strlen(g_user_rx) == 0)
return -ENODATA;
memset(out_buf, 0, buf_len);
strncpy(out_buf, g_user_rx, buf_len - 1);
// 读取完成清空应答缓冲区,等待下一次上报
memset(g_user_rx, 0, sizeof(g_user_rx))


267

被折叠的 条评论
为什么被折叠?



