Байты все еще доступны при использовании pthreads - PullRequest
0 голосов
/ 30 августа 2018

поэтому я снова обновляю код, как рекомендовано никудышным, чтобы вы могли видеть что-то, что вы можете скомпилировать и запустить. Я немного очистил код от других вещей, но проблема осталась. Выполняя его с помощью valgrind, когда я даю сигнал SIGINT, программа завершается правильной печатью завершенных потоков, но иногда она заканчивается без утечек памяти, другие сообщают о некоторых утечках памяти, которые я оставлять позади. На данный момент я делаю тесты без подключенных клиентов.


Я пытаюсь создать многопоточный сервер, который использует функцию select () и пул потоков для университетского проекта. Выполняя мой код в Valgrind, я вижу, что после отправки сигнала SIGINT иногда он завершается без ошибок, а в других случаях выдает 4 «все еще достижимых» ошибки.

Я запускаю программу с valgrind, указав следующие флаги:

 valgrind --leak-check=full --show-leak-kinds=all

Это новый код

#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>

#include <errno.h>
#include <sys/select.h>
#include <ctype.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>

#define UNIXPATH "./tmp/socket"
#define THREADSINPOOL 8
#define MAXCONNECTION 32
#define SYSCALL(r,c,msg) \
    if((r=c)==-1) {perror(msg); exit(errno); }


typedef struct _elem{
    long connfd;
    struct _elem *next;
}elem;

static volatile sig_atomic_t terminate=0;

int try=0;
int nelem=0;
elem *head=NULL;
elem *corr=NULL;
elem *tmp=NULL;


pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;  
pthread_cond_t empty=PTHREAD_COND_INITIALIZER;
pthread_cond_t full=PTHREAD_COND_INITIALIZER;

int update(fd_set set, int fdmax){
    for(int i=(fdmax-1);i>=0; i--)
        if(FD_ISSET(i,&set)) return i;
    return -1;
}

void *threadF(void *arg){
    pthread_t self;
    long connfd=0;

    while(1){
        pthread_mutex_lock(&mtx);
        if(try==1){
            pthread_mutex_unlock(&mtx);
            break;
        } 
        while(nelem==0 && try==0)
            pthread_cond_wait(&empty,&mtx);
        if(try==1){ 
            pthread_mutex_unlock(&mtx);
            break;
        }
        nelem--;

        tmp=head;
        connfd=tmp->connfd;
        tmp=tmp->next;
        free(head); 
        head=tmp;

        pthread_cond_signal(&full);
        pthread_mutex_unlock(&mtx);

        //read & write on tmp->connfd

        self = pthread_self();
        printf("tmp->connfd: %lu on thread: %lu\n", connfd,self);
        close(connfd);
    }
    pthread_exit(0);        
}

void insert(long connfd){
    pthread_mutex_lock(&mtx);
    while(nelem==MAXCONNECTION && try==0)
        pthread_cond_wait(&full,&mtx);
    if(try==1){ 
        pthread_mutex_unlock(&mtx);

    }else{
        nelem++;
        elem *new=malloc(sizeof(elem));
        new->connfd=connfd;
        new->next=NULL;
        if(head==NULL){
            head=new;
            corr=head;
        }else{
            corr->next=new;
            corr=corr->next;
        }
        pthread_cond_signal(&empty);
        pthread_mutex_unlock(&mtx);
    }
}

pthread_t *createarray(pthread_t *array){
    int i,err;
    pthread_t id;
    for(i=0;i<THREADSINPOOL;i++){
        if((err=pthread_create(&id,NULL,&threadF,NULL))!=0){
            fprintf(stderr,"thread\n");
            exit(errno);
        }
        array[i]=id;
    }
    return array;

}

void destroyArray(pthread_t *array){
    void* value=NULL;
    int i,tmp;
    for (i = 0; i < THREADSINPOOL; i++){
        if ((tmp=pthread_join(array[i],&value)) != 0)
                printf("error join: %d\n", tmp);
        printf("thread: %lu terminated\n",array[i]);
    }
    free(array);
}

void cleanup(){
    unlink(UNIXPATH);
}

void sigint_handler(int signmum){
    terminate=1;

}

