北冰洋以北 发表于 2025-4-1 11:38:06

Linux C++ 使用 io_uring 技能批量读取 tun 文件形貌符的数据。

以下是参考的实现代码,IO_URING 操作必须要举行按页大小对齐(仅在O_DIRECT直接I/O下),不能是非对称的,一样寻常大多数操作系统页大小为:4KB。

批量读取、writev 批量简写。 
                  static constexpr int MTU = ITap::Mtu;

                  struct io_uring ring;
                  memset(&ring, 0, sizeof(ring));

                  struct iovec tun_write_iov_data;
                  Byte packets;

                  SsmtThreadLocalTls& tls = ssmt_tls_;
                  tls.tun_write_iov_data = tun_write_iov_data;

                  if (io_uring_queue_init(PPP_TUN_ENTRIES_PACKET_SIZE, &ring, 0) < 0) {
                        Dispose();
                        return false;
                  }

                  bool any = false;
                  for (int i = 0; i < PPP_TUN_ENTRIES_PACKET_SIZE; i++) {
                        struct io_uring_sqe* sqe = io_uring_get_sqe(&ring);
                        if (NULL == sqe) {
                            goto LABEL_exit;
                        }

                        io_uring_prep_read(sqe, tun_fd, packets, MTU, 0);
                        sqe->user_data = (__u64)&packets;
                  }

                  io_uring_submit(&ring);
                  while (!disposed_) {
                        struct io_uring_cqe* cqes;
                        __u32 count = io_uring_peek_batch_cqe(&ring, cqes, PPP_TUN_ENTRIES_PACKET_SIZE);
                        if (count == 0) {
                            int err = io_uring_wait_cqe(&ring, &cqes);
                            if (err == -EINTR) {
                              continue;
                            }
                            elif(err >= 0) {
                              count = 1;
                            }
                            else {
                              break;
                            }
                        }
                        
                        ssmt_tls_.tun_wirte_iov_size = 0;
                        for (__u32 i = 0; i < count; i++) {
                            struct io_uring_cqe* cqe = cqes;
                            assert(NULL != cqe);

                            Byte* packet = (Byte*)cqe->user_data;
                            if (int bytes_transferred = cqe->res; bytes_transferred > 0) {
                              PacketInputEventArgs e{ packet, bytes_transferred };
                              tls.tun_fd_ = tun_fd;
                              OnInput(e);
                            }

                            struct io_uring_sqe* sqe = io_uring_get_sqe(&ring);
                            assert(NULL != sqe);

                            io_uring_prep_read(sqe, tun_fd, packet, MTU, 0);
                            sqe->user_data = (__u64)packet;
                        
                            io_uring_cqe_seen(&ring, cqe);
                        }

                        tls.tun_fd_ = -1;
                        io_uring_submit(&ring);
                        
                        if (tls.tun_wirte_iov_size > 0) {
                            int err = writev(tun_fd, tun_write_iov_data, tls.tun_wirte_iov_size);
                            for (int i = 0; i < tls.tun_wirte_iov_size; i++) {
                              struct iovec& iov = tls.tun_write_iov_data;
                              Mfree(iov.iov_base);
                            }

                            tls.tun_wirte_iov_size = 0;
                            if (err < 0) {
                              break;
                            }
                        }
                  }

                LABEL_exit:
                  io_uring_queue_exit(&ring);
                  Dispose();
                  return any; write 批量收集或超限写出:
      bool TapLinux::Output(const void* packet, int packet_size) noexcept {
            if (NULL == packet || packet_size < 1) {
                return false;
            }

            int disposed = disposed_.load();
            if (disposed != FALSE) {
                return false;
            }
            
            // https://man7.org/linux/man-pages/man2/write.2.html
            int tun = static_cast<int>(reinterpret_cast<std::intptr_t>(GetHandle()));
            if (Ssmt()) {
                SsmtThreadLocalTls& tls = ssmt_tls_;
                int fd = tls.tun_fd_;
                if (fd != -1) {
                  tun = fd;

#if defined(BOOST_ASIO_HAS_IO_URING)
                  void* packet_copy = Malloc(packet_size);
                  if (NULL == packet_copy) {
                        return false;
                  }

                  struct iovec& iov = tls.tun_write_iov_data;
                  memcpy(packet_copy, packet, packet_size);

                  iov.iov_base = packet_copy;
                  iov.iov_len = packet_size;

                  if (tls.tun_wirte_iov_size >= PPP_TUN_ENTRIES_PACKET_SIZE) {
                        int err = writev(fd, tls.tun_write_iov_data, tls.tun_wirte_iov_size);
                        for (int i = 0; i < tls.tun_wirte_iov_size; i++) {
                            struct iovec& iov = tls.tun_write_iov_data;
                            Mfree(iov.iov_base);
                        }

                        tls.tun_wirte_iov_size = 0;
                        if (err < 0) {
                            return false;
                        }
                  }
#endif
                }
            }

            ssize_t bytes_transferred = ::write(tun, (void*)packet, (size_t)packet_size);
            return bytes_transferred > -1;
      }

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Linux C++ 使用 io_uring 技能批量读取 tun 文件形貌符的数据。