Linux驱动(一)之最简单的驱动程序

2023-11-13

1、前言

为什么要有驱动?为了防止像我等小菜程序员写应用程序的时候权限过高直接去操作底层设备,给设备造成不可挽回的损失,所以要过度一下,让大牛们将底层封装好,应用开发工程师只需要通过特定的接口来完成特定的功能就可以了。

2、应用

通常情况下,应用开发只需要open一个/dev目录下的文件,然后执行write、read等操作即可。那具体是什么原理呢,以应用程序调用open函数为例,改函数为c库中的函数,实际上会产生中断,将权限交给内核,而内核则会根据相关参数去调用具体驱动程序实现的open函数。

2.1 驱动程序分析

接下来我们来分析一个最简单的字符驱动程序:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

/*
1. 构造file_operations
2. 填充file_operations
3. 注册file_operations
4. 编写入口和出口
*/

unsigned int major = 146;

int my_dev_open(struct inode * inode, struct file * file)
{
    printk("my_dev_open\n");
    return 0;
}

ssize_t my_dev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
    printk("my_dev_write\n");
    return 0;
}

static const struct file_operations fops = {
	.owner		= THIS_MODULE,
	.open		= my_dev_open,
	.write		= my_dev_write
};

int my_dev_init(void)
{
    printk("my_dev_init\n");
    register_chrdev(major, "mydev", &fops);
    return 0;
}

void my_dev_exit(void)
{
    printk("my_dev_exit\n");
    unregister_chrdev(major, "mydev");
    return;
}

module_init(my_dev_init);
module_exit(my_dev_exit);
MODULE_LICENSE("GPL");

my_dev_init和my_dev_exit函数是我们加载和卸载驱动内核帮我们调用的接口,这两个接口需要分别用module_init和module_exit函数进行修饰。

从头看,我们定义了my_dev_open和my_dev_write函数,并将这两个函数赋值给了file_operations结构体,然后通过register_chrdev函数将该结构体进行注册。注册到底是干啥?看注册函数的三个参数,major, "mydev", &fops,第一个参数是主设备号,什么是设备号呢,就是内核用来区分设备的一个数字,比如鼠标是1,键盘是2,我们在程序中将其写死为了146(没有什么特殊的,随便写的),第二个参数"mydev"是一个名字,第三个参数则是填充了我们自己写的open和write函数的file_operations结构体。整个过程的意思可以这样理解,就是内核维护了一个设备结构体数组,注册函数实现了向数组中添加了一项信息,添加的结构体数组的下标就是主设备号,结构体数组的内容有名字、file_operations等。

多说一下主设备号,执行cat /proc/devices可以查看当前设备,写死的设备号不要与当前设备重复

将上述代码命令为mydev.c,使用Makefile编译成ko文件,Makefile如下,注意KERN_DIR 要改为你虚拟机或者开发板内核文件目录。

KERN_DIR = /home/02-Kernel/linux-2.6.22.6

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= mydev.o

2.2 驱动安装

将ko文件拷贝至目标板中,然后执行insmod mydev.ko命令进行安装,可以看到驱动程序中my_dev_init函数添加的打印信息,即my_dev_init接口在执行isnmod命令时得到调用,再次执行 cat /proc/devices可以看到驱动程序完成了注册。

接下来使用mknod /dev/mydev c 146 0命令创建一个设备节点,该命令的含义:

mknod         // 创建设备节点
/dev/mydev    // 节点名称 
c             // c表示字符设备节点
146           // 主设备号
0             // 次设备号

执行完成后可以ls -l /dev查看创建的设备节点:

2.3 应用程序分析

编写测试的应用程序代码如下,我们在应用程序中打开了之前创建的设备节点,然后调用write接口向该节点写入了一个数字:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main(int argc, char **argv)
{
    int fd;
    char val = 1;

    fd = open("/dev/mydev", O_RDWR);
    if (fd < 0)
    {
        printf("error, can't open /dev/mydev\n");
        return 0;
    }
    
    write(fd, &val, 1);   
    
    return 0;
}

