Errno 24: слишком много открытых файлов. Мои темы не закрывают свои гнезда? - PullRequest
2 голосов
/ 07 октября 2019

Я написал программу на C, которая должна действовать как примитивный сервер, и мне нужна помощь с проблемой распределения сокетов / ресурсов.

Функция main() открывает один прослушивающий сокет, вызывает accept() для каждого входящего соединения, затем раскручивает отдельный поток, чтобы обработать фактическую обработку клиентского запроса:

typedef struct {
        int sock;
        struct sockaddr address;
        int addr_len;
} connection_t;


int main(int argc, char ** argv)
{
        int             sock = -1;
        struct          sockaddr_in address;
        int             port = 12345;
        connection_t*   connection;
        pthread_t       thread;
        int             cnt = 0;

        signal(SIGINT, handle_sigint);

        // Create the listening socket
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sock <= 0){
                fprintf(stderr, "%s: error: cannot create socket\n", argv[0]);
                return -3;
        }

        // Bind socket to port
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        address.sin_port = htons(port);
        if (bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0){
                fprintf(stderr, "%s: error: cannot bind socket to port %d\n", argv[0], port);
                return -4;
        }

        // Listen on the port
        if (listen(sock, 5) < 0){
                fprintf(stderr, "%s: error: cannot listen on port\n", argv[0]);
                return -5;
        }

        printf( "...starting loop...\n" );
        while (1){
                // Accept incoming connections
                connection = (connection_t *)malloc(sizeof(connection_t));
                connection->addr_len = 20;
                connection->sock = accept(sock, &connection->address, &connection->addr_len);

                if (connection->sock <= 0){
                        printf("Connection FAILED :: Value of errno: %d\n ", errno);
                        free(connection);
                }
                else{
                        // Start a new thread but do not wait for it
                        pthread_create(&thread, 0, process, (void *)connection);
                        pthread_detach(thread);
                }
        }
        return 0;
}

Выше должно быть C Network Programming 101. И это прекрасно работает… пока я не начну отправлятьбольшое количество клиентских запросов. Когда я это вижу, я вижу это:

me@mylinux:/home/me/myServer
...starting loop...
 Connection FAILED :: Value of errno: 24
 Connection FAILED :: Value of errno: 24
 Connection FAILED :: Value of errno: 24
 Connection FAILED :: Value of errno: 24

Хмм. Поработав как первопроходец в группе более ранних подключений, accept() внезапно задыхается.

Быстрый поиск в Google показывает, что Errno 24 равно EMFILE: «Слишком много открытых файлов». И поиск других сообщений в StackOverflow ( здесь ) предполагает, что каждый раз, когда мой код вызывает accept(), это создает файловый дескриптор дляновое открытое соединение. Это означает, что каждый новый поток претендует на новый дескриптор файла.

Я предполагаю, что проблема в том, что потоки не очищаются после себя. Они читают из сокета, выполняют свою работу, затем завершают работу, не сообщая системе, что их FD больше не нужны. Таким образом, система поддерживает все эти FD, и я получаю лавину ошибок Errno 24 после первых 4096 подключений.

(я выполнил «ulimit -n 4096» на моем компьютере с Linux).

Итак, вот функция process(), которая направляет каждый поток;Я опустил часть разбора строки, потому что здесь она выходит за рамки. Обратите внимание, что после того, как поток выполняет свою работу, он пытается убрать после себя:

void * process(void * ptr)
{
        char * buffer;
        int len;
        connection_t * conn;          // See above for this data type
        long addr = 0;

        conn = (connection_t *)ptr;

        // read length of message
        read(conn->sock, &len, sizeof(int));
        if (len > 0)
        {
                addr = (long)((struct sockaddr_in *)&conn->address)->sin_addr.s_addr;
                buffer = (char *)malloc((len+1)*sizeof(char));
                buffer[len] = 0;

                // Read the message from the socket
                read(conn->sock, buffer, len);

                // Parse client message
                parseCliMsg( buffer, len );

                free(buffer);
        }

        // Close socket and clean up
        close(conn->sock);
        free(conn);
        pthread_exit(0);
}

Кто-нибудь видит, где я иду не так?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...