编辑
2025-12-03
系统知识
0

目录

Linux PCIE驱动框架
Linux下用户态与内核态的交互

本文将基于开源项目RIFFA,分析Linux系统下PCIE设备的内核驱动是如何实现的,为开发Linux PCIE驱动提供一个参考模板,同时也可作为研究PCIE软硬件交互流程的具体实例,加深对PCIE相关知识的理解。

该开源项目的地址为:https://github.com/KastnerRG/riffa

Linux PCIE驱动框架

https://jklincn.com/posts/qemu-edu-driver/

https://www.kernel.org/doc/html/latest/PCI/index.html

https://www.qemu.org/docs/master/specs/edu.html

Linux下用户态与内核态的交互

我们考虑的场景是,数据在用户态准备,然后传递给内核PCIE驱动进行处理,所以我们首先介绍一下用户态和内核态的交互方式。

想要做用户态和内核态的交互,比较常见的做法是使用ioctl系统调用。

这一般要求内核驱动实现一个字符设备并给它注册上ioctl的文件操作回调函数。

以下给出驱动编程模板:

C
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "myioctl" #define MY_MAGIC 'x' #define MY_IOCTL_GET _IOR(MY_MAGIC, 0, int) #define MY_IOCTL_SET _IOW(MY_MAGIC, 1, int) static int device_open = 0; static int kernel_value = 123; static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret = 0; switch (cmd) { case MY_IOCTL_GET: if (copy_to_user((int __user *)arg, &kernel_value, sizeof(int))) { ret = -EFAULT; } break; case MY_IOCTL_SET: if (copy_from_user(&kernel_value, (int __user *)arg, sizeof(int))) { ret = -EFAULT; } break; default: ret = -ENOTTY; } return ret; } static struct file_operations fops = { .unlocked_ioctl = my_ioctl, // ioctl的回调函数 }; static int __init my_init(void) { register_chrdev(240, DEVICE_NAME, &fops); printk(KERN_INFO "My device loaded\n"); return 0; } static void __exit my_exit(void) { unregister_chrdev(240, DEVICE_NAME); printk(KERN_INFO "My device unloaded\n"); } module_init(my_init); module_exit(my_exit); MODULE_LICENSE("GPL");

在用户态编程中,可以这样调用ioctl实现与内核驱动的交互:

C
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define MY_MAGIC 'x' #define MY_IOCTL_GET _IOR(MY_MAGIC, 0, int) #define MY_IOCTL_SET _IOW(MY_MAGIC, 1, int) int main() { int fd; int value; fd = open("/dev/myioctl", O_RDWR); if (fd < 0) { perror("open"); return 1; } // 读取内核值 if (ioctl(fd, MY_IOCTL_GET, &value) == 0) { printf("Current kernel value: %d\n", value); } // 设置新值 value = 456; if (ioctl(fd, MY_IOCTL_SET, &value) == 0) { printf("Value set to: %d\n", value); } // 验证新值 if (ioctl(fd, MY_IOCTL_GET, &value) == 0) { printf("New kernel value: %d\n", value); } close(fd); return 0; }

这样,我们就打通了用户态和内核态的交互,此后,可以在用户程序中发送控制命令给内核驱动,随后内核驱动根据控制命令去对硬件做出合适的操作。

若是要在用户态和内核态之间进行大量的数据交换,可以采用mmap的方式,具体细节不在此赘述了,网络上对该技术有很多讨论。

本文作者:Test

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!