将程序保存为main.c,直接使用gcc工具进行编译即可:arm-linux-gcc -o main main.c,注意要改为使用自己虚拟机或者开发板的编译工具。

直接运行编译好的程序,可以看到应用程序执行的open和write函数最终调用了我们编写的驱动程序中的open和write接口:

3、总结

通过一个简单的字符驱动程序讲解了一下linux驱动的框架和调用流程,后续再讲解自动创建字符设备节点等优化内容。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux驱动(一)之最简单的驱动程序 的相关文章

  • 对 sf:: 的未定义引用

    我想用 C 制作 GUI 应用程序 发现 SFML 是一个不错的选择 幸运的是 我使用的是 Linux 所以 SFML 2 4 已经安装在我的系统上 所以我开始搜索一些教程并找到了一个制作简单窗口的教程 但是当我运行代码时 出现错误 提示未
  • 使用 MongoDB docker 镜像停止虚拟机而不丢失数据

    我已经在 AWS EC2 上的虚拟机中安装了官方的 MongoDB docker 映像 并且数据库上已经有数据 如果我停止虚拟机 以节省过夜费用 我会丢失数据库中包含的所有数据吗 在这些情况下我怎样才能让它持久 有多种选择可以实现此目的 但
  • 原生 Linux 应用程序可像 ResHacker 一样编辑 Win32 PE

    我想运行自动修改 dll服务 用户提交特定的 dll 我在服务器上修改它 然后用户可以下载 dll的修改版本 是否有任何本机 Linux 应用程序提供常见的 Win32 PE 修改功能 例如图标 字符串 加速器 对话等 至少提供命令行或脚本
  • 将 stdout 作为命令行 util 的文件名传递?

    我正在使用一个命令行实用程序 该实用程序需要传递文件名以将输出写入 例如 foo o output txt 它唯一写入的东西stdout是一条消息 表明它运行成功 我希望能够通过管道传输写入的所有内容output txt到另一个命令行实用程
  • 静态链接共享对象?或者损坏的文件?

    我有一个从专有来源获得的库 我正在尝试链接它 但出现以下错误 libxxx so 文件无法识别 文件格式无法识别 Collect2 ld 返回 1 退出状态 确实 ldd libxxx so statically linked 这究竟意味着
  • 在中断时获取 current->pid

    我正在Linux调度程序上写一些东西 我需要知道在我的中断到来之前哪个进程正在运行 当前的结构可用吗 如果我在中断处理程序中执行 current gt pid 我是否可以获得我中断的进程的 pid 你可以 current gt pid存在并
  • 如何在两个不同帐户之间设置无密码身份验证

    我们可以在两台机器的两种不同用途之间设置无密码身份验证吗 例如 计算机A有用户A 计算机B有用户B 我们可以设置密码 ssh 以便计算机 A 上的用户 A 使用其用户帐户 A 登录计算机 B 谢谢你 如果我理解你的问题 你能设置一下吗ssh
  • 编写多个mysql脚本

    是否可以在复合脚本中包含其他 mysql 脚本 理想情况下 我不想为包含的脚本创建存储过程 对于较大的项目 我想分层维护几个较小的脚本 然后根据需要组合它们 但现在 我很乐意学习如何包含其他脚本 source是一个内置命令 您可以在 MyS
  • 如何在 Linux x86_64 上模拟 iret

    我正在编写一个基于 Intel VT 的调试器 由于当 NMI Exiting 1 时 iret 指令在 vmx guest 中的性能发生了变化 所以我应该自己处理vmx主机中的NMI 否则 guest会出现nmi可重入错误 我查了英特尔手
  • Windows 与 Linux 文本文件读取

    问题是 我最近从 Windows 切换到 Ubuntu 我的一些用于分析数据文件的 python 脚本给了我错误 我不确定如何正确解决 我当前仪器的数据文件输出如下 Header 有关仪器等的各种信息 Data 状态 代码 温度 字段等 0
  • R 未获取用户库

    我有一个带 R 3 6 0 的 Fedora 30 系统 用户库设置在Renviron就像这个 R LIBS USER R LIBS USER R x86 64 redhat linux gnu library 3 6 事实上 它出现在交互
  • 如何从 Linux 的 shell 中删除所有以 ._ 开头的文件?

    确实如标题所示 我已将许多文件从 Mac 复制到 Raspberry Pi 这导致了许多以前缀开头的多余文件 我想删除以以下开头的文件夹中的每个文件 我该怎么做 尝试类似的方法 cd path to directory rm rf 或者 如
  • Linux命令列出所有可用命令和别名

    是否有一个 Linux 命令可以列出该终端会话的所有可用命令和别名 就好像您输入 a 并按下 Tab 键一样 但针对的是字母表中的每个字母 或者运行 别名 但也返回命令 为什么 我想运行以下命令并查看命令是否可用 ListAllComman
  • 如何从类似于 eclipse 的命令行创建可运行的 jar 文件

    我知道 eclipse 会生成一个可运行的 jar 文件 其中提取并包含在该 jar 文件中的所有库 jar 文件 从命令提示符手动创建 jar 文件时如何执行类似的操作 我需要将所有 lib jar 解压到类文件夹中吗 目前我正在使用 j
  • 确保 config.h 包含一次

    我有一个库项目 正在使用 Linux 中的 autotools 套件移植到该项目 我对自动工具很陌生 本周 我已经了解了其操作的基础知识 我有一个关于如何保留内容的问题config h免遭重新定义 我惊讶地发现生成的config h文件也没
  • “grep -q”的意义是什么

    我正在阅读 grep 手册页 并遇到了 q 选项 它告诉 grep 不向标准输出写入任何内容 如果发现任何匹配 即使检测到错误 也立即以零状态退出 我不明白为什么这可能是理想或有用的行为 在一个程序中 其原因似乎是从标准输入读取 处理 写入
  • 如何从 Linux 命令行获取视频文件的分辨率(宽度和高度)?

    我一直在挖掘 mplayer mencoder 和 ffmpeg 文档 但我似乎无法想出anything 我对输出格式不是特别挑剔 因为我可以使用正则表达式将其拉出来 我只是似乎无法首先获取数据 Use ffprobe https ffmp
  • 无法仅在控制台中启动 androidstudio

    你好 我的问题是下一个 我下载了Android Studio如果我去 路径 android studio bin 我执行studio sh 我收到以下错误 No JDK found Please validate either STUDIO
  • gethostbyname() 或 getnameinfo() 如何在后台工作?

    How gethostbyname or getnameinfo 在后台工作 include
  • InstaPy:“错误,无法确定 64 位 Linux 的正确文件名”

    有人知道如何解决或解决这个问题吗 来自控制台的堆栈跟踪 执行后报告错误 InstaPy Version 0 6 9 Workspace in use home zanettra InstaPy Error unable to determi

