【问题标题】:pthread_create Memory Leakspthread_create 内存泄漏
【发布时间】:2018-05-19 20:55:49
【问题描述】:

我正在尝试编写一个非常基本的多线程 TCP 客户端服务器系统,并设法使主要功能正常工作,并对除一个之外的任何内存问题进行排序。

当服务器关闭时,我将其设置为接收和处理一个 SIGINT 信号,该信号将关闭监听和 connfd 以及执行一些任意输出。

问题是 valgrind 在退出时报告内存泄漏,它说是来自 pthread_create 并且线程在退出时保持活动状态。泄漏是当时每个连接用户的 272 字节大小。

sig handler和main的代码如下:

struct timeval t1, t2;
int connfd = 0;
int listenfd = 0;
pthread_t sniffer_thread;

static void SIGhandler(int sig, siginfo_t *siginfo, void *context)
{

    pthread_join(sniffer_thread, NULL);

    shutdown(connfd, SHUT_RDWR);
    shutdown(listenfd, SHUT_RDWR);

gettimeofday(&t2, NULL);

    double totalSeconds = (double) (t2.tv_usec - t1.tv_usec) / 1000000 + (double) (t2.tv_sec - t1.tv_sec) ;

    int seconds = ((int)totalSeconds % 60);
    int minutes = ((int)totalSeconds % 3600) / 60;
    int hours = ((int)totalSeconds % 86400) / 3600;
    int days = ((int)totalSeconds % (86400 * 30)) / 86400;

    printf("\n\nServer shutdown request received");
    printf ("\nTotal server up time = %d days %d hours %d minutes and %d seconds\n\n",days, hours ,minutes , seconds);

    close(connfd);
    close(listenfd);


exit(EXIT_SUCCESS);
}


int main(void)
{

    gettimeofday(&t1, NULL);

struct sigaction act;

memset(&act, '\0', sizeof(act));

// this is a pointer to a function
act.sa_sigaction = &SIGhandler;

// the SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler
act.sa_flags = SA_SIGINFO;

if (sigaction(SIGINT, &act, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}

struct sockaddr_in serv_addr;
struct sockaddr_in client_addr;
socklen_t socksize = sizeof(struct sockaddr_in);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(50001);

bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));

if (listen(listenfd, 10) == -1) {
perror("Failed to listen");
exit(EXIT_FAILURE);
}
// end socket setup

//Accept and incoming connection
puts("Waiting for incoming connections...");
while (1) {
printf("Waiting for a client to connect...\n\n");
connfd =
    accept(listenfd, (struct sockaddr *) &client_addr, &socksize);

printf("\n\nConnection established with: %s\n", inet_ntoa(client_addr.sin_addr));
printf("%s is connected on socket:  %d\n",inet_ntoa(client_addr.sin_addr),connfd);


    // third parameter is a pointer to the thread function, fourth is its actual parameter
if (pthread_create
    (&sniffer_thread, NULL, client_handler,
     (void *) &connfd) < 0) {
    perror("could not create thread");
    exit(EXIT_FAILURE);
}


printf("Handler assigned\n\n");
}

// never reached...
exit(EXIT_SUCCESS);
} // end main()

客户端处理程序代码是:

// thread function - one instance of each for each connected client
void *client_handler(void *socket_desc)
{
    pthread_detach(pthread_self());
//Get the socket descriptor
int connfd = *(int *) socket_desc;

send_conConfirm(connfd);


char choice[8];

do {
            get_menu_choice(connfd,choice);
            switch (*choice) {
            case '1':
                printf("Executing IP/ID return for socket: %d\n",connfd);
            send_studentID(connfd);
        break;
            case '2':
                printf("Executing server time return for socket: %d\n",connfd);
            send_serverTime(connfd);
        break;
            case '3':
                printf("Executing sys info return for socket: %d\n",connfd);
            send_uname(connfd);
        break;
            case '4':
                printf("Executing file list return for socket: %d\n",connfd);
            send_filenames(connfd,choice);
        break;
            case '5':
            send_filenames(connfd,choice);
        break;
            case '6':
                printf("Disconection choice on socket: %d\n",connfd);
        break;
            default:
            printf("Client on socket %d has been disconnected\n", connfd);
                goto jump;
        break;
        }
} while (*choice != '6' );



    jump: //jump for goto statement


shutdown(connfd, SHUT_RDWR);
close(connfd);

printf("Thread %lu exiting\n", (unsigned long) pthread_self());

return 0;
}  // end client_handler()

