c - 在用户空间和内核空间之间同步 mmap 中的队列

标签 c linux-kernel linux-device-driver mmap

我有一段代码,我正在努力提高其性能。代码在概念上很简单:每 5 毫秒从内核空间的传感器获取一些数据,用这些数据填充队列,并在用户空间中读取它进行处理。

目前,我在内核空间中有一个线程填充队列,并且我有一个具有读取文件操作功能的字符设备,可以使用copy_to_user将此数据传输到用户空间。

让我向您展示代码的一个小框架:

用户空间的观点。

// In userspace side

int process_queue (void)
{
    int fd = -1;
    int data_offset = 0;

    fd = open_char_device_from_kernel ();
    while (1)
    {
        // read from char device data from kernel
        err = read (fd, &ctx->data_queue[data_offset], SAMPLE_SIZE);
        data_offset = (data_offset + SAMPLE_SIZE) % QUEUE_DATA_SIZE;

        // process data
        (...)
    }

}

内核空间观点:

//In kernel side

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = xxx__ioctl,
    .read = xxx__read,
    .open = xxx__open,
    .release = xxx__release,
};

int kernel_thread(...)
{
    while (1)
    {
        // wait signal from irq
        wait_event_interruptible(wq_irq, flag_irq);
        // get data and fill queue
        (...)
        memcpy (&queue[write_offset], rx_buf[REGISTER], SAMPLE_SIZE);
        write_offset += ELEMENT_SIZE;
        if (write_offset == SIZE_QUEUE) write_offset = 0;
        up(&read_sem);
    }
}

static int xxx_read(struct file *filp, char __user *data, size_t len, loff_t *ppos)
{
    if (down_interruptible (&read_sem)) return (-EINTR);

    err = copy_to_user(data, &queue[read_offset], SAMPLE_SIZE);
    read_offset += ELEMENT_SIZE;
    if (read_offset == SIZE_QUEUE) read_offset = 0;
    return (ELEMENT_SIZE - err);
}

int probe_kernel_driver (...)
{
    (...)
    alloc_chrdev_region(&dev_no , 0, 1, DATA_DEVICE_NAME);
    cl = class_create(THIS_MODULE, DATA_DEVICE_NAME);
    device_create(cl, NULL, dev_no, NULL, DATA_DEVICE_NAME);
    (...)
}

这里的主要思想是删除这段代码中的copy_to_user,这非常慢。我想使用 mmap 来代替,并且我能够在两侧实现它以共享从内核空间到用户空间的数据。仍然存在一个问题:我不知道如何同步用户空间和内核空间之间的数据。用户空间进程如何知道新数据可用?

当然,我不想使用轮询或任何其他会失去 mmap 性能增益的技术。

最佳答案

用户空间进程与内核通信的替代方案(有意)受到限制。它们包括:

  • 进行系统调用
  • 在设备上执行 I/O
  • 对内核映射文件执行 I/O,例如/proc
  • 通过信号被动接收通知

如果您愿意用户空间进程阻止数据变得可用,那么其中任何一个都是可行的可能性。如果您既不能轮询也不能阻止,那么最后一个是我看到的唯一选择。

请注意,进行系统调用或执行 I/O 并不是为了检索数据本身,而只是为了检索新数据可用的信号。

您可能需要考虑为您的设备设置一个或多个合适的 ioctl,以直接支持阻止可用数据或注册一个进程,以便在新数据到达时发出信号。

但请注意,由于大量开销,用户进程和内核之间的所有交互都相当缓慢。在这种交互过程中传输的数据量有一定的相关性,但它不是唯一的因素。这就是为什么减少程序执行的系统调用次数是一个好习惯。因此,如果基于 mmap 的方法允许您减少与内核的交互次数,则可以预期它会是一个胜利,但如果它仅改变数据传输模式,但它不会那么成功。无论何种类型,仍然需要相同数量的内核交互。 (访问 mmapped 内存本身并不是与内核的交互。)

关于c - 在用户空间和内核空间之间同步 mmap 中的队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57310711/

相关文章:

linux - 使用 linux,我如何在/dev 目录之外创建一个设备文件,以允许我打开和读取它?

linux - 内核页面是否被换出?

linux - i2cdetect 在 goodix 芯片上找不到任何东西

c - 如果数组中的每个整数都不同,我该如何编写一个返回 true 的函数?

c - 什么是有效的 TC (QoS) u32 过滤器句柄 ID

c - 简单 I/O 循环调度程序

c - 编译内核模块时如何解决函数名冲突

c - 为什么向 %s 提供指针参数不起作用?

c - 是什么导致了这个 C 内存泄漏? (退出并出现错误 137)

c - 如何修复构建 Linux 内核时出现的链接错误?