多线程调用gethostbyname时memcpy触发Segmentation Fault问题排查
多线程域名解析程序memcpy段错误排查
我有一个Linux程序,需要从包含数百万域名的文本文件中获取每个域名的IP,并将其存入struct sockaddr_in结构体。为了提升效率,采用多线程实现,但程序运行4-5分钟后,会在memcpy代码处触发Segmentation Fault(段错误)。
线程主例程代码
#include <netdb.h> #include <arpa/inet.h> void* startcheckingdomains(){ while(1){ char* domain = "www.google.com"; struct hostent* hp = gethostbyname(domain); struct sockaddr_in sockaddr; if(hp == NULL){ herror("hp"); }else{ memcpy(&sockaddr.sin_addr, hp->h_addr, hp->h_length); } } }
GDB错误输出
线程 535 "main" 收到信号 SIGSEGV,段错误。 [切换到线程 0x7ffe8f606700 (LWP 27402)] __memmove_avx_unaligned_erms ()
问题根源
核心问题是**gethostbyname并非线程安全函数**:它返回的struct hostent结构体是内部静态分配的内存空间,多线程并发调用时,这个静态内存会被不同线程互相覆盖。当某个线程刚拿到hp指针,还没完成memcpy操作时,其他线程调用gethostbyname就会修改hp指向的内容,导致访问hp->h_addr或hp->h_length时出现非法内存引用,运行一段时间后触发段错误。
解决方法
1. 使用线程安全的域名解析函数
推荐使用符合POSIX标准的线程安全函数,比如:
gethostbyname_r:gethostbyname的可重入版本,需要自行提供内存缓冲区存储结果。getaddrinfo:更现代的API,支持IPv6,天然线程安全,是官方推荐的替代方案。
2. 用getaddrinfo改写的示例代码
#include <netdb.h> #include <arpa/inet.h> #include <string.h> void* startcheckingdomains(){ while(1){ const char* domain = "www.google.com"; struct addrinfo hints, *res, *p; struct sockaddr_in sockaddr; int rv; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; // 指定IPv4 hints.ai_socktype = SOCK_STREAM; if((rv = getaddrinfo(domain, NULL, &hints, &res)) != 0){ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); continue; } // 遍历结果取第一个IPv4地址 for(p = res; p != NULL; p = p->ai_next){ if(p->ai_family == AF_INET){ memcpy(&sockaddr, p->ai_addr, p->ai_addrlen); break; } } freeaddrinfo(res); // 必须释放getaddrinfo分配的内存 } }
3. 额外注意事项
- 控制线程并发数:过多线程同时发起DNS请求可能导致本地DNS缓存过载或被远端DNS服务器限制,反而降低效率。
- 域名队列的线程同步:如果是从文件读取域名分发给线程,要使用互斥锁或条件变量保证队列访问的线程安全,避免数据竞争。
内容的提问来源于stack exchange,提问作者user19628924
相关产品推荐
相关产品推荐