我尝试了许多不同的方法,包括在完成时分离线程,但总是以这种内存泄漏告终。偶尔会发生内存泄漏,即使客户端只连接了服务器,服务器结束了,其他时候只有客户端连接并与服务器交互时才会发生。

【问题讨论】:

  • 可以分享client_handler函数的代码吗?
  • @JorgeOmarMedra 对其进行了编辑,现在为您包含了 client_handler。
  • 除了您特别询问的问题之外,代码还有很多问题。 IE。从socket()返回的值不检查,调用exit()不清理,pthread_detach()后主线程不能使用pthread_join()'choice'是8个字符,但是'switch'只能处理一个'int'值,而不是字符串。等等等等等等
  • 关于:goto jump; 这是一个非常糟糕的主意,只需将 'choice' 设置为 6。注意:'choice' 是一个数组,而不是指向数组的指针,因此: switch (*choice) { 应该是:switch( choice ) 和 'choice' 应该是 'int' 或单个字符。
  • 为了便于阅读和理解:1) 一致地缩进代码。在每个左大括号“{”后缩进。在每个右大括号 '}' 之前取消缩进。建议每个缩进级别为 4 个空格。 2) 通过一个空行分隔代码块(for、if、else、while、do...while、switch、case、default)。

标签: c multithreading sockets tcp memory-leaks


【解决方案1】:

你需要在你的 while 循环结束之前加入一个连接,而不是在你的信号捕获函数中

我建议您阅读本指南的最后几节,他们处理的代码存在一些问题:

  • 加入线程,这样线程就不会因为您正在使用而保持打开状态 线程,
  • 您的 connfd 可以被覆盖(因为它存在于同一 跨不同线程的内存空间)
  • 将您的 while 循环调整为 仅在连接打开时运行(将其更改为 接受返回)

http://www.binarytides.com/socket-programming-c-linux-tutorial/

【讨论】:

  • 我去实施了建议,不幸的是 272 字节仍然丢失!我继续为建议的教程提供了一个完整的实现,即使在 sigint 退出时丢失了相同的 272 个字节
  • @DarylVaDazzleMcAllister,在运行 valgrind 时,始终将其设置为忽略系统/库函数中发现的问题
  • 链接的教程有一些问题。 1) 将大多数错误消息输出到stdout 而不是stderr 2) 在将套接字传递给free() 之前,在客户端处理程序代码中未能将套接字传递给close()。 3) 有关于调用pthread_join() 的评论,但从未真正调用过该函数。 4) 线程应该调用 pthread_exit() 作为比 return 更好的选择 5) 无法检查来自 listen() 的返回值 6) 提取 IP 地址的循环 cmets 它将使用第一个但实际上使用最后一个一个
  • cont: 7) 'connection_handler()` 函数应该循环直到read() 的返回值为 0,(意味着客户端已断开连接)然后退出循环 8) 此语句:new_sock = malloc(1);没有分配足够的空间。应该是:new_sock = malloc( sizeof( int ) );,然后检查分配是否成功:if( !new_sock ) { perror( "malloc failed"; pthread_exit( NULL ); } 8) the call to listen()` 声明三个连接可以同时处于活动状态,所以sniffer_thread 应该是一个 3 的数组,并进行相关更改,所以主线程可以调用pthread_join()(续)
  • (cont) 在一个循环中,所以在所有线程都退出之前它不会退出。
猜你喜欢
  • 2011-11-08
  • 2014-09-27
  • 2021-10-01
  • 2011-09-18
  • 2013-08-05
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
相关资源
最近更新 更多