用户空间与内核通信(二)

这篇具有很好参考价值的文章主要介绍了用户空间与内核通信(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

用户空间与内核通信(二),嵌入式linux,linux,内核空间,用户空间,驱动

文章:用户空间与内核通信(一)介绍了系统调用(System Call),内核模块参数和sysfs,sysctl函数方式进行用户空间和内核空间的访问。本章节我将介绍使用netlink套接字和proc文件系统实现用户空间对内核空间的访问。

netlink套接字

netlink是一种基于socket的通信机制,用于在用户空间与内核空间之间进行小量数据的及时交互。netlink套接字允许用户空间程序与内核空间程序建立连接,并通过发送和接收消息来进行通信。

内核模块:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>

#define NETLINK_USER 31
#define MSG_LEN 1024

struct sock *nl_sock = NULL;

// 接收用户空间消息的回调函数
static void nl_recv_msg(struct sk_buff *skb) {
    struct nlmsghdr *nlh;
    char msg[MSG_LEN];

    // 从skb中获取消息头
    nlh = nlmsg_hdr(skb);
    if (nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
        netlink_skb_valid(skb, nlh->nlmsg_len)) {
        // 拷贝消息数据到msg缓冲区
        strncpy(msg, NLMSG_DATA(nlh), MSG_LEN);
        // 打印接收到的消息
        printk(KERN_INFO "Received message from user space: %s\n", msg);
    }
}

// 模块初始化函数
static int __init init_netlink(void) {
    // 创建Netlink套接字
    nl_sock = netlink_kernel_create(&init_net, NETLINK_USER, 0, nl_recv_msg, NULL, THIS_MODULE);
    if (!nl_sock) {
        printk(KERN_ERR "Failed to create netlink socket\n");
        return -1;
    }
    printk(KERN_INFO "Netlink socket created\n");
    return 0;
}

// 模块退出函数
static void __exit exit_netlink(void) {
    if (nl_sock)
        netlink_kernel_release(nl_sock);
    printk(KERN_INFO "Netlink socket released\n");
}

module_init(init_netlink);
module_exit(exit_netlink);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");


用户空间应用程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>

#define NETLINK_USER 31
#define MSG_LEN 1024

int main() {
    int sockfd;
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    struct msghdr msg;

    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sockfd < 0) {
        perror("socket");
        return -1;
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();  // This is the source's port number

    bind(sockfd, (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;  // To kernel
    dest_addr.nl_groups = 0;

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MSG_LEN));
    memset(nlh, 0, NLMSG_SPACE(MSG_LEN));
    nlh->nlmsg_len = NLMSG_SPACE(MSG_LEN);
    nlh->nlmsg_pid = getpid();
    nlh->nlmsg_flags = 0;
    strcpy(NLMSG_DATA(nlh), "Hello from user space");

    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    memset(&msg, 0, sizeof(msg));
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    sendmsg(sockfd, &msg, 0);

    close(sockfd);
    free(nlh);

    return 0;
}

编译内核模块:

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

加载内核模块:

sudo insmod your_module.ko

编译用户空间程序:

gcc user_program.c -o user_program

运行用户空间程序:

sudo ./user_program

proc文件系统

proc是一个虚拟文件系统,用于导出内核和进程的状态信息。用户空间程序可以通过读取proc文件系统中的文件来获取内核和进程的信息,也可以通过写入proc文件来向内核发送指令或修改配置。

这些机制为用户空间与内核空间之间的通信提供了灵活和多样化的方式,使得用户程序能够与操作系统内核进行交互,获取系统服务并完成各种任务。

下面的示例,演示了如何使用proc文件系统在用户空间和内核空间之间进行通信。在此示例中,我们将创建一个proc文件,并使用它来向内核发送和接收数据。

首先,让我们创建一个名为"proc_example"的内核模块,该模块将在/proc目录下创建一个名为"proc_example"的文件。用户可以通过读取和写入此文件来与内核通信。

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

#define PROC_ENTRY_NAME "proc_example"
#define BUFFER_SIZE 1024

static struct proc_dir_entry *proc_entry;
static char proc_buffer[BUFFER_SIZE];

// 读取proc文件的回调函数
static ssize_t proc_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) {
    ssize_t len = strlen(proc_buffer);
    if (*offset >= len) {
        return 0; // 已经读取完毕
    }
    if (count > len - *offset) {
        count = len - *offset; // 如果请求的数据超过了剩余的数据量,则只读取剩余的数据量
    }
    if (copy_to_user(buffer, proc_buffer + *offset, count) != 0) {
        return -EFAULT; // 复制数据失败
    }
    *offset += count; // 更新偏移量
    return count;
}

// 写入proc文件的回调函数
static ssize_t proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *offset) {
    if (count >= BUFFER_SIZE) {
        return -EINVAL; // 写入的数据过大
    }
    if (copy_from_user(proc_buffer, buffer, count) != 0) {
        return -EFAULT; // 复制数据失败
    }
    proc_buffer[count] = '\0'; // 添加字符串结束符
    return count;
}

// 模块初始化函数
static int __init init_proc_example(void) {
    proc_entry = proc_create(PROC_ENTRY_NAME, 0666, NULL, &proc_fops);
    if (!proc_entry) {
        printk(KERN_ERR "Failed to create /proc/%s\n", PROC_ENTRY_NAME);
        return -ENOMEM;
    }
    printk(KERN_INFO "/proc/%s created\n", PROC_ENTRY_NAME);
    return 0;
}