随机推荐

  • ON_MESSAGE使用方法

    ON MESSAGE响应的是自定义消息 有关自定义消息的处理请看如下步骤 1 定义消息 在CCDlg类的头文件中加入如下代码 define WM CUSTOMIZE WM USER 1 头文件中加入Customize的声明 afx msg
  • 剑指offer !! 68.二叉树的最近公共祖先

    二叉树的最近公共祖先 中等 2 4K 相关企业 给定一个二叉树 找到该树中两个指定节点的最近公共祖先 百度百科中最近公共祖先的定义为 对于有根树 T 的两个节点 p q 最近公共祖先表示为一个节点 x 满足 x 是 p q 的祖先且 x 的
  • BeyondCompare4破解最佳实践

    先说原理 和大多数软件破解的方法一致 针对官方给出的试用期做文章 一般来说 收费软件大多提供一个 7 30 天的试用期 那对于软件来说 找出记录这个试用期的值就是关键 找到试用期的值以后 能够自动化修改即可完美实现无线循环使用正版软件 完美
  • 计算机毕业设计Node.js+Vue大学生网络安全题库系统(程序+源码+LW+部署)

    该项目含有源码 文档 程序 数据库 配套开发软件 软件安装教程 欢迎交流 项目运行 环境配置 Node js Vscode Mysql5 7 HBuilderX Navicat11 Vue Express 项目技术 Express框架 No
  • 简单 TCP UDP server client

    UDP server include stdafx h include
  • 记一下 Java Static 有哪些玩法

    static 变量 static变量是使用 static关键字定义的变量 又被称为静态变量 静态变量是属于类的 也叫类变量 实例变量 非static变量 属于某个具体的对象 静态变量和非静态变量的区别 静态变量被所有对象共享 在内存中只有一
  • 如何用Vue实现简易的富文本编辑器,并支持Markdown语法

    前端开发经常会用到富文本编辑器 比如CKEditor 动不动一个库几十M的代码量 其中涉及许多你可能用不到的功能特性和相关设置 CKEditor最新版本的代码仓库就有接近2000个JS文件 300 000行代码 可是如果你只需要一个简易版的
  • 挑战全网最详细靶机教程——vulnhub靶机实战vulnhub Tr0ll: 1【适合刚接触的新人学习】

    靶机地址 https www vulnhub com entry tr0ll 1 100 靶机难度 简单 靶机发布日期 2014年8月14日 靶机描述 Tr0ll的灵感来自OSCP实验室中不断摇曳的机器 作者 木木北星 目标 得到root权
  • [763]阿里企业云邮箱POP\SMTP\IMAP地址和端口信息

    企业云邮箱各个服务器地址及端口信息如下 收件服务器地址 POP 服务器地址 pop3 mxhichina com 端口110 SSL 加密端口995 或 IMAP 服务器地址 imap mxhichina com 端口143 SSL 加密端
  • springboot+mybatisplus动态数据源

    1 pom xml 引入
  • python中的rt_tensorRT动态输入(python)

    关于tensorRT动态输入的例子大多数都是c 版本的 python版本的较少 这里简单总结下python处理tensorRT动态输入时 遇到的一些问题及解决方案 这里的动态输入是指batch width height等不固定大小的输入 对
  • 【PID】51单片机PID控制电机转速

    PID参考链接 PID增量控制 include Pid Init h include main h extern u16 out extern u16 CurSpeed extern u16 SpeedSet extern u16 PWMT
  • python中字典取最大值的应用

    字典里面的值如何取最大值 举个列子 list a 12 b 33 c 55 第一种 用for循环去调取 先建一个列表 在列表取最大值 list a 12 b 33 c 55 ls for item in list ls append lis
  • Java创建文件和文件夹

    例子如下 java代码 import java io 导入所需的包 public class IOTest 类 public static void main String args 主程序 程序入口 File file new File
  • Mysql和ES、Redis数据同步方案汇总

    文章目录 前言 一 数据同步方案 1 同步双写 2 异步双写 MQ https so csdn net so search q MQ spm 1001 2101 3001 7020 方式 3 基于Mysql表定时扫描同步 4 基于 Binl
  • 记录TensorFlow成功安装无法import

    ERROR root Internal Python error in the inspect module Below is the traceback from this internal error ERROR root Intern
  • 【Docker】docker基础使用

    文章目录 docker概念 什么是docker docker引擎迭代 docker与虚拟机 docker版本发展 docker基础 docker架构 docker Registry 镜像仓库 镜像仓库使用流程 实际研发镜像仓库使用 不同镜像
  • Spring(三):IoC容器装配Bean(xml配置方式和注解方式)

    XML配置方式 一 三种实例化Bean的方式 1 使用类构造器实例化 默认无参数
  • Pinia的使用方法

    Pinia的作用 作用 Pinia 是 Vue js 的轻量级状态管理库 他可以在组件和页面间共享状态 相关链接 官方网站 https pinia vuejs org 中文文档 https pinia web3doc top introdu
  • Linux驱动(一)之最简单的驱动程序

    1 前言 为什么要有驱动 为了防止像我等小菜程序员写应用程序的时候权限过高直接去操作底层设备 给设备造成不可挽回的损失 所以要过度一下 让大牛们将底层封装好 应用开发工程师只需要通过特定的接口来完成特定的功能就可以了 2 应用 通常情况下