Linux内核版本导致网络延迟增加

Linux内核版本导致网络延迟增加

我正在测量两台机器之间的乒乓延迟,两者都是20.04.1-Ubuntu

我的乒乓延迟:发送方向接收方发送 udp pkt,接收方立即发回 udp pkt。当发送者收到 pkt 时,本轮将结束。

起初,我使用的是5.4.0-100-generic,测得平均延迟是13.632 us

后来,我将内核更新为5.15.0-41-generic,并测量平均延迟为20.141 us

系统其余部分的设置是相同的,我多次运行了代码。我想知道是linux内核的问题还是我犯了一些错误?

这是我在旁边的代码sender(你可以关注里面的逻辑main),至于接收者,我在符号中添加了它:

#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
#include <linux/errqueue.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>


#define UDP_MAX_LENGTH 1500
#define WARM_UP_PACKETS 10000
#define TOTAL_PACKETS 1000000

long arr2[TOTAL_PACKETS + 5];


typedef struct {
    int fd;
    int port;
    int err_no;
    struct sockaddr_in local;
    struct sockaddr_in remote;
    struct timeval time_kernel;
    struct timeval time_user;
    int64_t prev_serialnum;
} socket_info;

typedef struct {
    int64_t serialnum;

    int64_t user_time_serialnum;
    int64_t user_time;

    int64_t kernel_time_serialnum;
    int64_t kernel_time;

    size_t message_bytes;
} message_header;


static int setup_udp_sender(socket_info *inf, int port, char *address) {
    inf->port = port;
    inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (inf->fd < 0) {
        inf->err_no = errno;
        fprintf(stderr, "setup_udp_client: socket failed: %s\n",
                strerror(inf->err_no));
        return inf->fd;
    }

    int timestampOn =
        SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE |
        SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE |
        SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE |
        // SOF_TIMESTAMPING_OPT_TSONLY |
        0;
    int r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, &timestampOn,
                        sizeof timestampOn);
    if (r < 0) {
        inf->err_no = errno;
        fprintf(stderr, "setup_udp_server: setsockopt failed: %s\n",
                strerror(inf->err_no));
        return r;
    }

    inf->remote = (struct sockaddr_in){.sin_family = AF_INET,
                                        .sin_port = htons((uint16_t)port)};
    r = inet_aton(address, &inf->remote.sin_addr);
    if (r == 0) {
        fprintf(stderr, "setup_udp_client: inet_aton failed\n");
        inf->err_no = 0;
        return -1;
    }

    inf->local = (struct sockaddr_in){.sin_family = AF_INET,
                                        .sin_port = htons(0),
                                        .sin_addr.s_addr = htonl(INADDR_ANY)};
    inf->prev_serialnum = -1;

    return 0;
}
static int setup_udp_receiver(socket_info *inf, int port) {
    inf->port = port;
    inf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (inf->fd < 0) {
        inf->err_no = errno;
        fprintf(stderr, "setup_udp_server: socket failed: %s\n",
                strerror(inf->err_no));
        return inf->fd;
    }

    int timestampOn =
        SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE |
        SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE |
        SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE |
        // SOF_TIMESTAMPING_OPT_TSONLY |
        0;
    int r = setsockopt(inf->fd, SOL_SOCKET, SO_TIMESTAMPING, &timestampOn,
                        sizeof timestampOn);
    if (r < 0) {
        inf->err_no = errno;
        fprintf(stderr, "setup_udp_server: setsockopt failed: %s\n",
                strerror(inf->err_no));
        return r;
    }

    int on = 1;
    r = setsockopt(inf->fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof on);
    if (r < 0) {
        inf->err_no = errno;
        fprintf(stderr, "setup_udp_server: setsockopt2 failed: %s\n",
                strerror(inf->err_no));
        return r;
    }

    inf->local = (struct sockaddr_in){.sin_family = AF_INET,
                                        .sin_port = htons((uint16_t)port),
                                        .sin_addr.s_addr = htonl(INADDR_ANY)};
    r = bind(inf->fd, (struct sockaddr *)&inf->local, sizeof inf->local);
    if (r < 0) {
        inf->err_no = errno;
        fprintf(stderr, "setup_udp_server: bind failed: %s\n",
                strerror(inf->err_no));
        return r;
    }

    inf->prev_serialnum = -1;

    return 0;
}

