You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程访问内存映射文件缓冲区触发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);

额外优化点

  1. 文件描述符管理:mmap成功后可以关闭文件描述符,内存映射不会因为文件关闭而失效,避免资源泄漏。
  2. 错误处理完善:
    • argc < 2时应该直接return 1(main函数返回值应为int,false是布尔值,不符合规范);
    • 错误信息可以结合perror打印具体错误原因,方便调试;
  3. 线程资源释放:线程创建完成后可以销毁pthread_attr_t属性,避免资源泄漏;
  4. 内存释放:calloc分配的tinfo最后需要用free释放;
  5. 映射区域合法性检查:确保文件大小大于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:12:37