【学习记录】从0开始的Linux学习之旅——字符型设备驱动及应用

一、概述

    Linux操作系统通常是基于Linux内核,并结合GNU项目中的工具和应用程序而成。Linux操作系统支持多用户、多任务和多线程,具有强大的网络功能和良好的兼容性。基于前面应用与驱动的开发学习,本文主要讲述如何在linux系统上把应用与驱动的链路打通,即在应用中使用新增的驱动接口。

二、概念及原理

    应用程序通过系统调用与内核进行交互,而驱动程序则提供了硬件设备的访问接口,内核本身则提供了系统调用、驱动框架等基础设施。
    驱动开发:Linux 驱动开发是指为 Linux 内核开发各种设备驱动程序,用于控制和管理硬件设备。驱动程序运行在内核空间,直接与硬件进行交互。Linux 内核提供了丰富的接口和框架,开发者可以编写各种类型的设备驱动,包括网络设备、存储设备、输入设备等。驱动程序通过内核提供的接口与用户空间的应用程序进行通信。
    应用开发:Linux 应用开发是指在 Linux 系统上开发各种类型的应用程序,包括命令行工具、图形界面应用、服务器端应用等。Linux 提供了丰富的开发环境和工具链,开发者可以使用各种编程语言和开发工具进行应用开发。应用程序运行在用户空间,通过系统调用与操作系统内核进行交互,执行各种任务和功能。
    内核开发:Linux 内核开发是指对 Linux 内核本身进行开发和维护。Linux 内核是操作系统的核心,负责管理系统资源、调度任务、提供系统调用等功能。内核开发包括对内核功能的添加和修改,修复内核漏洞,优化性能等工作。内核开发人员通常会编写和维护内核的各种子系统和模块,包括调度器、文件系统、网络协议栈等。
    设备类型:Linux 中的设备类型主要分为字符设备和块设备两种,它们分别适用于以字符为单位和以数据块为单位进行输入输出的场景。
    用户空间与内核空间:用户空间和内核空间分别代表了操作系统中不同的内存空间和权限级别,它们共同构成了操作系统的运行环境,保证了系统的稳定性、安全性和可管理性。用户与内核的交互只能通过特定的系统接口,这样就保证了内核的稳定性。

三、准备工作

  1. 安装虚拟机VMware
  2. 安装ubuntu 22.04
  3. 安装vim、vscode等工具
sudo apt update
sudo apt install vim code

    另外需要先熟悉单独的驱动开发及应用开发,具体可参考最底下相关文章链接。

四、代码实现

4.1 驱动代码实现

    除了原本的模块加载和卸载接口,这里我们还要实现一些接口可供应用层使用。众所周知,Linux中万物皆文件,那我们就来实现操作文件所需的最常用的几个接口:open、close、write、read。
    首先新建一个driver.c的文件,在文件中添加如下代码。

