LV06-03-chrdev-03-内核空间与用户空间数据交互
本文主要是字符设备驱动——内核空间与用户空间数据交互的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
PC端开发环境 | Windows | Windows11 |
Ubuntu | Ubuntu20.04.2的64位版本 | |
VMware® Workstation 17 Pro | 17.6.0 build-24238078 | |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) | |
Win32DiskImager | Win32DiskImager v1.0 | |
Linux开发板环境 | Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04 | |
linux内核 | linux-4.19.71(NXP官方提供) |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
nxp-imx/linux-imx/releases/tag/v4.19.71 | NXP linux内核仓库tags中的v4.19.71 | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 | NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0 | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) | |
Source Code | https://elixir.bootlin.com/linux/latest/source | linux kernel源码 |
https://elixir.bootlin.com/u-boot/latest/source | uboot源码 |
一、内核空间与用户空间
Linux 系统将可访问的内存空间分为了两个部分,一部分是内核空间,一部分是用户空间。 操作系统和驱动程序运行在内核空间(内核态),应用程序运行在用户空间(用户态)。
1. 为什么分两部分?
(1)内核空间中的代码控制了硬件资源,用户空间中的代码只能通过内核暴露的系统调用接口来使用系统中的硬件资源,这样的设计可以保证操作系统自身的安全性和稳定性。
(2)从另一方面来说,内核空间的代码更偏向于系统管理,而用户空间中的代码更偏重 业务逻辑实现,两者的分工不同。
硬件资源管理都是在内核空间完成的,应用程序无法直接对硬件进行操作,只能通过调用相应的内核接口来完成相应的操作。比如应用程序要对磁盘上的一个文件进行读取,应用程序 可以向内核发起一个“系统调用”申请——我要读取磁盘上的文件。这个过程其实是通过一个 特殊的指令让进程从用户态进入到了内核态。在内核空间中,CPU 可以执行任何命令,包括 从磁盘上读取数据,具体过程是先把数据读取到内核空间中,然后再把数据拷贝到用户空间并 从内核态切换到用户态。此时应用程序已经从系统调用中返回并拿到了想要的数据,可以继续 往下执行了。
进程只有从用户空间切换到内核空间才可以使用系统的硬件资源,切换的方式有三种:系统调用,软中断,硬中断:
2. 用户空间和内核空间数据交换
内核空间和用户空间的内存是不能互相访问的。但是很多应用程序都需要和内核进行数据 的交换,例如应用程序使用 read 函数从驱动中读取数据,使用 write 函数向驱动中写数据,上 述功能就需要使用 copy_from_user 和 copy_to_user 两个函数来完成。copy_from_user 函数是将用户空间的数据拷贝到内核空间。copy_to_user 函数是将内核空间的数据拷贝到用户空间。
2.1 copy_to_user函数
copy_to_user函数定义在uaccess.h - include/linux/uaccess.h - copy_to_user:
1 | static __always_inline unsigned long __must_check |
该函数把内核空间的数据复制到用户空间。
参数:
- *to:指定目标地址,也就是数据存放的地址,在这里是用户空间的指针
- *from:指定源地址,也就是数据的来源,在这里是内核空间的指针
- n 是从内核空间向用户空间拷贝的字节数
返回值:内核空间向用户空间拷贝的字节数
2.2 copy_from_user函数
copy_from_user定义在uaccess.h - include/linux/uaccess.h - copy_from_user:
1 | static __always_inline unsigned long __must_check |
该函数把用户空间的数据复制到内核空间。
参数:
- *to:指定目标地址,也就是数据存放的地址,在这里是内核空间的指针
- *from:指定源地址,也就是数据的来源,在这里是用户空间的指针
- n 是从用户空间向内核空间拷贝的字节数
返回值:用户空间向内核空间拷贝的字节数
二、使用实例
1. 源码编写
1.1 chrdev_data_exchange_demo.c
点击查看详情
1 |
|
1.2 chrdev_data_exchange_demo_app.c
点击查看详情
1 |
|
2. 开发板测试
2.1 app demo实现读写
1 | 加载驱动 |
2.2 cat与echo
除了使用自己写的app demo测试,我们还可以使用cat命令查看字符设备缓冲区的内容
1 | cat /dev/dev_node |
可以通过echo命令向字符设备缓冲区写入数据:
1 | echo "hello world" > /dev/dev_node |
这个好像有点问题,不按下Ctrl + c 的话,会一直往里面写,具体原因还不知道,不过这里只是为了演示有这样的方式,后面知道原因了再补充。