Linux 设备驱动开发实例

2023-05-16

编译和运行

驱动编译要用到kernel的Makefile文件 — — 也就是源码树的编译系统。因此,源码需要被配置和编译,以ubuntu自带的源码为例:

编译外部模块(.ko)的编译命令是:

make -C M= mak*e−*C< pathtokernelsrc> M=PWD

也就是进入到kernel目录,利用kbuild系统来编译驱动文件。obj-m 告诉编译系统需要编译成一个module(.ko),foo.o表明需要源文件是foo.c或者foo.S,如果驱动模块包含多个文件(如: foo_main.c, foo_common.c),写法如下:

kbuild将编译$(foo-y)列出的所有文件,合并产生 foo.ko

在编译期间,模块的Makefile会被kbuild多次读取,因此建议使用$(KERNELRELEASE)来区分Makefile的使用阶段,优化后的Makefile如下:

第一次运行make的时侯,$(KERNELRELEASE) 为空,因此,Makefile的 'else' 内容首先被读取,然后,执行 ‘make -C .....’\, 执行过程中,会回读Makefile文件,这次, 'ifneq' 条件满足,两次走不同的路径,编译系统配置不同的变量参数。

如果,不使用 $(KERNELRELEASE) 区分的话,每次编译系统都会设置所有的变量和规则,可能会与kernel的Makefile变量或者规则冲突,因此,建议在(KERNELRELEASE)为空的情况下,配置driver专用的变量和规则,除了使用(KERNELRELEASE)为空的情况下,配置d*river*专用的变量和规则,除了使用(KERNELRELEASE)外,kernel还提供了一些其它的做法, 更多的kernel 编译系统信息,请参考kernel源码下的 “Documentation/kbuild/”

驱动模块运行相关命令

  • insmod foo.ko —— 加载driver 到kernel去运行。
  • rmmod foo —— 从kernel 移除driver.
  • lsmod —— 查看当前kernel 运行的模块。

字符设备

字符设备驱动实际上就是实现一个文件接口,让设备文件可以像一个普通文件那样来访问,这样应用程序就可以使用libc库的'文件IO API(open/write/read/close 系列函数)' 来访问驱动程序,与驱动交换数据,因此,它的核心就是实现文件系统的接口 -- 文件操作。

程序入口

宏内核与微内核的一个最大区别就是驱动程序的运行空间。微内核系统,驱动程序作为一个应用程序,运行在用户空间,它的入口就是应用程序的‘main’函数。 Linux作为一个宏内核系统,它的驱动程序与内核是一体的,运行在内核空间,它的入口是 ‘module_init’,‘module_exit’则是对应的退出函数,它们一定是成对出现的。

foo_init 执行了最基本的字符设备操作:使用 cdev_add 添加一个 'cdev'到字符设备列表(其实是一个map结构), 这样就把foo这个字符设备托付给kernel进行管理了,当应用程序操作相应的设备文件时,kernel能调度到foo驱动程序。

foo_exit 一定要使用 cdev_del 从列表里面删除设备,不然,当kernel从列表里面查找到 cdev时,返回的将是“过时”的指针,使用它来 callback相应操作时,就会出现空指针异常,导致kernel会挂掉。切记!foo_initfoo_exit 一定要成对使用,执行相反的操作。

bug 实例:

  1. foo_exit 不执行 cdev_del 函数。
  2. insmod foo.ko -- OK。
  3. 应用程序对设备文件读写 -- OK。
  4. rmmod foo -- OK。
  5. insmod foo.ko -- OK。
  6. 应用程序对设备文件读写 -- core dump。

rmmod foo’时,会调用 foo_exit,但是,程序员忘了执行 cdev_del 函数,导致 foo.cdev 的指针没有被删除而变成了一个空指针,它仍然在字符设备列表里面。 当第二次插入foo.ko后, 读写该设备时,Kernel找的是旧的 foo.cdev 空指针,用它调用相应的文件操作时,就发生了空指针的 core dump 错误。

文件操作

setup_dev: 注册当前的设备的文件操作函数,当应用程序操作设备文件时,调用到对应的驱动函数。与用户空间交换数据,copy_from/to_user,这两个函数返回0表示函数执行成功。

  • copy_from_user: 把用户写入的数据copy驱动数据buf保存起来。
  • copy_to_user: copy驱动数据buf到 用户读取数据的buf。

应用程序与字符驱动的交互流程

  1. 创建设备文件 -- sudo mknod /dev/foodev c 500 0
  2. 修改设备文件权限 -- sudo chmod 766 /dev/foodev
  3. 应用程序使用open函数打开设备文件。
  4. kernel根据文件类型(字符设备文件)找到字符设备列表,并根据设备号(Major, Minor),找到对应的设备驱动模块。
  5. 调用设备驱动的open函数 foo_open 。
  6. 应用程序调用 read/write函数来读写设备文件。
  7. 驱动调用 foo_read/write并使用copy_from/to_user来交换数据。

常见问题

Q: 读写设备文件时,write或者 read函数返回0,不能读写数据 ? A: 这类设备文件读写失败问题,很有可能是权限问题,确认下文件读写权限,其次是数据是否符合驱动的要求。

块设备

块设备指的是存储设备,块设备驱动就是存储驱动如:HD,SSD。Linux 用 Block 子系统对它们进行管理,把应用层的IO读写请求,转变为Request ,传给相应的会设备驱动。驱动流程比较简单:

