Small problem · Select always returns 0 for several reasons

describe

        For socket programming, select\poll\epoll is indispensable. Because select is easy to use, it is generally selected for applications that do not require high concurrent connections, especially for I\O device monitoring.

        The following is the source code of a listening socket in a recent project (logic simplification), but after merging this code into the version, the CPU usage has been as high as 80% when running it. Logically speaking, the use of select is to reduce the CPU usage. When there is no data, the monitored events are uniformly handed over to the kernel for processing, and there should not always be such a high usage rate.

        By adding printing where necessary, it is found that the waiting time of wait is always 0, and the return value of select is always 0, so it is equivalent to not waiting, and it will always wake up the thread for processing.

        The processing method is very simple. Add the wait assignment to the while loop. When the select is waiting, the kernel has been reducing the wait, and wakes up the thread when it is reduced to 0.

        After solving this point, the above problems will still occur, so I thought of trying it out: put the FD_SET operation before the select, that is, put it in the while loop. Unexpectedly, such a change will directly solve the problem.

        After select returns, the fd that was added before but no event occurred will be cleared from fd_set, so it is necessary to add the concerned fd to FD_SET again before calling select again.

    int client = -1;
    wait.tv_sec = 1; wait.tv_usec = 0;
    FD_ZERO(&read_fdset);
    FD_SET(listenfd, &read_fdset);
    while (1) {
        if (clientfd != -1) FD_SET(clientfd, &read_fdset);
        printf("select listen %ld\n", wait.tv_sec);
        iRet = select(FD_SETSIZE, &read_fdset, NULL, NULL, &wait);
        if (FD_ISSET(listenfd, &read_fdset)) {
            clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
            //...
        } else if (FD_ISSET(clientfd, &read_fdset)) {
            printf("clientfd\n");
            //...
        }
    }

Solution

Problem solving: Before calling select each time, call FD_ZERO to clear the readable file handle set, and call FD_SET to add the TCP socket to the fd_set type set, and reset the time parameter.

    int client = -1;
    while (1) {
        wait.tv_sec = 1; wait.tv_usec = 0;
        FD_ZERO(&read_fdset);
        FD_SET(listenfd, &read_fdset);
        if (clientfd != -1) FD_SET(clientfd, &read_fdset);
        printf("select listen %ld\n", wait.tv_sec);
        iRet = select(FD_SETSIZE, &read_fdset, NULL, NULL, &wait);
        if (FD_ISSET(listenfd, &read_fdset)) {
            clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
            //...
        } else if (FD_ISSET(clientfd, &read_fdset)) {
            printf("clientfd\n");
            //...
        }
    }

There are three possible reasons why select returns a value of 0:

  1. Before calling select, FD_ZERO is not called to clear the readable file handle set, and FD_SET is used to add the TCP socket to the fd_set type set. --- Because after the select returns, the fd that was previously added but no event occurred will be cleared from fd_set
  2. Before calling select, the time parameter is not reset. --- Because when select is waiting, the kernel has been reducing the wait, and wakes up the thread when it is reduced to 0.
  3. The node that select listens to is set to non-blocking, causing select to be unable to block and wait. --- Because select is blocked according to the wait time by default, but if the listening node is non-blocking, select will also be non-blocking.

Guess you like

Origin blog.csdn.net/m0_64560763/article/details/131557035