int main(int argc, char *argv[]) {
    cleanup();
    atexit(cleanup);
    int notused;

    sigset_t setmask;
    SYSCALL(notused,sigemptyset(&setmask),"SIGEMPTYSET");
    SYSCALL(notused,sigaddset(&setmask,SIGINT),"SIGADDSET");
    SYSCALL(notused,pthread_sigmask(SIG_SETMASK,&setmask,NULL),"pthread_sigmask");

    //create threadspool
    pthread_t *array=malloc(THREADSINPOOL*sizeof(pthread_t));
    array=createarray(array);

    struct sigaction s;
    memset(&s,0,sizeof(s));

    s.sa_handler=sigint_handler;
    s.sa_flags=SA_RESTART;
    SYSCALL(notused,sigaction(SIGINT,&s,NULL),"SIGINT");

    SYSCALL(notused,sigemptyset(&setmask),"SIGEMPTYSET");
    SYSCALL(notused,pthread_sigmask(SIG_SETMASK,&setmask,NULL),"sigmask");


    int listenfd;
    SYSCALL(listenfd, socket(AF_UNIX, SOCK_STREAM, 0), "socket");

    struct sockaddr_un serv_addr;
    memset(&serv_addr, '0', sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    strncpy(serv_addr.sun_path, UNIXPATH, strlen(UNIXPATH)+1);

    SYSCALL(notused, bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr)), "bind"); 
    SYSCALL(notused, listen(listenfd, MAXCONNECTION), "listen");

    long fd_c;
    int i=0; 

    fd_set set, rdset;

    FD_ZERO(&rdset);
    FD_ZERO (&set); 
    FD_SET(listenfd,&set); 

    int fd_num=listenfd; 

    struct timeval tv;
    tv.tv_sec=3;
    tv.tv_usec=0;

    while(1){
        if(terminate==1){
            break;
        }
        rdset=set; 
        if((notused=select(fd_num+1, &rdset, NULL, NULL, &tv))==-1){
            if(errno==EINTR){
                if(terminate==1){
                    break;
                }else{
                    perror("select");
                    exit(EXIT_FAILURE);
                }

            }

        }

        for(i=0; i<=fd_num;i++){
            if(FD_ISSET(i,&rdset)){
                if(i==listenfd){
                    SYSCALL(fd_c, accept(listenfd, (struct sockaddr*)NULL ,NULL), "accept");
                    printf("connection accepted\n");
                    FD_SET(fd_c, &set);
                    if(fd_c>fd_num) fd_num=fd_c;
                }else{
                    fd_c=i;
                }
                insert(fd_c);
                FD_CLR(fd_c,&set);
                if(fd_c==fd_num) fd_num=update(set,fd_num);

            }
        }
    }
    pthread_mutex_lock(&mtx);
    try=1;
    pthread_mutex_unlock(&mtx);
    close(listenfd);
    pthread_cond_broadcast(&empty);
    pthread_cond_signal(&full);
    // join on the thread and free(array)
    destroyArray(array);
    if(tmp)
        free(tmp);
    return 0;
}

Наконец, это вывод Valgrind, который я иногда получаю при выполнении кода:

==7578== Memcheck, a memory error detector
==7578== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==7578== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==7578== Command: ./server
==7578== 
^Cthread: 100800256 terminated
thread: 109192960 terminated
thread: 117585664 terminated
thread: 125978368 terminated
thread: 134371072 terminated
thread: 142763776 terminated
thread: 151156480 terminated
thread: 159549184 terminated
==7578== 
==7578== HEAP SUMMARY:
==7578==     in use at exit: 1,638 bytes in 4 blocks
==7578==   total heap usage: 15 allocs, 11 frees, 4,958 bytes allocated
==7578== 
==7578== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==7578==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578==    by 0x401CF99: strdup (strdup.c:42)
==7578==    by 0x40187DE: _dl_load_cache_lookup (dl-cache.c:311)
==7578==    by 0x4009168: _dl_map_object (dl-load.c:2364)
==7578==    by 0x4015576: dl_open_worker (dl-open.c:237)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x4014DA8: _dl_open (dl-open.c:660)
==7578==    by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x519A663: dlerror_run (dl-libc.c:46)
==7578==    by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578==    by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578==    by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578== 
==7578== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==7578==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578==    by 0x400BEF3: _dl_new_object (dl-object.c:165)
==7578==    by 0x400650C: _dl_map_object_from_fd (dl-load.c:1028)
==7578==    by 0x4008C26: _dl_map_object (dl-load.c:2498)
==7578==    by 0x4015576: dl_open_worker (dl-open.c:237)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x4014DA8: _dl_open (dl-open.c:660)
==7578==    by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x519A663: dlerror_run (dl-libc.c:46)
==7578==    by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578==    by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578==    by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578== 
==7578== 384 bytes in 1 blocks are still reachable in loss record 3 of 4
==7578==    at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578==    by 0x40120BD: _dl_check_map_versions (dl-version.c:293)
==7578==    by 0x4015B18: dl_open_worker (dl-open.c:286)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x4014DA8: _dl_open (dl-open.c:660)
==7578==    by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x519A663: dlerror_run (dl-libc.c:46)
==7578==    by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578==    by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578==    by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578==    by 0x4E4A06F: __pthread_unwind (unwind.c:121)
==7578==    by 0x4E42844: __do_cancel (pthreadP.h:283)
==7578==    by 0x4E42844: pthread_exit (pthread_exit.c:28)
==7578== 
==7578== 1,182 bytes in 1 blocks are still reachable in loss record 4 of 4
==7578==    at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==7578==    by 0x400BBF5: _dl_new_object (dl-object.c:75)
==7578==    by 0x400650C: _dl_map_object_from_fd (dl-load.c:1028)
==7578==    by 0x4008C26: _dl_map_object (dl-load.c:2498)
==7578==    by 0x4015576: dl_open_worker (dl-open.c:237)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x4014DA8: _dl_open (dl-open.c:660)
==7578==    by 0x519A5AC: do_dlopen (dl-libc.c:87)
==7578==    by 0x4010563: _dl_catch_error (dl-error.c:187)
==7578==    by 0x519A663: dlerror_run (dl-libc.c:46)
==7578==    by 0x519A663: __libc_dlopen_mode (dl-libc.c:163)
==7578==    by 0x4E4B91A: pthread_cancel_init (unwind-forcedunwind.c:52)
==7578==    by 0x4E4BB03: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==7578== 
==7578== LEAK SUMMARY:
==7578==    definitely lost: 0 bytes in 0 blocks
==7578==    indirectly lost: 0 bytes in 0 blocks
==7578==      possibly lost: 0 bytes in 0 blocks
==7578==    still reachable: 1,638 bytes in 4 blocks
==7578==         suppressed: 0 bytes in 0 blocks
==7578== 
==7578== For counts of detected and suppressed errors, rerun with: -v
==7578== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Новый вывод Valgrind