// 模块退出函数
static void __exit exit_proc_example(void) {
    if (proc_entry) {
        remove_proc_entry(PROC_ENTRY_NAME, NULL);
        printk(KERN_INFO "/proc/%s removed\n", PROC_ENTRY_NAME);
    }
}

// 定义proc文件操作结构体
static const struct file_operations proc_fops = {
    .read = proc_read,
    .write = proc_write,
};

module_init(init_proc_example);
module_exit(exit_proc_example);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");

编译和加载模块后,你可以在/sys/kernel/debug/proc_example中读取和写入数据。例如,你可以使用cat和echo命令:文章来源地址https://www.toymoban.com/news/detail-831082.html

$ echo "Hello from user space" > /proc/proc_example
$ cat /proc/proc_example
Hello from user space

到了这里,关于用户空间与内核通信(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【嵌入式Linux内核驱动】05_IIC子系统 | 硬件原理与常见面试问题 | 应用编程 | 内核驱动 | 总体框架

    1.1 IIC 基础 IIC协议简介—学习笔记_iic标准协议_越吃越胖的黄的博客-CSDN博客 I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器、传感器、存储器和其他外设。 I2C使用两条线(SDA和SCL)进行通信,可以连接多个设备,每个设备都有一个唯一的地址。I2C总线上的

    2024年02月09日
    浏览(59)
  • 【嵌入式Linux内核驱动】04_Jetson nano GPIO应用 | 驱动开发 | 官方gpiolib、设备树与chip_driver

    0.暴露给应用层 应用 解决调试目录为空的问题 调试信息 1.最简读写文件(在/SYS下) 设备树 验证测试 编译文件 驱动 of_get_named_gpio_flags //获取设备树节点的属性 gpio_is_valid //判断是否合法 devm_gpio_request //申请使用gpio,并调用设置pinctrl device_create_file //根据设备树节点属性,创建

    2024年02月07日
    浏览(58)
  • 嵌入式内核及驱动开发高级

    仅devfs,导致开发不方便以及一些功能难以支持: 热插拔 不支持一些针对所有设备的统一操作(如电源管理) 不能自动mknod 用户查看不了设备信息 设备信息硬编码,导致驱动代码通用性差,即没有分离设备和驱动 uevent机制:sysfs + uevent + udevd(上层app) sysfs用途:(类似于

    2024年02月16日
    浏览(61)
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第七天-内核函数接口(物联技术666)

    链接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688 提取码:1688 //************************************************** #include linux/module.h    /*module_init()*/ #include linux/kernel.h        /* printk() */ #include linux/init.h            /* __init __exit */ #include linux/fs.h              /* file_opera

    2024年02月22日
    浏览(64)
  • 嵌入式开发之linux内核移植

    目录  前言 一、下载内核源码 1.1 下载linux-3.0.1 1.2 解压源码文件 二、 内核添加yaffs2文件系统支持 2.1 下载yaffs2 2.2 内核添加yaffs2文件补丁 三、配置开发板 3.1 修改机器ID 3.2 添加开发板初始化文件 3.3 配置NandFalsh 3.3.1 添加NandFlash设备 3.3.2 添加NandFlash驱动 3.3 修改Kconfig(支持

    2024年02月07日
    浏览(99)
  • 嵌入式Linux底层系统开发 +系统移植+内核文件系统(基础)

    搭建交叉编译开发环境 bootloader的选择和移植 kernel的配置、编译、移植和调试 根文件系统的制作 前两个要点通常芯片厂家提供。后边两个要点是公司的工作重点。 学习方法:先整体后局部,层层推进 如何编译—如何添加命令和功能—如何定义自己的开发板。 移植的基本步

    2024年02月03日
    浏览(67)
  • 修改嵌入式 ARM Linux 内核映像中的文件系统

    zImage 是编译内核后在 arch/arm/boot 目录下生成的一个已经压缩过的内核映像。通常我们不会使用编译生成的原始内核映像 vmlinux ,因其体积很大。因此, zImage 是我们最常见的内核二进制,可以直接嵌入到固件,也可以直接使用 qemu 进行调试。当然,在 32 位嵌入式领域还能见到

    2024年02月10日
    浏览(77)
  • 【嵌入式Linux】编译应用和ko内核模块Makefile使用记录

    在Makefile中,变量的赋值可以使用以下几种方式: = :最基本的赋值符号,表示简单的延迟展开(lazy expansion)方式。变量的值将会在使用变量的时候进行展开。 := :立即展开(immediate expansion)的赋值方式。变量的值在赋值的时候立即展开,并且在后续的使用中不再改变。

    2024年02月08日
    浏览(50)
  • 使用VSCode clangd插件进行linux内核代码阅读和嵌入式开发

    在进行 Linux 内核代码阅读和嵌入式开发时,选择合适的开发工具至关重要。VSCode 是一个流行的跨平台编辑器,并且它的扩展生态系统非常强大。在这篇博客中,我们将介绍如何使用 VSCode Clangd 插件来提高 Linux 内核代码的阅读和嵌入式开发效率。 Clangd 是一个基于 Clang 的语言

    2024年02月09日
    浏览(49)
  • 正点原子嵌入式linux驱动开发——Linux WIFI驱动

    WIFI的使用已经很常见了,手机、平板、汽车等等,虽然可以使用有线网络,但是有时候很多设备存在布线困难的情况,此时WIFI就是一个不错的选择。 正点原子STM32MP1开发板支持USB和SDIO这两种接口的WIFI ,本章就来学习一下如何在STM32MP1开发板上使用USB和SDIO这两种WIFI。 正点原

    2024年02月05日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包