为什么写入 tun 接口这么慢?

Why is writing to a tun interface so slow?

提问人:fadedbee 提问时间:11/15/2023 更新时间:11/15/2023 访问量:50

问:

我已将检测添加到我的代码中:

state.worker_stats.counters[TUN_TX]++;
state_switch_activity(KERN_WRITE_TUN);
ssize_t wrote = write(peer->tun_fd, plaintext, plaintext_len);
state_switch_activity(USER_UDP_TO_TUN);

哪里:

static uint64_t nanonow() {
    struct timespec t;
    int err = clock_gettime(CLOCK_MONOTONIC, &t);
    if (err) {
        perror(__FILE__);
        exit(errno);
    }
    return t.tv_nsec + 1000000000 * t.tv_sec;
}

enum state_activity state_switch_activity(enum state_activity new_activity) {
    uint64_t now = nanonow();
    struct state_worker_stats *ws = &state.worker_stats; // abbreviate "state.worker_stats."

    enum state_activity old_activity = ws->current_activity;
    ws->current_activity = new_activity;
    uint64_t duration = now - ws->last_change;
    ws->last_change = now;
    ws->times[old_activity] += duration;

    return old_activity;
}

这和其他检测会导致每个数据包的统计信息:

  outbound:
      KERN_READ_TUN per TUN_RX:      3.199167 µs
    USER_TUN_TO_UDP per TUN_RX:      4.038177 µs
       USER_ENCRYPT per UDP_TX_UNIQ: 7.955924 µs
      KERN_SEND_UDP per UDP_TX:      15.789421 µs
    USER_TX_HISTORY per UDP_TX_UNIQ: 0.603579 µs

  incoming:
      KERN_RECV_UDP per UDP_RX:      6.712680 µs
    USER_UDP_TO_TUN per UDP_RX:      5.095780 µs
    USER_RX_HISTORY per UDP_RX:      1.087274 µs
       USER_DECRYPT per UDP_RX_UNIQ: 11.485002 µs
     KERN_WRITE_TUN per TUN_TX:      20.241663 µs

(我已经根据结果检查了我的用户空间和内核/系统总数,他们几乎同意,所以我对我的检测充满信心。getrusage()

对于传入数据包,时间的最大用途是将它们写入 tun0(每个数据包 20.241663 μs)。

发送 UDP 的速度也非常慢。

  • 我该怎么做才能提高 write() 和 sendmsg() 的速度?(如果我的用户空间代码不使用 CPU 时间,则每个 1500 字节数据包的系统时间为 25μs,将我限制为 480Mbps。

  • Linux 上的用户空间中是否可能存在 1Gbps+ 隧道?

  • 如果是这样,如何?

C Linux TUN

评论

1赞 dimich 11/16/2023
您确定您的测量方法能给出与吞吐量相关的结果吗?1480 字节到 TUN 接口的单次迭代确实需要 ≈10。在我的系统上为 30 μs,但循环中 1000000 次迭代每次迭代大约需要 700..800 ns。write()clock_gettime()write()
0赞 fadedbee 11/16/2023
@dimich 感谢您的评论。测量时间是数千个数据包的平均值。每个 worker 都必须接收一个 UDP 数据包,对其进行解密并将其写入 tun 接口。也就是说,写到 tun 是多次完成的,但不是在一个紧密的循环中。也许批处理写入有助于提高吞吐量,但代价是延迟?你能发布你的快速(7-800ns)代码,以防我忽略一些明显的东西吗?
1赞 dimich 11/16/2023
你不使用多队列接口(),是吗?我的测试代码在这里:https://0x0.st/Hv9a.cIFF_MULTI_QUEUE
0赞 fadedbee 11/16/2023
@dimich 是的,我用来为每个工作线程提供自己的隧道文件描述符/队列。IFF_MULTI_QUEUE
0赞 fadedbee 11/16/2023
@dimich 谢谢,我可以确认您的代码在我的台式 PC 上发送的每个数据包需要 2μs,比我的写入速度快 10 倍。我会尝试批处理我的屯。如果你把这个作为答案,我会接受的。write()

答: 暂无答案