性能Read()和Write()往返于Linux SKB

时间:2017-07-07 06:19:05

标签: linux tcp kernel

基于标准Linux系统,其中有用户态应用程序和内核网络堆栈。我已经读过,在CPU周期方面,将帧从用户空间移动到内核空间(反之亦然)会很昂贵。

我的问题是,

  1. 为什么?并在一个方向上移动框架(即从用户移动到 内核)有更大的影响。
  2. 另外,当你遇到不同的事情时 进入基于TAP的界面。因为框架仍将继续 用户/内核空间之间。空间问题是否适用,或者是否存在某种形式的零拷贝?

2 个答案:

答案 0 :(得分:4)

在线解决问题:

  

为什么呢?并在一个方向上移动框架(即从用户移动到   内核)有更大的影响。

Moving to/from user/kernel spaces is expensive因为操作系统必须:

  • 验证复制操作的指针。
  • 传输实际数据。
  • 了解在用户/内核模式之间转换所涉及的常规成本。

这有一些例外,例如你的驱动程序是否实现了strategy such as "page flipping",它有效地重新映射了一块/内存页面,以便用户空间应用程序可以访问它。这已经足够接近"进行零拷贝操作。

With respect to copy_to_user/copy_from_user performance, the performance of the two functions is apparently comparable.

  

此外,当您进入基于TAP的界面时,情况会有何不同。如   框架仍将在用户/内核空间之间进行。做空间   关注是否适用,或者是否存在某种形式的零拷贝?

使用基于TUN / TAP的界面时,除非您正在使用某种DMA,页面翻转等,否则应用相同的注意事项;逻辑。

答案 1 :(得分:3)

上下文切换

将帧从用户空间移动到内核空间称为上下文切换,这通常是由系统调用(调用int 0x80中断)引起的。

  • 发生中断,进入内核空间;
  • 当中断发生时,操作系统将存储所有寄存器'值进入线程的内核堆栈dsesfseaxcr3
  • 然后它像函数调用一样跳转到IRQ处理程序;
  • 通过一些常见的IRQ执行路径,它会选择一些算法运行的下一个线程;
  • 运行时信息(所有寄存器)从下一个线程加载;
  • 返回用户空间;

正如我们所看到的,在将帧移入/移出内核时我们会做很多工作,这比简单的函数调用要多得多(只需设置ebpesp,{{ 1}})。这就是为什么这种行为相对耗时。

虚拟设备

作为虚拟网络设备,与写入eip相比,写入TAP没有区别。

如果您写入TAP,os将像上层描述一样被中断,然后它会将您的参数复制到内核中并阻止当前线程(阻塞IO)。将以某种方式(例如消息队列)通知内核驱动程序线程以接收参数并使用它。

在Andorid中,存在一些zero-copy system call,在我的演示实现中,这可以通过用户和内核之间的地址转换来完成。因为内核和用户线程不共享相同的地址空间并且用户线程的数据可能被更改,所以我们通常将数据复制到内核中。因此,如果我们符合条件,我们可以避免复制:

  • 必须阻止此系统调用,即数据不会发生变化;
  • 按页表在地址之间进行转换,即内核可以引用正确的数据;

代码

以下是我的演示操作系统中的代码,如果您对此问题感兴趣,则与此问题相关: