套接字发送有时只在无限循环中工作 (C)

Socket send works only sometimes in infinite loop (C)

我正在为 Zynq FPGA 建立 DAQ 系统的原型。我通过以太网从服务器接收数据,使用 DMA 将其写入 FIFO,反之亦然,使用两个不同的 pthreads。但是,发送操作挂起,经过一些迭代后,它正确执行然后再次挂起,同时 recv 操作成功并填充 FIFO。

有时线程仅在存在或不存在某些 printf 时才按预期工作,因此我暂时打印到 stderr。

对于可耻的代码感到抱歉,但我现在尝试替换几乎每一行以发现问题。


void *sender(void *params) {

    arguments *args = params;

    if (args->head == NULL) {
        fprintf(stderr, "[-] Head pointer not valid\n");
        exit(0);
    }
    if (args->tail == NULL) {
        fprintf(stderr, "[-] Tail pointer not valid\n");
        exit(0);
    }
    if (args->virtual_address == NULL) {
        fprintf(stderr, "[-] AXI DMA register pointer not valid\n");
        exit(0);
    }
    if (args->virtual_buffer == NULL) {
        fprintf(stderr, "[-] Send buffer pointer not valid\n");
        exit(0);
    }

    unsigned long int units_sent = 0;

    for (;;) {
        pthread_mutex_lock(args->lock);
        fprintf(stderr, "[*] Send   Head: %d    Tail: %d\n", *(args->head), *(args->tail));
        if (*(args->head) != *(args->tail)) {
            pthread_mutex_unlock(args->lock);
            int remaining = args->buffsize;
            int sent = 0;
            pthread_mutex_lock(args->lock);
            int src = FIFO_ADDR + (*(args->tail)) * args->buffsize;
            pthread_mutex_unlock(args->lock);
            if (args->verbose > 2) {
                fprintf(stderr, "[*] Sender: DMA is transferring data from 0x%x to 0x%x\n", src, SEND_ADDR);
            }
            unsigned int length = args->buffsize;
            unsigned int verb = args->verbose > 2 ? 1 : 0;
            pthread_mutex_lock(args->lock);
            while (remaining > 0) {
                length = remaining < MAX_TRANSF ? remaining : remaining % MAX_TRANSF;
                DMATransfer(args->virtual_address, src + sent, SEND_ADDR, length, verb);
                remaining -= args->buffsize;
                sent += remaining;
            }
            elements--;
            units_sent++;
            if (args->verbose > 2) {
                fprintf(stderr, "[*] %f elements in FIFO: %lu\n", ((double)(clock() - start_time)) / CLOCKS_PER_SEC, elements);
                fprintf(stderr, "[*] %f DMA tranfer to buffer: %d\n", ((double)(clock() - start_time)) / CLOCKS_PER_SEC, units_sent);
            }
            pthread_mutex_unlock(args->lock);
            remaining = args->buffsize;
            sent = 0;
            int result = 0;
            pthread_mutex_lock(args->lock);
            while (remaining > 0) {
                    result = send(args->sockfd, args->virtual_buffer + sent, remaining, 0);
                    if (result > 0) {
                        remaining -= result;
                        sent += remaining;
                    } else if (result < 0) {
                        fprintf(stderr, "[-] Error retrieving configuration from the server\n");
                    exit(0);
                    }
            }
            //memset(args->virtual_buffer, 0, args->buffsize);
            if (args->verbose > 2) {
                fprintf(stderr, "[*] %f Unit sent: %d\n", ((double)(clock() - start_time)) / CLOCKS_PER_SEC, units_sent);
            }
            if (args->verbose > 0) {
                fprintf(stderr, "[*] Packet retrieved");
            }
            if (args->verbose > 1) {
                fprintf(stderr, " content: ");
                memdump(args->virtual_buffer, args->buffsize);
            }
            if (args->verbose > 0) {
                fprintf(stderr, "\n");
            }
            *(args->tail) = (*(args->tail) + 1) % args->fifosize;
            pthread_mutex_unlock(args->lock);
            if (args->verbose > 2) {
                fprintf(stderr, "[*] %f CPU Usage: %d\n", ((double)(clock() - start_time)) / CLOCKS_PER_SEC, GetCPULoad());
            }
        }
        pthread_mutex_unlock(args->lock);
    }

}


int main(int argc, char *argv[]) {

        if (argc < 3 || argc > 5) {
                fprintf(stderr, "\nUsage: DAQTest [IP address] [fifo size]\nExample: DAQTest 192.168.1.81 64\n\n");
        fprintf(stderr, "Optional flags:    -v  Verbose (print operations)\n");
        fprintf(stderr, "       -vv Very verbose (also print data content)\n");
        fprintf(stderr, "       -vvv    Extremely verbose (also print DMA info)\n\n");
                exit(0);
        }
    if (isValidIpAddress(argv[1]) == 1) {
        fprintf(stderr, "[-] Invalid ip address\n");
        exit(0);
    }
        int fifosize = atoi(argv[2]);
        if (fifosize < 0 || fifosize > 8192) {
                fprintf(stderr, "[-] Invalid fifo size\n");
                exit(0);
        }

    char verbose = 0;
    if (argc == 4) {
        if (strcmp(argv[3], "-v") == 0) {
            verbose = 1;
        } else if (strcmp(argv[3], "-vv") == 0) {
            verbose = 2;
        } else if (strcmp(argv[3], "-vvv") == 0) {
            verbose = 3;
        } else {
            fprintf(stderr, "[-] Unwanted parameter\n");
            exit(0);
        }
    }

        struct sockaddr_in servaddr, cli;

        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (-1 == sockfd) {
                fprintf(stderr, "[-] Socket creation failed\n");
                exit(0);
        }
        bzero(&servaddr, sizeof(servaddr));

        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = inet_addr(argv[1]);
        servaddr.sin_port = htons(PORT);

    /*
    struct timeval tv;
    tv.tv_sec = TIMEOUT;
    tv.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
    */

        if (connect(sockfd, (SA * ) & servaddr, sizeof(servaddr)) != 0) {
                fprintf(stderr, "[-] Connection failed\n");
                exit(0);
        }

    fprintf(stderr, "[+] Connected to the server\n");

    int buffsize = 0;
    char* recv_buffer = (char*)&buffsize;
    int remaining = sizeof(int);
    int received = 0;
    int result = 0;
    while (remaining > 0) {
            result = recv(sockfd, recv_buffer + received, remaining, 0);
            if (result > 0) {
                remaining -= result;
                received += result;
            } else if (result == 0) {
                fprintf(stderr, "[-] Remote side closed his end of the connection before all data was received\n");
                exit(0);
            } else if (result < 0) {
                fprintf(stderr, "[-] Error retrieving configuration from the server\n");
            exit(0);
            }
    }

    //fprintf(stderr, "[*] Page size: %ld\n", sysconf(_SC_PAGE_SIZE));

        int dh = open("/dev/mem", O_RDWR | O_SYNC);

        unsigned int *virtual_address = mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, dh, VIRT_ADDR);
    unsigned int *virtual_sendbuff = mmap(NULL, buffsize, PROT_READ | PROT_WRITE, MAP_SHARED, dh, SEND_ADDR);
    unsigned int *virtual_recvbuff = mmap(NULL, buffsize, PROT_READ | PROT_WRITE, MAP_SHARED, dh, RECV_ADDR);
    unsigned int *virtual_fifo = mmap(NULL, (fifosize + 1) * buffsize, PROT_READ | PROT_WRITE, MAP_SHARED, dh, FIFO_ADDR);
    if (virtual_address == MAP_FAILED) {
        fprintf(stderr, "[-] AXI DMA registers mmap failed\n");
    }
    if (virtual_sendbuff == MAP_FAILED) {
        fprintf(stderr, "[-] Send buffer mmap failed\n");
    }
    if (virtual_sendbuff == MAP_FAILED) {
        fprintf(stderr, "[-] Send buffer mmap failed\n");
    }
    if (virtual_recvbuff == MAP_FAILED) {
        fprintf(stderr, "[-] Receiver buffer mmap failed\n");
    }
    if (virtual_fifo == MAP_FAILED) {
        fprintf(stderr, "[-] Fifo mmap failed\n");
    }
    memset(virtual_address, 0, buffsize);
    memset(virtual_sendbuff, 0, buffsize);
    memset(virtual_recvbuff, 0, buffsize);
    memset(virtual_fifo, 0, buffsize);
    int head = 0, tail = 0;

    pthread_t sendth, recvth;
    pthread_mutex_t lock;
    pthread_mutex_init(&lock, NULL);

    arguments send_args;
    send_args.virtual_address = virtual_address;
    send_args.virtual_buffer = virtual_sendbuff;
    send_args.head = &head;
    send_args.tail = &tail;
    send_args.buffsize = buffsize;
    send_args.fifosize = fifosize;
    send_args.sockfd = sockfd;
    send_args.lock = &lock;
    send_args.verbose = verbose;

    arguments recv_args;
    recv_args.virtual_address = virtual_address;
    recv_args.virtual_buffer = virtual_recvbuff;
    recv_args.head = &head;
    recv_args.tail = &tail;
    recv_args.buffsize = buffsize;
    recv_args.fifosize = fifosize;
    recv_args.sockfd = sockfd;
    recv_args.lock = &lock;
    recv_args.verbose = verbose;

    start_time = clock();

    if (pthread_create(&sendth, NULL, sender, &send_args)) {
        fprintf(stderr, "[-] Error creating sender thread\n");
        exit(0);
    }
    if (pthread_create(&recvth, NULL, receiver, &recv_args)) {
        fprintf(stderr, "[-] Error creating receiver thread\n");
        exit(0);
    }

    if (pthread_join(sendth, NULL)) {
        fprintf(stderr, "[-] Error joining sender thread\n");
        exit(0);
    }
    if (pthread_join(recvth, NULL)) {
        fprintf(stderr, "[-] Error joining receiver thread\n");
        exit(0);
    }

    pthread_mutex_destroy(&lock);
        close(sockfd);

    fprintf(stderr, "[+] Exit\n");

    return 0;

}


实际输出显示来自 recv 线程的打印,而发送线程打印

[*] Send Head: %d Tail: %d\n

几乎总是,有时它必须做什么。

sender 函数中,您有这样的代码(大大缩短)

if (*(args->head) != *(args->tail)) {
    // ...

    pthread_mutex_unlock(args->lock);
    if (args->verbose > 2) {
        fprintf(stderr, "[*] %f CPU Usage: %d\n", ((double)(clock() - start_time)) / CLOCKS_PER_SEC, GetCPULoad());
    }
}
pthread_mutex_unlock(args->lock);

这将导致 pthread_mutex_unlock(args->lock) 将在同一个锁上被调用 两次 ,中间没有锁定。

正如在例如this POSIX reference for pthread_mutex_unlock 尝试解锁不属于您的线程的互斥锁(例如解锁已经解锁的互斥锁,或解锁另一个线程持有的互斥锁)会导致 未定义的行为

这需要以某种方式避免,例如将最后一次解锁调用放在 else 子句中:

if (*(args->head) != *(args->tail)) {
    // ...

    pthread_mutex_unlock(args->lock);
    if (args->verbose > 2) {
        fprintf(stderr, "[*] %f CPU Usage: %d\n", ((double)(clock() - start_time)) / CLOCKS_PER_SEC, GetCPULoad());
    }
}
else {
    pthread_mutex_unlock(args->lock);
}