static void handle_scm_timestamping(struct scm_timestamping *ts) {
    struct timespec cur;
    if( clock_gettime(CLOCK_REALTIME, &cur) == -1 ) {
      perror( "clock gettime" );
      exit( EXIT_FAILURE );
    }
    // printf("pkt: timestamp: %ld.%ld ns\n", ts->ts[0].tv_sec, ts->ts[0].tv_nsec);
    // printf("now: timestamp: %ld.%ld ns\n\n", cur.tv_sec, cur.tv_nsec);
    // printf("Latency: %.3f us\n\n\n", (cur.tv_nsec - ) * 0.001);
    // printf("timestamp: %ld ns\n", ts->ts[0].tv_nsec);

    static int count = 0;
    ++count;
    if (count > WARM_UP_PACKETS) {
        // arr[++arr[0]] = end.tv_sec * 1000000000LL + end.tv_nsec - start.tv_sec * 1000000000LL - start.tv_nsec;
        arr2[++arr2[0]] = ts->ts[0].tv_sec * 1000000000LL + ts->ts[0].tv_nsec;
    }

    // for (size_t i = 0; i < sizeof ts->ts / sizeof *ts->ts; i++) {
    //     printf("timestamp: %lld.%.9lds\n", (long long)ts->ts[i].tv_sec,
    //         ts->ts[i].tv_nsec);
    // }
    // puts("\n");
}

static void handle_time(struct msghdr *msg) {
    for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
        // printf("level=%d, type=%d, len=%zu\n", cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_len);

        if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) {
            struct sock_extended_err *ext =
                (struct sock_extended_err *)CMSG_DATA(cmsg);
            printf("errno=%d, origin=%d\n", ext->ee_errno, ext->ee_origin);
            continue;
        }

        if (cmsg->cmsg_level != SOL_SOCKET) continue;

        switch (cmsg->cmsg_type) {
            case SO_TIMESTAMPNS: {
                struct scm_timestamping *ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
                handle_scm_timestamping(ts);
            } break;
            case SO_TIMESTAMPING: {
                struct scm_timestamping *ts = (struct scm_timestamping *)CMSG_DATA(cmsg);
                handle_scm_timestamping(ts);
            } break;
            default:
            /* Ignore other cmsg options */
            break;
        }
    }
    // printf("End messages\n");
}

static ssize_t udp_receive(socket_info *inf, char *buf, size_t len) {
    char ctrl[2048];
    struct iovec iov = (struct iovec){.iov_base = buf, .iov_len = len};
    struct msghdr msg = (struct msghdr){.msg_control = ctrl,
                                        .msg_controllen = sizeof ctrl,
                                        .msg_name = &inf->remote,
                                        .msg_namelen = sizeof inf->remote,
                                        .msg_iov = &iov,
                                        .msg_iovlen = 1};
    ssize_t recv_len = recvmsg(inf->fd, &msg, 0);
    gettimeofday(&inf->time_user, NULL);

    if (recv_len < 0) {
        inf->err_no = errno;
        fprintf(stderr, "udp_receive: recvfrom failed: %s\n",
                strerror(inf->err_no));
    }

    // printf("length: %d\n", );

    // handle_time(&msg);

    return recv_len;
}

static const size_t payload_max = UDP_MAX_LENGTH - sizeof(message_header);

char type_str[50] = "specpaxos.vr.proto.PrepareMessage";
static ssize_t generate_random_message(socket_info *inf, char *buf,
                                       size_t len) {
    size_t payload_len = 10;

    buf[0] = 0x18, buf[1] = 0x03, buf[2] = 0x05, buf[3] = 0x20;
    buf[4] = 0x21;
    int x = 5;
    for(; x < 12; ++x) buf[x] = 0x00;
    for(int i = 0; i < 33; ++i) buf[x + i] = type_str[i];
    x += 33;
    buf[x++] = 0x18;
    for(int i = 0; i < 7; ++i, ++x) buf[x] = 0x00;
    for(int i = 0; i < 24; ++i, ++x) buf[x] = 0x00;

    buf[x++] = 0x02;
    for(int i = 0; i < 7; ++i, ++x) buf[x] = 0x00;
    buf[x++] = 0x00;
    buf[x++] = 0x00;

    return (ssize_t)x;
}


