【linux kernel 常用数据结构和设计模式】【数据结构 3】【模拟input子系统input_dev和input_handler之间的多对多关系】

一、背景

上节内容我们介绍了 内核中 一对一、一对多、多对多 的数据结构, 本节我们参照内核 input 子系统来模拟 input 子系统里
input_devinput_handler 多对多关系 ,用中间对象 input_handle 把两者连起来。

加载模块后,可以在 dmesg 里看到设备和处理器的绑定关系。

二、驱动

2.1 code

c 复制代码
// demo_input_m2m.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>

struct my_input_dev;
struct my_input_handler;
struct my_input_handle;

/* 中间关系对象 */
struct my_input_handle {
    struct my_input_dev *dev;
    struct my_input_handler *handler;
    struct list_head d_node; /* 挂到 dev->h_list */
    struct list_head h_node; /* 挂到 handler->h_list */
};

/* 设备 */
struct my_input_dev {
    const char *name;
    struct list_head h_list; /* 所有关联的 handle */
    struct list_head node;   /* 挂到全局 dev_list */
};

/* 处理器 */
struct my_input_handler {
    const char *name;
    struct list_head h_list; /* 所有关联的 handle */
    struct list_head node;   /* 挂到全局 handler_list */
};

/* 全局链表 */
static LIST_HEAD(dev_list);
static LIST_HEAD(handler_list);

/* 工具函数:创建 handle 并建立关联 */
static void connect_dev_handler(struct my_input_dev *dev,
                                struct my_input_handler *handler)
{
    struct my_input_handle *handle;

    handle = kzalloc(sizeof(*handle), GFP_KERNEL);
    if (!handle)
        return;

    handle->dev = dev;
    handle->handler = handler;

    INIT_LIST_HEAD(&handle->d_node);
    INIT_LIST_HEAD(&handle->h_node);

    /* 双向挂接 */
    list_add(&handle->d_node, &dev->h_list);
    list_add(&handle->h_node, &handler->h_list);

    pr_info("Connect: dev=%s <-> handler=%s\n", dev->name, handler->name);
}

/* 初始化 demo */
static int __init demo_init(void)
{
    struct my_input_dev *kbd, *mouse;
    struct my_input_handler *evdev, *mousedev;
    struct my_input_handle *h;

    pr_info("demo_input_m2m init\n");

    /* 创建设备 */
    kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
    mouse = kzalloc(sizeof(*mouse), GFP_KERNEL);

    kbd->name = "keyboard";
    mouse->name = "mouse";
    INIT_LIST_HEAD(&kbd->h_list);
    INIT_LIST_HEAD(&mouse->h_list);
    list_add(&kbd->node, &dev_list);
    list_add(&mouse->node, &dev_list);

    /* 创建 handler */
    evdev = kzalloc(sizeof(*evdev), GFP_KERNEL);
    mousedev = kzalloc(sizeof(*mousedev), GFP_KERNEL);

    evdev->name = "evdev";
    mousedev->name = "mousedev";
    INIT_LIST_HEAD(&evdev->h_list);
    INIT_LIST_HEAD(&mousedev->h_list);
    list_add(&evdev->node, &handler_list);
    list_add(&mousedev->node, &handler_list);

    /* 建立绑定关系 */
    connect_dev_handler(kbd, evdev);     // keyboard -> evdev
    connect_dev_handler(kbd, mousedev);  // keyboard -> mousedev
    connect_dev_handler(mouse, evdev);   // mouse -> evdev

    /* 打印绑定关系 */
    list_for_each_entry(kbd, &dev_list, node) {
        pr_info("Device %s bound to: ", kbd->name);
        list_for_each_entry(h, &kbd->h_list, d_node) {
            pr_cont("%s ", h->handler->name);
        }
        pr_cont("\n");
    }

    return 0;
}

/* 清理 */
static void __exit demo_exit(void)
{
    struct my_input_dev *d, *d_tmp;
    struct my_input_handler *h, *h_tmp;
    struct my_input_handle *hdl, *hdl_tmp;

    /* 释放 handle */
    list_for_each_entry(d, &dev_list, node) {
        list_for_each_entry_safe(hdl, hdl_tmp, &d->h_list, d_node) {
            list_del(&hdl->d_node);
            list_del(&hdl->h_node);
            kfree(hdl);
        }
    }

    /* 释放 dev */
    list_for_each_entry_safe(d, d_tmp, &dev_list, node) {
        list_del(&d->node);
        kfree(d);
    }

    /* 释放 handler */
    list_for_each_entry_safe(h, h_tmp, &handler_list, node) {
        list_del(&h->node);
        kfree(h);
    }

    pr_info("demo_input_m2m exit\n");
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("leo demo");
MODULE_DESCRIPTION("Demo: input_dev <-> input_handler many-to-many");

2.2 允许

c 复制代码
/test # insmod demo_input_m2m.ko 
[   30.580747] demo_input_m2m init
[   30.581018] Connect: dev=keyboard <-> handler=evdev
[   30.581140] Connect: dev=keyboard <-> handler=mousedev
[   30.581275] Connect: dev=mouse <-> handler=evdev
[   30.581404] Device mouse bound to: evdev 
[   30.581569] Device keyboard bound to: mousedev evdev 
/test # 
/test # 

三、对比 input 子系统的真实实现

  • 真正的 input 子系统用 input_handle 作为中间结构,和上面 demo 一样。

  • input_register_device() 时,遍历所有 handler,看谁能处理这个 dev,就创建 handle 挂上。

  • input_register_handler() 时,也会遍历所有 dev,看谁能被处理,建立 handle。

  • 多对多关系由 双向链表 + 中间对象 管理,支持设备和处理器的 动态添加删除

相关推荐
亚空间仓鼠21 分钟前
OpenEuler系统常用服务(五)
linux·运维·服务器·网络
夏乌_Wx25 分钟前
剑指offer | 2.4数据结构相关题目
数据结构·c++·算法·剑指offer·c/c++
minji...1 小时前
Linux 线程同步与互斥(二) 线程同步,条件变量,pthread_cond_init/wait/signal/broadcast
linux·运维·开发语言·jvm·数据结构·c++
虚伪的空想家1 小时前
k8s集群configmap和secrets备份脚本
linux·容器·kubernetes
the sun342 小时前
从 QEMU 直接启动到 U-Boot 引导:嵌入式 Linux 启动流程的本质差异
linux·运维·服务器
草莓熊Lotso2 小时前
【Linux 线程进阶】进程 vs 线程资源划分 + 线程控制全详解
java·linux·运维·服务器·数据库·c++·mysql
游乐码2 小时前
C#Queue
数据结构·游戏·c#
ShineWinsu2 小时前
对于Linux:文件操作以及文件IO的解析
linux·c++·面试·笔试·io·shell·文件操作
-SGlow-2 小时前
Linux相关概念和易错知识点(52)(基于System V的信号量和消息队列)
linux·运维·服务器
江畔何人初2 小时前
TCP的三次握手与四次挥手
linux·服务器·网络·网络协议·tcp/ip