我有一段代码,我正在努力提高其性能。代码在概念上很简单:每 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
的方法允许您减少与内核的交互次数,则可以预期它会是一个胜利,但如果它仅改变数据传输模式,但它不会那么成功。无论何种类型,仍然需要相同数量的内核交互。 (访问 mmap
ped 内存本身并不是与内核的交互。)
关于c - 在用户空间和内核空间之间同步 mmap 中的队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57310711/