#include <linux/init.h>
#include <linux/module.h>#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>#define DEVICE_NAME	"myfirstdev"
#define CLASS_NAME	"myfirstdev_class"
/****************************模块的文件操作接口***************************/
static int majorNumber;
static struct class* charClass = NULL;
static struct device* charDevice = NULL;/* 用来存储应用层传下来数据 */
static char DevMsg[100];/* 打开接口 */
static int dev_open(struct inode *inodep,struct file *f){printk(KERN_INFO "打开设备!\n");return 0;
}/* 关闭接口 */
static int dev_release(struct inode *inodep,struct file *f){printk(KERN_INFO "关闭设备!\n");return 0;
}/* 读接口 */
static ssize_t dev_read(struct file *f,char *buffer,size_t len,loff_t *offset){int error_count = 0;/* 可以传一些数据到应用层 */if (len > sizeof(DevMsg)){printk(KERN_INFO "读取数据字节数过长,共获取了%d字节\n", len);return 0;}/* 把模块内的数据缓存拷贝到用户缓存中 */error_count = copy_to_user(buffer, DevMsg, len);if (error_count == 0){printk(KERN_INFO "成功发送%d个字节数据给到用户\n", len);return 0;}else{printk(KERN_INFO "发送失败\n");return -EFAULT;}
}/* 写接口 */
static ssize_t dev_write(struct file *f,const char *buffer,size_t len,loff_t *offset){int error_count = 0;/* 可以保存一些应用层传下来的数据 */if (len > sizeof(DevMsg)){printk(KERN_INFO "写入数据字节数过长,需要写入%d字节\n", len);return 0;}/* 内核空间与用户空间的数据交互必须通过这个接口 */copy_from_user(DevMsg, buffer, len);printk(KERN_INFO "写入数据成功,共写入%d个字节\n", len);return len;
}/* 文件接口挂接 */
static struct file_operations fops = {.open = dev_open,.release = dev_release,.read = dev_read,.write = dev_write,
};/****************************模块的加载和卸载接口****************************/
/* 定义模块的初始化函数 */
static int Driver_Init(void)
{/* 先注册字符型设备 */majorNumber = register_chrdev(0, DEVICE_NAME, &fops);printk(KERN_INFO "注册的设备名为:%s\n", DEVICE_NAME);/* 创建设备类 */charClass = class_create(THIS_MODULE, CLASS_NAME);if (IS_ERR(charClass)){unregister_chrdev(majorNumber, DEVICE_NAME);printk(KERN_ALERT "注册设备失败\n");return PTR_ERR(charClass);}/* 创建设备驱动 */charDevice = device_create(charClass,NULL,MKDEV(majorNumber, 0),NULL,DEVICE_NAME);if (IS_ERR(charDevice)){class_destroy(charClass);unregister_chrdev(majorNumber, DEVICE_NAME);printk(KERN_ALERT "创建设备失败\n");return PTR_ERR(charDevice);}printk(KERN_INFO "字符型设备驱动加载成功!\n");return 0;
}/* 定义模块的退出函数 */
static void Driver_Exit(void)
{/* 先销毁设备驱动 */device_destroy(charClass, MKDEV(majorNumber, 0));class_unregister(charClass);class_destroy(charClass);/* 再注销字符型设备 */unregister_chrdev(majorNumber, DEVICE_NAME);printk(KERN_INFO "字符型设备驱动卸载成功!\n");
}/* 注册模块的初始化和退出函数,这个是给内核识别的 */
module_init(Driver_Init);
module_exit(Driver_Exit);/* 声明该模块符合GPL协议——必须加,不然编译会出错 */
MODULE_LICENSE("GPL");/* 下面是声明作者姓名、设备类型和版本号,可加可不加 */
MODULE_AUTHOR("Chewie");
MODULE_DESCRIPTION("A simple char driver");
MODULE_VERSION("0.1");

    再新增一个Makefile文件,添加如下内容。

obj-m := driver.oKDIR := /lib/modules/$(shell uname -r)/buildall:make -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean

    编译模块

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

    编译成功无告警后会生成xx.ko文件,加载.ko模块,在应用使用期间都需要保持加载状态。

sudo insmod driver.ko

    如不需要使用此模块时,需要使用rmmod卸载模块。

sudo rmmod driver

4.2 应用代码实现

    同样的,即然驱动实现了对应的文件接口,那应用层就可以直接打开对应的驱动文件进行操作。新建一个app.c的文件,添加如下代码。

#include <stdio.h>
#include <fcntl.h>#define DEVICE_NODE	"/dev/myfirstdev"int main(){int file_desc;int ret;char msg[100];char write_msg[] = "hello";/* 打开刚才的设备驱动文件 */file_desc = open(DEVICE_NODE, O_RDWR);if (file_desc < 0){printf("无法打开设备文件\n");return -1;}/* 从设备中写入数据 */ret = write(file_desc, write_msg, strlen(write_msg));printf("写入的数据为:%s\n", write_msg);if (ret < 0){printf("写入数据失败\n");close(file_desc);return -1;}/* 从设备中读取数据 */ret = read(file_desc, msg, sizeof(msg));printf("读出的数据为:%s\n", msg);if (ret < 0){printf("读取数据失败\n");close(file_desc);return -1;}printf("读出写入的数据为:%s\n", msg);/* 关闭设备 */close(file_desc);return 0;
}

    再新增一个Makefile文件,添加如下内容。