==3920== Memcheck, a memory error detector
==3920== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3920== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==3920== Command: ./server
==3920== 
^C
==3920== 
==3920== Process terminating with default action of signal 2 (SIGINT)
==3920==    at 0x4E3FE6D: __pthread_initialize_minimal (nptl-init.c:433)
==3920==    by 0x4E3F588: ??? (in /lib/x86_64-linux-gnu/libpthread-2.23.so)
==3920==    by 0x400044F: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==3920==    by 0x4010679: call_init.part.0 (dl-init.c:58)
==3920==    by 0x4010834: call_init (dl-init.c:104)
==3920==    by 0x4010834: _dl_init (dl-init.c:87)
==3920==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==3920== 
==3920== HEAP SUMMARY:
==3920==     in use at exit: 0 bytes in 0 blocks
==3920==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3920== 
==3920== All heap blocks were freed -- no leaks are possible
==3920== 
==3920== For counts of detected and suppressed errors, rerun with: -v
==3920== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

1 Ответ

0 голосов
/ 04 сентября 2018

но иногда это заканчивается без утечек памяти, другие сообщают о некоторых утечках памяти, которые я оставляю позади.

Вероятно, это связано с тем, что программа не очищает связанный список принятых соединений перед завершением.

Но у вас есть другие проблемы в вашем коде.

  • Во-первых, использование переменной terminate не является потокобезопасным. Не смущайтесь именем типа sig_atomic_t: это , а не атомарный тип данных . Это безопасно для использования обработчиком сигнала для связи с потоком, который получил сигнал, но это не безопасно для связи между потоками без использования соответствующего объекта синхронизации, такого как мьютекс. Более того, хотя это необходимо для использования с обработчиком сигнала, делая его volatile также не обеспечивает безопасности потока . Это проблема для вашего текущего кода, потому что когда вы отправляете SIGINT, он может обрабатываться любым потоком.

    Я предлагаю решить эту проблему, заблокировав SIGINT во всех, кроме основного потока. Когда процесс получает сигнал, ориентированный на процесс, он ставится в очередь для потока, который в это время не заблокирован, поэтому вам не нужно беспокоиться об успешном получении сигнала. Каждый поток имеет свою собственную маску сигнала, начальное значение которой наследуется от его родительского потока. Поэтому я предлагаю заблокировать SIGINT в главном потоке перед запуском всех рабочих, а затем разблокировать его только в главном потоке, как только все рабочие будут запущены.

  • Во-вторых, все обращаются к изменяемым неатомарным данным, разделяемым между потоками, которые должны быть защищены соответствующим объектом синхронизации. Вы выбрали мьютекс для этой цели, но ваша программа выполняет некоторые действия без своей защиты.

    • К общим данным относятся как минимум переменные try, nelem и все содержимое связанного списка, озаглавленное head.

    • threadF() проверки try без удержания мьютекса

    • main() устанавливает try без удержания мьютекса

    Поэтому поведение вашей программы не определено. На практике кажется маловероятным, что это имеет какое-либо отношение к «утечке», но это может привести к тому, что ваша программа иногда не сможет завершиться корректно. В принципе, все может случиться. На практике наиболее вероятный эффект заключается в том, что ваша программа иногда не может завершить работу чисто.

...