0%

使用内核线程的Netlink内核模块

使用内核线程的Linux内核的Netlink服务器

  • 代码仓库
  • ./client下是客户目录
  • 根目录下执行make可以编译模块,执行./remake.sh可以一键编译安装
  • 此服务器运行在Ubuntu系统,内核版本为6.5.0-27-generic,基于NETLINK协议
  • 主要实现了基于netlink的通信响应,每次响应的时候都创建一个内核线程(kthread)来执行操作,执行完之后线程自动结束
    • 在内核中,不需要手动的执行wait或者是join等负责回收工作,内核会自动回收内核线程
    • 可以在需要强行停止的时候执行kthread_stop() 函数。该函数会向内核线程发送一个停止请求,并等待线程完全退出。在内核线程函数中,我们可以使用 kthread_should_stop() 函数来检查是否收到了停止请求, 检测到该函数返回值为真的时候退出内核线程的工作函数。
  • 内核创建netlink服务端
    nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
  • 上面注意NETLINK_USER协议号必须跟用户态的程序设置的相同,否则无法通信
  • 发送数据使用nlmsg_new, nlmsg_put两个函数构造信息,使用nlmsg_unicast发送数据(不区分多播组)
  • 接收数据使用回调函数实现
    // 创建netlink socket
    struct netlink_kernel_cfg cfg = {
    .input = <call back function>,
    };
    nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
  • 回调函数会自动传入此时接收到的数据,使用内核提供的宏NLMSG_DATANLMSG_PAYLOAD等可以提取到信息和长度

    用户端的相关操作

  • 创建socksock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  • 绑定后可以通信bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))
  • 发送sendmsg(sock_fd, &msg, 0)
  • 接收recvmsg(sock_fd, &msg_recv, 0)

    /proc下的文件

  • /proc下创建一个文件用于读取内核模块的信息
    proc_entry = proc_create("netlink_stats", 0666, NULL, &proc_file_fops);
  • 处理用户的读取操作
    // proc文件的读取操作
    static ssize_t proc_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) {
    char buffer[128];
    int len;

    len = sprintf(buffer, "Messages received: %d\nTotal bytes: %d\n", message_count, total_bytes);
    return simple_read_from_buffer(user_buf, count, ppos, buffer, len);
    }

    内核线程

  • kthread_run(threadfn, data, namefmt, ...)
  • 第一个函数是一个int(*)(void*)的函数,接收一个void*的参数,返回一个返回码
  • 第二个是传入的参数
  • 第三个是名称

    执行结果

  • picture 0
  • picture 1
  • picture 2