以下是参考的实现代码,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[PPP_TUN_ENTRIES_PACKET_SIZE];
Byte packets[PPP_TUN_ENTRIES_PACKET_SIZE][MTU];
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[i], MTU, 0);
sqe->user_data = (__u64)&packets[i][0];
}
io_uring_submit(&ring);
while (!disposed_) {
struct io_uring_cqe* cqes[PPP_TUN_ENTRIES_PACKET_SIZE];
__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[0]);
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[i];
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[i];
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[tls.tun_wirte_iov_size++];
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[i];
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;
}