字符设备驱动(内核态用户态内存交互)

news2024/11/26 2:42:49

前言

内核驱动:运行在内核态的动态模块,遵循内核模块框架接口,更倾向于插件。
应用程序:运行在用户态的进程。
应用程序与内核驱动交互通过既定接口,内核态和用户态访问依然遵循内核既定接口。

环境搭建

系统:openEuler-20.03-LTS-SP3

yum install gcc kernel-devel

编写源码

  • char_module.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/device.h> //下面这三个头文件是由于动态创建需要加的
#include <linux/device.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

#define DEVICE_NAME "char_module"
#define BUF_SIZE 32

static struct class *cdev_class;
dev_t dev_num = 0; // 这里是动态分配设备号和动态创建设备结点需要用到的
struct cdev dev_c;

static char context_buf[BUF_SIZE]={"this a test context buffer\0"};

static ssize_t read(struct file *, char *, size_t, loff_t *);
static ssize_t write(struct file *, const char *, size_t, loff_t *);
static int open(struct inode *, struct file *);
static int release(struct inode *, struct file *);

// 初始化字符设备驱动的 file_operations 结构体
struct file_operations fops = {
        .read = read,
        .write = write,
        .open = open,
        .release = release
};

static int __init demo_init(void)
{
        int ret, err;

        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);

        // 注册设备驱动
        ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); // 动态分配设备号
        if (ret)
        {
                printk("demo_init register failure\n");
                unregister_chrdev_region(dev_num, 1);
                return ret;
        }
        printk("demo_init register success\n");

        // 初始化设备操作
        cdev_init(&dev_c, &fops);
        err = cdev_add(&dev_c, dev_num, 1);
        if (err)
        {
                printk(KERN_NOTICE "error %d adding cdev\n", err);
                unregister_chrdev_region(dev_num, 1);
                return err;
        }

        // 动态创建设备结点
        cdev_class = class_create(THIS_MODULE, DEVICE_NAME); 
        if (IS_ERR(cdev_class))
        {
                printk("ERR:cannot create a cdev_class\n");
                unregister_chrdev_region(dev_num, 1);
                return -1;
        }
        device_create(cdev_class, NULL, dev_num, 0, DEVICE_NAME);

        return ret;
}

static void __exit demo_exit(void)
{
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);

        // 注销设备驱动
        device_destroy(cdev_class, dev_num);
        class_destroy(cdev_class);
        unregister_chrdev_region(dev_num, 1);
}

static ssize_t read(struct file *filp, char *buf, size_t len, loff_t *off)
{
        // 内核空间到用户空间copy
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);
        if (raw_copy_to_user(buf, &context_buf, sizeof(context_buf)))
        {
                return -EFAULT;
        }
        printk(KERN_INFO "user space: %pF", buf);
        printk(KERN_INFO "read: %pF; size: %ld; data: %s", &context_buf, sizeof(context_buf), context_buf);
        return BUF_SIZE;
}

static ssize_t write (struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
        // 用户空间到内核空间copy
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);
        if (raw_copy_from_user(&context_buf, buf, sizeof(context_buf)))
        {
                return -EFAULT;
        }
        printk(KERN_INFO "user space: %pF", buf);
        printk(KERN_INFO "write: %pF; size: %ld; data: %s", &context_buf, sizeof(context_buf), context_buf);
        return BUF_SIZE;
}

static int open(struct inode *inodp, struct file *filp)
{
        printk(KERN_INFO "%s: %s", DEVICE_NAME , __func__);
        return 0;
}

static int release(struct inode *inodp, struct file *filp)
{
        printk(KERN_INFO "%s: %s", DEVICE_NAME, __func__);
        return 0;
}

module_init(demo_init);
module_exit(demo_exit);
  • Makefile
ifneq ($(KERNELRELEASE),)
obj-m := char_module.o

else
PWD  := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules modules_install
clean:
        rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.*  Module.*
endif

app.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
 
#define CHAR_DEV_NAME "/dev/char_module"

int main()
{
        int ret;
        int fd;
        char buf[32];
 
        fd = open(CHAR_DEV_NAME, O_RDWR | O_NDELAY);
        if(fd < 0)
        {
                printf("open failed!\n");
                return -1;
        }

        int size = read(fd, buf, 32);

        printf("read size: %d;\nbuffer:[%s]\n", size, buf);

        char *write_buf = "use a application wirte to driver buffer";

        int w_size = write(fd, write_buf, strlen(write_buf));

        printf("write size: %d;\nbuffer:[%s]\n", w_size, write_buf);

        close(fd);

        return 0;
}

构建并测试

  • 驱动构建
    make && insmod char_module.ko
    
  • 驱动信息确认
    在这里插入图片描述
  • 应用程序构建
    gcc app.c -o app
    ./app
    
  • 应用程序运行结果
    在这里插入图片描述
  • 查看驱动日志
    dmesg
    
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/941131.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安防监控视频平台EasyCVR视频汇聚平台调用接口出现跨域现象的问题解决方案

视频监控汇聚EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视…

科技云报道:软件供应链安全如此重要,但为什么难以解决?

科技云报道原创。 软件供应链安全如今已经成了一个世界性难题。从2021年底Apache Log4j“核弹级”风险爆发&#xff0c;时至今日影响仍然存在&#xff0c;保障软件供应链安全已成为业界关注焦点。 但近2年时间过去了&#xff0c;软件供应链安全问题似乎并没有得以缓解&#x…

微服务事务管理(Dubbo)

Seata 是什么 Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 一、示例架构说明 可在此查看本示例完整代码地址&#x…

FreeSWITCH 1.10.10 简单图形化界面6 - 配置讯时网关落地