# 定义编译器和编译选项
CC = gcc
CFLAGS = -Wall -g# 定义目标文件和源文件
TARGET = app
SRCS = app.c
OBJS = $(SRCS:.c=.o)# 默认构建规则
all: $(TARGET)# 生成目标可执行文件
$(TARGET): $(OBJS)$(CC) $(CFLAGS) -o $@ $^# 生成目标文件
%.o: %.c$(CC) $(CFLAGS) -c -o $@ $<# 清理生成的文件
clean:rm -f $(OBJS) $(TARGET)

    在当前目录下,使用make命令编译应用程序。编译无错误后会生成app文件,执行以下命令运行程序,因为这里程序里调用了内核驱动,所以需要sudo权限。

sudo ./app

    这里可以打开系统日志看下整个过程。

sudo tail -f /var/log/kern.log

在这里插入图片描述

五、相关链接

【学习记录】从0开始的Linux学习之旅——驱动模块编译与加载
【学习记录】从0开始的Linux学习之旅——应用开发(helloworld)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STC15-串口通信打印输出数据printf函数与sprintf函数

STC15-串口通信打印输出数据printf函数与sprintf函数 1.打印输出数据有二种printf函数与sprintf函数&#xff0c;不同之处有&#xff1a;&#xff08;1&#xff09;函数的声明不同&#xff08;2&#xff09;函数的功能不同&#xff08;3&#xff09;用法举例 该问题引用百度知道…

word模板导出word文件

前期准备工作word模板 右键字段如果无编辑域 ctrlF9 一下&#xff0c;然后再右键 wps 直接 ctrlF9 会变成编辑域 pom.xml所需依赖 <dependencies> <!--word 依赖--> <dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId…

Hadoop入门学习笔记

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 这里写目录标题 一、VMware准备Linux虚拟机1.1. VMware安装Linux虚拟机1.1.1. 修改虚拟机子网IP和网关1.1.2. 安装…

揭秘C语言结构体:通往内存对齐的视觉之旅

揭秘C语言结构体&#xff1a;通往内存对齐的视觉之旅 引言 在C语言的编程旅程中&#xff0c;结构体&#xff08;structs&#xff09;是一个关键而强大的概念。结构体不仅允许我们组织和存储不同类型的数据&#xff0c;而且通过深入了解内存对齐&#xff0c;我们可以更好地优化…

uni-app 微信小程序之自定义中间圆形tabbar

文章目录 1. 自定义tabbar效果2. pages新建tabbar页面3. tabbar 页面结构4. tabbar 页面完整代码 1. 自定义tabbar效果 2. pages新建tabbar页面 首先在 pages.json 文件中&#xff0c;新建一个 tabbar 页面 "pages": [ //pages数组中第一项表示应用启动页&#xff…

关于svn如何上传一个完整的项目

注意&#xff1a;请一定要按照该步骤进行操作&#xff0c;请上传新项目时将项目名称进行规范命名 例如原始文件是arrange_v2 将此项目需要注入新的医院 则命名为 arrange_某医院名称_门诊或者医技或者药房_v2 重新命名文件夹名称快捷键 &#xff08;F12&#xff09; 一 &…

链表OJ题

目录 一. 反转链表 1.思路 2.代码实现 二. 链表中倒数第k个节点 1.思路 2.代码实现 三. 相交链表 1.思路 2.代码实现 四. 环形链表 1. 思路 2. 代码实现 一. 反转链表 1.思路 2.代码实现 struct ListNode {int val;struct ListNode *next; };//链表结构 struct Lis…

常见的Bean工厂后置处理器

此代码在jdk11上测试通过&#xff0c;SpringBoot版本为2.7.14 1.上代码 导入坐标 <dependencies><!-- spring数据坐标 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-rest</art…

ORA-600 kcbzib_kcrsds_1一键恢复

一个19c库由于某种原因redo损坏强制打开库报ORA-600 kcbzib_kcrsds_1错误 SQL> startup mount pfile?/database/pfile.txt; ORACLE instance started. Total System Global Area 859830696 bytes Fixed Size 9034152 bytes Variable Size 5…

kubeadm 安装k8s1.28.x 底层走containerd 容器

