多线程访问内存映射文件缓冲区触发SIGSEGV段错误问题
问题分析与解决
问题现象
尝试将文件数据处理任务拆分到多线程执行:主线程完成文件内存映射后,创建多线程从映射缓冲区的不同偏移位置处理数据。但即使只创建一个线程,程序也会触发SIGSEGV段错误崩溃。主线程中可以正常打印缓冲区前100字节,但线程中执行相同操作时就会崩溃。
错误代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> #include <fcntl.h> #include <pthread.h> const unsigned NUM_THREADS = 1; char *bufferBase = NULL; char *bufferEnd = NULL; struct thread_info { pthread_t thread_id; int thread_num; size_t offset; size_t numSamples; }; static void *thread_start(void *arg) { struct thread_info *tinfo = (struct thread_info *)arg; printf("\nStart Thread %d\n", tinfo->thread_num); // Crashing when trying to Dump data here !!! printf("Dump mmap from thread\n"); for (size_t i = 0; i < 100; i++) { printf("%x ", bufferBase[i]); } return NULL; } int main(int argc, char *argv[]) { int s = 0; if (argc < 2) { printf("Args error\n"); } int file = open(argv[1], O_RDWR); if (file < 0) { printf("open failed\n"); return false; } struct stat filestat; if (stat(argv[1], &filestat) != 0) { printf("stat failed\n"); return false; } char *bufferBase = (char *)mmap(NULL, filestat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, file, 0); if (bufferBase == MAP_FAILED) { printf("mmap failed\n"); return false; } // OK to Dump data here !!! printf("dump mmap\n"); for (size_t i = 0; i < 100; i++) { printf("%x ", bufferBase[i]); } /* Initialize thread creation attributes */ pthread_attr_t attr; s = pthread_attr_init(&attr); if (s != 0) { printf("pthread_attr_init failed\n"); return false; } struct thread_info *tinfo = (struct thread_info *)calloc(NUM_THREADS, sizeof(struct thread_info)); for (size_t i = 0; i < NUM_THREADS; i++) { tinfo[i].thread_num = i + 1; int s = pthread_create(&tinfo[i].thread_id, &attr, &thread_start, &tinfo[i]); if (s != 0) { printf("pthread_create failed [%zu]\n", i); return false; } } void *res = NULL; for (size_t i = 0; i < NUM_THREADS; i++) { s = pthread_join(tinfo[i].thread_id, &res); if (s != 0) { printf("pthread_join failed [%zu]\n", i); return false; } printf("Joined with thread %d; returned value was %s\n", tinfo[i].thread_num, (char *)res); } return 0; }
核心错误原因
全局变量bufferBase被主线程的局部变量覆盖:
在main函数中,你重新声明了一个局部的char *bufferBase,这会屏蔽全局的同名变量。主线程中打印的是局部变量指向的映射缓冲区,而线程函数中访问的是全局的bufferBase——这个全局变量始终是NULL,所以线程访问NULL[i]时触发段错误。
修复方案
去掉main函数中mmap行的char *声明,直接给全局变量赋值:
// 原错误代码 char *bufferBase = (char *)mmap(...); // 修改后 bufferBase = (char *)mmap(NULL, filestat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, file, 0);
额外优化点
- 文件描述符管理:
mmap成功后可以关闭文件描述符,内存映射不会因为文件关闭而失效,避免资源泄漏。 - 错误处理完善:
argc < 2时应该直接return 1(main函数返回值应为int,false是布尔值,不符合规范);- 错误信息可以结合
perror打印具体错误原因,方便调试;
- 线程资源释放:线程创建完成后可以销毁
pthread_attr_t属性,避免资源泄漏; - 内存释放:
calloc分配的tinfo最后需要用free释放; - 映射区域合法性检查:确保文件大小大于100字节,避免越界访问。
修改后的完整代码示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> #include <fcntl.h> #include <pthread.h> const unsigned NUM_THREADS = 1; char *bufferBase = NULL; char *bufferEnd = NULL; size_t fileSize = 0; struct thread_info { pthread_t thread_id; int thread_num; size_t offset; size_t numSamples; }; static void *thread_start(void *arg) { struct thread_info *tinfo = (struct thread_info *)arg; printf("\nStart Thread %d\n", tinfo->thread_num); printf("Dump mmap from thread\n"); // 检查访问范围是否合法 size_t dumpSize = (tinfo->offset + 100 > fileSize) ? (fileSize - tinfo->offset) : 100; for (size_t i = 0; i < dumpSize; i++) { printf("%x ", bufferBase[tinfo->offset + i]); } return NULL; } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s <file>\n", argv[0]); return 1; } int file = open(argv[1], O_RDWR); if (file < 0) { perror("open failed"); return 1; } struct stat filestat; if (stat(argv[1], &filestat) != 0) { perror("stat failed"); close(file); return 1; } fileSize = filestat.st_size; bufferBase = (char *)mmap(NULL, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, file, 0); if (bufferBase == MAP_FAILED) { perror("mmap failed"); close(file); return 1; } // mmap成功后关闭文件 close(file); printf("dump mmap\n"); size_t dumpSize = (fileSize > 100) ? 100 : fileSize; for (size_t i = 0; i < dumpSize; i++) { printf("%x ", bufferBase[i]); } pthread_attr_t attr; int s = pthread_attr_init(&attr); if (s != 0) { fprintf(stderr, "pthread_attr_init failed: %s\n", strerror(s)); munmap(bufferBase, fileSize); return 1; } struct thread_info *tinfo = (struct thread_info *)calloc(NUM_THREADS, sizeof(struct thread_info)); if (!tinfo) { perror("calloc failed"); pthread_attr_destroy(&attr); munmap(bufferBase, fileSize); return 1; } for (size_t i = 0; i < NUM_THREADS; i++) { tinfo[i].thread_num = i + 1; tinfo[i].offset = (fileSize * i) / NUM_THREADS; tinfo[i].numSamples = (fileSize * (i+1)) / NUM_THREADS - tinfo[i].offset; s = pthread_create(&tinfo[i].thread_id, &attr, &thread_start, &tinfo[i]); if (s != 0) { fprintf(stderr, "pthread_create failed [%zu]: %s\n", i, strerror(s)); // 清理已创建的线程 for (size_t j = 0; j < i; j++) { pthread_join(tinfo[j].thread_id, NULL); } free(tinfo); pthread_attr_destroy(&attr); munmap(bufferBase, fileSize); return 1; } } pthread_attr_destroy(&attr); void *res = NULL; for (size_t i = 0; i < NUM_THREADS; i++) { s = pthread_join(tinfo[i].thread_id, &res); if (s != 0) { fprintf(stderr, "pthread_join failed [%zu]: %s\n", i, strerror(s)); } printf("\nJoined with thread %d; returned value was %p\n", tinfo[i].thread_num, res); } free(tinfo); munmap(bufferBase, fileSize); return 0; }
内容的提问来源于stack exchange,提问作者meodou
相关产品推荐
相关产品推荐