FreeSWITCH 1.10.10 简单图形化界面6 - 配置讯时网关落地 0、 界面预览1、 创建一个话务台2、 创建PBX SIP中继并设置呼入权限3、 设置呼叫权限4、 设置分机呼出权限5、 设置FXO 网关相关信息6、 设置FXO网关呼叫路由&#xff08;呼入及呼出&#xff09;7、 查看SIP中继状态 0、…

研磨设计模式day15策略模式

场景 问题描述 经常会有这样的需要&#xff0c;在不同的时候&#xff0c;要使用不同的计算方式。 解决方案 策略模式 定义&#xff1a; 解决思路&#xff1a;

HEGERLS智能四向穿梭车是如何解决机械制造领域内SKU种类复杂且量多的问题?

伴随着电子商务和智能制造技术的快速发展&#xff0c;对于自动化立体仓库系统、密集存储系统、自动输送系统、自动识别系统、无线通讯系统、条码扫描、手持终端及其系统集成的需求急剧增加&#xff0c;物流装备系统密集化、自动化、智能化、绿色环保等技术特征日益明显。密集存…

简单的springboot应用部署后内存占用量过大问题排查

1.问题背景 需要部署一个演示环境。所有组件都要部署到一台服务器&#xff0c;采用Docker容器部署&#xff0c;发现多个简单的springboot应用占用内存高达2G&#xff0c;后续的应用因为内存不足就部署不了了。排查下内存占用大的原因&#xff1a; docker stats命令&#xff1a…

ucharts修改ToolTip边框阴影文字居中

ucharts修改ToolTip边框阴影文字居中 效果 Demo 链接&#xff1a; https://pan.baidu.com/s/1k0FxmBPKAHlHksFR3YQSlQ 提取码&#xff1a;ytv7

在vue.config.js中配置文件路径代理名

今天在公司项目中看到一个非常有趣的导入路径 crud 先是一蒙 这是个啥 突然想起一个被自己遗漏的知识点 在vue.config.js中配置路径指向 这里 我们随便找一个vue项目 在src下找到 components 目录 如果没有就创建一个 下面找到HelloWorld.vue 如果没有也是自己创建一个就好 然…

LabVIEW开发异步电动机定子故障在线诊断系统

LabVIEW开发异步电动机定子故障在线诊断系统 三相感应电机&#xff08;IM&#xff09;因其简单性、坚固性和可靠性而广泛用于许多工业应用。然而&#xff0c;对于需要高可靠性的特定领域&#xff0c;如汽车、航空航天、军事和核能&#xff0c;使用经典的三相IM似乎不再适用&am…

JavaScript函数复习

这节课我们来通过我们之前学过的函数来逐渐完善&#xff01; const yearsUntilRetiremen (birthyear, firstName) > {const age 2037 - birthyear;const retirement 65 - age;return ${firstName}还有${retirement}年就退休了&#xff01;;}这个是我们之前写的代码&…

循环结构(个人学习笔记黑马学习)

while循环语句 在屏幕中打印0~9这十个数字 #include <iostream> using namespace std;int main() {int i 0;while (i < 10) {cout << i << endl;i;}system("pause");return 0; } 练习案例: 猜数字 案例描述:系统随机生成一个1到100之间的数字&…

数字电路-二进制学习

什么是二进制&#xff1f; 数字电路 中 只有 高电平 和低电平 就是 1 和0 进位规则是“逢二进一”&#xff0c;借位规则是“借一当二”。 二进制、八进制 、十进制、十六进制 二进制 有两个数来表示 &#xff1a; 0、1 八进制 有8个数来表示 &#xff1a; 0、1、2、3、4、…

ASEMI肖特基模块MBR400100CT功能应用介绍

编辑-Z 肖特基模块MBR400100CT是一款高性能半导体器件&#xff0c;常用于电源和开关电路中。该模块采用肖特基二极管技术&#xff0c;具有低导通压降和高速开关特性&#xff0c;适合在高频率和高温环境下使用。 肖特基二极管是基于金属-半导体接触的特殊结构的二极管。与传统P…

Go几种读取配置文件的方式

比较有名的方案有 使用viper管理配置[1] 支持多种配置文件格式&#xff0c;包括 JSON,TOML,YAML,HECL,envfile&#xff0c;甚至还包括Java properties 支持为配置项设置默认值 可以通过命令行参数覆盖指定的配置项 支持参数别名 viper[2]按照这个优先级&#xff08;从高到低&am…

博客系统后台前端UI设计

效果展示 API编写 index.js import axios from "./request"const fastdfs {delete: file/fastdfs/delete } const permission {search: "/sys/permission/search",add: "/sys/permission/add",update: "/sys/permission/update",d…

适合新手程序员的体质,一键代码审查轻松搞定

很多刚入行的程序员会面临一个问题&#xff0c;写完代码进行运行会出现很多bug但是不能准确的定位问题的所在&#xff0c;很多人对于自己的代码结构和层次也摸不着头脑&#xff0c;为了提高代码的质量经常会消耗大量的人力物力来做这件事情。 在&#xff08;软件工程的事实与谬…

阻塞io读取内核驱动变量值

应用程序&#xff1a; #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include "head.…

Spring MVC 四:Context层级

这一节我们来回答上篇文章中避而不谈的有关什么是RootApplicationContext的问题。 这就需要引入Spring MVC的有关Context Hierarchy的问题。Context Hierarchy意思就是Context层级&#xff0c;既然说到Context层级&#xff0c;说明在Spring MVC项目中&#xff0c;可能存在不止…

Linux学习之DNS服务的原理

DNS服务一些理论 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09;是互联网的核心应用服务&#xff0c;可以通过IP地址查询到域名&#xff0c;也可以通过域名查询到IP地址。 FQDN&#xff08;Full Qualified Domain Name&#xff09;是完全限定域名&#xf…