1. k8s1.28.x 的概述 1.1 k8s 1.28.x 更新 Kubernetes v1.28 是 2023 年的第二个大版本更新&#xff0c;包含了 46 项主要的更新。 而今年发布的第一个版本 v1.27 有近 60 项&#xff0c;所以可以看出来&#xff0c;在发布节奏调整后&#xff0c; 每个 Kubernetes 版本中都会包…

C++ vector基本操作

目录 一、介绍 二、定义 三、迭代器 四、容量操作 1、size 2、capacity 3、empty 4、resize 5、reserve 总结&#xff08;扩容机制&#xff09; 五、增删查改 1、push_back & pop_back 2、find 3、insert 4、erase 5、swap 6、operator[] 一、介绍 vector…

深度学习手势检测与识别算法 - opencv python 计算机竞赛

文章目录 0 前言1 实现效果2 技术原理2.1 手部检测2.1.1 基于肤色空间的手势检测方法2.1.2 基于运动的手势检测方法2.1.3 基于边缘的手势检测方法2.1.4 基于模板的手势检测方法2.1.5 基于机器学习的手势检测方法 3 手部识别3.1 SSD网络3.2 数据集3.3 最终改进的网络结构 4 最后…

时间序列预测实战(二十五)PyTorch实现Seq2Seq进行多元和单元预测(附代码+数据集+完整解析)

一、本文介绍 本文给大家带来的时间序列模型是Seq2Seq&#xff0c;这个概念相信大家都不陌生了&#xff0c;网上的讲解已经满天飞了&#xff0c;但是本文给大家带来的是我在Seq2Seq思想上开发的一个模型和新的架构&#xff0c;架构前面的文章已经说过很多次了&#xff0c;其是…

更改AndroidStudio模拟器位置

C盘何等的珍贵&#xff0c;可是好多工具&#xff0c;软件非得默认安装在C盘。。导致C盘越来越紧张。。 在日常使用过程中&#xff0c;安装任何软件都会将其安装到非系统盘下&#xff0c;Android模拟器也不能例外。保护好C盘也是日常一个良好的习惯。 Android AVD默认路径&…

计算n的阶乘-递归与迭代之间的相爱相杀

n的阶乘是指从1连乘到n的结果&#xff0c;通常用符号n!表示。例如&#xff0c;3的阶乘表示为3!&#xff0c;计算过程如下&#xff1a; 3! 3 2 1 6 一般地&#xff0c;n的阶乘可以用递归或迭代的方式计算&#xff0c;公式为&#xff1a; n! n (n-1) (n-2) ... 2 1 …

Adobe ColdFusion文件读取漏洞(CVE-2010-2861)

任务一&#xff1a; 复现漏洞 任务二&#xff1a; 尝试利用漏洞读取目标系统中的“opt/coldfusion8/license.txt"文件 1.环境搭建&#xff08;网上写的密码是admin&#xff0c;就用admin&#xff09; 2.看答案就是一层一层进行路径穿越攻击&#xff0c;这里要注意如果…

史上最强 Charles 抓包

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

netcore swagger 错误 Failed to load API definition

后端接口报错如下&#xff1a; 前端nswag报错如下&#xff1a; 根据网上查询到的资料说明&#xff0c;说一般swagger这种错误都是控制器里有接口代码异常造成的&#xff0c;通常是接口没有加属性Attribute&#xff0c; 比如[HttpPost("Delete")]、[HttpGet("Del…

Kafka Connect :构建强大分布式数据集成方案

Kafka Connect 是 Apache Kafka 生态系统中的关键组件&#xff0c;专为构建可靠、高效的分布式数据集成解决方案而设计。本文将深入探讨 Kafka Connect 的核心架构、使用方法以及如何通过丰富的示例代码解决实际的数据集成挑战。 Kafka Connect 的核心架构 Kafka Connect 的核…

flutter开发实战-readmore长文本展开和收缩控件

flutter开发实战-readmore长文本展开和收缩控件 当长文本展开和收缩控件&#xff0c;我们需要使用readmore来处理长文本展开和收缩&#xff0c;方便阅读 一、引入readmore 在工程的pubspec.yaml中引入插件 readmore: ^2.1.0ReadMoreText的属性如下 const ReadMoreText(this.…