【问题标题】:C - Mysterious Segmentation Fault with ThreadingC - 带有线程的神秘分段错误
【发布时间】:2011-08-02 11:19:04
【问题描述】:

这是我一直在研究的一个简单程序,它监听一个套接字,并启动一个新线程来处理与所述套接字的每个连接。 在我的 while 循环中,我遇到了一个分段错误,这与 pthread_create 有关(如果我正确注释掉程序循环的那一行)。我对指针的了解充其量只是平庸,使用 gdb 进行调试并没有产生任何价值。这是 gdb 的输出:

#0  0x0000000000000000 in ?? ()
#1  0x000000080064f4f1 in pthread_getprio () from /lib/libthr.so.3
#2  0x0000000000000000 in ?? ()
Error accessing memory address 0x7fffffbff000: Bad address.

程序成功通过一次while循环,并正确接收并响应套接字上的连接,但在进入第二个while循环之前,程序因Segmentation Fault错误而失败。

这是我的程序的精简版:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <pthread.h>

#define UNIX_PATH_MAX 100
#define SOCK_PATH "/tmp/demo_socket"

/*===============> CONNECTION HANDLER FUNCTION <=================*/
void *connection_handler(int connection_fd)
{
    int nbytes;
    char buffer[256];

nbytes = read(connection_fd, buffer, 256); 
buffer[nbytes] = 0;
printf("\tMESSAGE FROM CLIENT: %s\n", buffer);

nbytes = snprintf(buffer, 256, "Hello from the server!");
write(connection_fd, buffer, nbytes);

close(connection_fd);

return;
}


/*==========================> MAIN <=============================*/
int main(void)
{
    struct sockaddr_un addr; //socket address information
    int sock_fd, conn_fd; //socket file descriptors
    socklen_t addr_len = sizeof(struct sockaddr_un); //size of sockaddr_un structure
    pid_t child_pid; //pid holder
    pthread_t thread; // thread identifier 


sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); 
if (sock_fd < 0)
    return 1;
unlink(SOCK_PATH);



memset(&addr, 0, addr_len); 

addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1); // Copies up to sizeof(addr.sun_path)-1 bytes from SOCK_PATH into addr.sun_path 
printf("> Socket sun_family = %d (AF_UNIX), Socket sun_path = %s ...\n", addr.sun_family, addr.sun_path);


/*----------------------FAIL CHECKS-------------------------*/
if (bind(sock_fd, (struct sockaddr *) &addr, addr_len) != 0) 
    return 1;
if (listen(sock_fd, 5) != 0)
    return 1;

printf("> Listening to socket bound at %s ...\n\n", SOCK_PATH);


/*--------------------WHILE LOOP----------------------------*/
while ( (conn_fd = accept(sock_fd, (struct sockaddr *) &addr, &addr_len)) > -1) {

    pthread_create(&thread , NULL, connection_handler(conn_fd), NULL); 

    printf("> Closing connection at %d inside server process ...\n", conn_fd);
    close(conn_fd);
    printf("> Reached bottom of loop!\n");
    }


/*---------------------------FIN------------------------------*/
close(sock_fd);
unlink(SOCK_PATH);
printf("> Socket closed and unlinked from path ... Done!\n ");
return 0;
}

任何帮助将不胜感激!

【问题讨论】:

  • 顺便说一句,您可能不想在创建线程后立即关闭conn_fd。让线程处理关闭它的连接(除非pthread_create 失败)

标签: c multithreading sockets segmentation-fault


【解决方案1】:

这是错误的:

pthread_create(&thread , NULL, connection_handler(conn_fd), NULL);

pthread_create 需要函数的地址才能在新线程中运行。您的代码所做的是在主线程中调用 connection_handler,然后将 connection_handler 的结果作为函数地址传递给 pthread_create。

你需要的是:

pthread_create(&thread , NULL, connection_handler, (void*)conn_fd);

您还需要将connection_handler 更改为void* 而不是int

void *connection_handler(void* arg)
{
  intptr_t connection_fd = (intptr_t)arg;
  ...
}

【讨论】:

  • 为什么会编译?该参数是一个函数指针void *(*__start_routine) (void *)
  • 啊,太棒了!这行得通,我认为是我对线程的业余知识导致了段错误。非常感谢!但是我认为它应该是 pthread_create() 调用中的 &connection_handler。
  • @C_p678 22:不客气。顺便说一句,&amp;connection_handler 中的 &amp; 是可选的 - 请参阅 c-faq.com/~scs/cclass/int/sx10a.html
  • @aix:如果您将指针传递给参数,请确保该值在更改之前由线程获取。这很可能需要线程之间进行一些小的同步。
  • &amp;conn_fd 不起作用。它有一个非常糟糕的竞争条件,将在新线程读取它之前进行修改。相反,您需要(void *)conn_fd 或更复杂的方式来传递它。
【解决方案2】:

您对pthread_create 的使用不正确。第三个参数应该是指向void *(*start_routine) (void *) 类型函数的指针,而不是传递connection_handler 的返回值。

更改connection_handler 以接收void * 参数(并确保它返回实际值),例如。

#include <stdint.h>

void *connection_handler(void *arg)
{
    intptr_t connection_fd = (intptr_t)arg;
    ...
    return NULL;
}

并将您的呼叫更改为以下内容

pthread_create(&thread, NULL, &connection_handler, (void *)conn_fd);

您还应该确保以分离的方式启动线程,使用pthread_detach 分离线程或稍后使用pthread_join 加入它

【讨论】:

  • 我喜欢你用这个去哪里,但我得到这个错误:警告:传递'pthread_create'的参数4使指针从整数而不进行强制转换......
  • @C_p678:对不起,应该是(void *),而不是intptr_t。已更正。
【解决方案3】:
buffer[nbytes] = 0;

如果您已读取 256 个字节,这将溢出。增加缓冲区大小或将读取大小减一。

【讨论】:

  • 我很确定那不是问题,我发送的消息只是“Hello World”。
【解决方案4】:

很可能是因为您在两个地方关闭了套接字连接。很有可能在线程的一次运行中它还没有到达write,但在你的父线程中已经关闭了连接。

为什么需要创建这么多线程?一个工作线程不够吗?您可以将作业堆积到此工作线程...

【讨论】:

  • 好吧,套接字可能会在很短的时间内获得多个连接,所以我认为为每个连接创建一个线程将是最好的方法。与每个连接一个进程相反。
  • 你认为线程会运行多长时间?我的意思是你只是在读写 256 字节。它应该很快,所以通过排队作业你应该看不到任何区别。
  • 这只是一个骨架,线程会在收到的消息中搜索某些关键字,检查它的发送时间等。所以它可能需要一些时间。
猜你喜欢
  • 1970-01-01
  • 2011-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-14
  • 1970-01-01
  • 2017-02-01
相关资源
最近更新 更多