static socket_info registerReceiver(void) {
    socket_info inf;
    int ret = setup_udp_receiver(&inf, 12345);
    if (ret < 0) {
        exit(-1);
    }
    return inf;
}

static ssize_t udp_send(socket_info *inf, char *buf, size_t len) {
    struct iovec iov = (struct iovec){.iov_base = buf, .iov_len = len};
    struct msghdr msg = (struct msghdr){.msg_name = &inf->remote,
                                        .msg_namelen = sizeof inf->remote,
                                        .msg_iov = &iov,
                                        .msg_iovlen = 1};
    gettimeofday(&inf->time_user, NULL);
    ssize_t send_len = sendmsg(inf->fd, &msg, 0);
    if (send_len < 0) {
        inf->err_no = errno;
        fprintf(stderr, "udp_send: sendmsg failed: %s\n", strerror(inf->err_no));
    }

    return send_len;
}

ssize_t send_packet(socket_info inf) {
    char packet_buffer[4096];
    struct timespec cur;
    ssize_t len = generate_random_message(&inf, packet_buffer, sizeof packet_buffer);
    
    return udp_send(&inf, packet_buffer, (size_t)len);
}



static socket_info registerSender(char *host) {
    socket_info inf;
    int ret = setup_udp_sender(&inf, 12345, host);
    if (ret < 0) {
        exit(-1);
    }
    return inf;
}


long arr[TOTAL_PACKETS + 5];
int cmpfunc (const void * a, const void * b) {
   return ( *(int*)a - *(int*)b );
}
void queryStatistic() {
    int n = arr[0];
    qsort(arr + 1, n, sizeof(arr[0]), cmpfunc);
    double sum = 0.0;
    for(int i = 1; i <= n; ++i) sum += arr[i];

    printf("Average latency: %.3f\n", sum / n * 0.001);
    printf("90th tail latency: %.3f\n", arr[(long)(n * 0.9)] * 0.001);
    printf("99th tail latency: %.3f\n", arr[(long)(n * 0.99)] * 0.001);
    printf("99.9th tail latency: %.3f\n", arr[(long)(n * 0.999)] * 0.001);
}



int main(void) {
    socket_info inf_tx, inf_rx;
    inf_tx = registerSender("10.10.1.3");
    inf_rx = registerReceiver();

    int count = 0;
    
    char packet_buffer[4096];
    struct timespec start, end;
    uint64_t next_send_tick = 0;
    /* for receiver:
    while (1) {
        udp_receive(&inf_rx, packet_buffer, sizeof packet_buffer);
        send_packet(inf_tx);      
    }
    */
    while(1) {
        // printf("asd123www\n");
        struct timespec time = {0, 0};
        clock_gettime(CLOCK_MONOTONIC, &time);
        uint64_t tick = time.tv_sec * 1000000000LL + time.tv_nsec;
        if(tick >= next_send_tick) {

            clock_gettime(CLOCK_REALTIME, &start);
            send_packet(inf_tx);
            udp_receive(&inf_rx, packet_buffer, sizeof packet_buffer);
            clock_gettime(CLOCK_REALTIME, &end);

            ++count;
            if (count > WARM_UP_PACKETS) {
                arr[++arr[0]] = end.tv_sec * 1000000000LL + end.tv_nsec - start.tv_sec * 1000000000LL - start.tv_nsec;
                // arr[++arr[0]] = end.tv_sec * 1000000000LL + end.tv_nsec;
            }
            // printf("%ld\n", arr[arr[0]]);

            // printf("%.3f\n", (end.tv_sec * 1000000000LL + end.tv_nsec - start.tv_sec * 1000000000LL - start.tv_nsec) * 0.001);
            next_send_tick = tick + 1500; 

            if (count == TOTAL_PACKETS) break;
        }
    }
    // for(int i = 1; i <= arr[0]; ++i) arr[i] -= arr2[i];

    queryStatistic();
}

相关内容