register_blkdev → alloc_disk → 处理request

Q: 文件系统与Block子系统的关系? A: Block子系统主要是提供最底层的数据读写,也就是raw io,文件系统使用它进行IO操作。

注册

注册块设备(主设备号)

注册设备(MAJOR,MINOR)

添加磁盘

这个磁盘会出现在 /dev 目录下面, 本例是 /dev/frd0,用户可以对设备文件进行格式化,分区等磁盘相关的操作。如: ‘_mkfs.ext2 /dev/frd0_’, ‘_mount /dev/frd0 /mnt_’。

初始化请求队列

处理设备请求

kernel 提供了一些宏来帮助遍历请求列表。对请求的处理策略,就是Block驱动最核心最精华的部分,开发者得根据设备的物理特性来提高访问效率,解决并发拥堵等问题。 fr_queue_rq()\ -- ‘请求队列’处理函数,在初始化请求队列时设置,Loop处理每个请求:

fr_transfer()\ -- 物理设备读写数据,根据请求的上下文内容(context),进行底层数据传输,这里就是最底层的IO通讯了,驱动根据物理设备的接口协议来进行数据的读写。

块设备驱动测试

执行上面命令后,frd_data_r 和 frd_data_w的内容应该是一样的。

以上就是良许教程网为各位朋友分享的Linux 设备驱动开发实例。 以上就是良许教程网为各位朋友分享的Linux相关知识。

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

Linux 设备驱动开发实例 的相关文章

  • Qt 嵌入式触摸屏 QMouseEvents 在收到 MouseButtonRelease 之前未收到

    我在带有触摸屏的小型 ARM 嵌入式 Linux 设备上使用 Qt 4 8 3 我的触摸屏配置了 tslib 并对其进行了校准 因此 etc 中有一个 pointcal 文件 我的触摸事件的位置工作得很好 但无论如何我都会在鼠标按下或鼠标释
  • 如何在 Vim 中突出显示 Bash 脚本?

    我的 Vim 编辑器自动突出显示 PHP 文件 vim file php HTML 文件 vim file html 等等 但是当我输入 vim file在里面写一个Bash脚本 它不会突出显示它 我如何告诉 Vim 将其突出显示为 Bas
  • Linux 内核使用的设备树文件 (dtb) 可视化工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个可以图形化表示Linux内核中使用的硬件设备树的工具 我正在尝试了解特定 Arm 芯片组
  • 使用 ioctl 在 C++ 中以编程方式添加路由

    我编写了简单的 C 函数 添加了新路线 void addRoute int fd socket PF INET SOCK DGRAM IPPROTO IP struct rtentry route memset route 0 sizeof
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • Bash:将字符串添加到文件末尾而不换行

    如何将字符串添加到文件末尾而不换行 例如 如果我使用 gt gt 它将添加到文件末尾并换行 cat list txt yourText1 root host 37 echo yourText2 gt gt list txt root hos
  • Ruby:在 Ubuntu 上安装 rmagick

    我正在尝试在 Ubuntu 10 04 上安装 RMagick 看起来here https stackoverflow com questions 1482823 is there an easy way to install rmagic
  • 正则表达式删除块注释也删除 * 选择器

    我正在尝试使用 bash 从 css 文件中删除所有块注释 我有以下 sed 命令的正则表达式 sed r s w s w d 这可以很好地去除块注释 例如 This is a comment this is another comment
  • Linux无法删除文件

    当我找到文件时 我在删除它们时遇到问题 任务 必须找到带有空格的文件并将其删除 我的尝试 rm find L root grep i 但我有错误 rm cannot remove root test No such file or dire
  • 如何在线程创建和退出时调用函数?

    include
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • 在内核代码中查找函数的最佳方法[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我开始浏览内核代码 遇到的一件事是如何跟踪函数调用 结构定义等 有没有一种好的方法可以快速跳转到函数定义并退出 我尝试过 Source N
  • Linux 中 m 标志和 o 标志将存储在哪里

    我想知道最近收到的路由器通告的 m 标志和 o 标志的值 从内核源代码中我知道存储了 m 标志和 o 标志 Remember the managed otherconf flags from most recently received R
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可
  • Google BQ:运行参数化查询,其中参数变量是 BQ 表目标

    我正在尝试从 Linux 命令行为 BQ 表目标运行 SQL 此 SQL 脚本将用于多个日期 客户端和 BQ 表目标 因此这需要在我的 BQ API 命令行调用中使用参数 标志 parameter 现在 我已经点击此链接来了解参数化查询 h
  • 我们真的应该使用 Chef 来管理 sudoers 文件吗?

    这是我的问题 我担心如果 Chef 破坏了 sudoers 文件中的某些内容 可能是 Chef 用户错误地使用了说明书 那么服务器将完全无法访问 我讨厌我们完全失去客户的生产服务器 因为我们弄乱了 sudoers 文件并且无法再通过 ssh
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • 使用 \r 并打印一些文本后如何清除控制台中的一行?

    对于我当前的项目 有一些代码很慢并且我无法使其更快 为了获得一些关于已完成 必须完成多少的反馈 我创建了一个进度片段 您可以在下面看到 当你看到最后一行时 sys stdout write r100 80 n I use 80覆盖最终剩余的
  • 通过 Visual Studio 2017 使用远程调试时 Linux 控制台输出在哪里?

    我的Visual Studio 2017 VS2017 成功连接Linux系统 代码如下 include

随机推荐