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

未用-fsanitize=address编译时程序触发SEGFAULT,如何修复?

修复pthreads项目中的段错误与内存问题

问题描述

我正在完成学校作业,编写一个基于pthreads的C项目。发现未使用-fsanitize=address编译时,运行程序会触发段错误,错误信息为[1] 2446 segmentation fault ./time;使用该选项编译后,程序会输出time(2544,0x10b1d1600) malloc: nano zone abandoned due to inability to preallocate reserved vm space.的提示,但仍能正常运行。

环境信息

  • 操作系统:macOS 12.6
  • 编译器版本:
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

项目Makefile

CC = /usr/bin/gcc
CPPFLAGS =-g -Wall -fsanitize=address -std=c11
HDRS = gui2.h display.h
OBJS = gui2.o display.o

main: $(OBJS) $(HDRS)
    $(CC) $(CPPFLAGS) -lpthread -o time time.c $(OBJS)

%.o: %.c %.h

.PHONY: clean

clean:
    rm -r time.dSYM && rm $(OBJS) time

项目代码

time.c

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>
#include "gui2.h"


typedef struct data {
    int tid;
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} data;

typedef struct gui_data {
    int bin;
    int *done;
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} gui_data;


void *thread_function(void *args)
{
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 100000000;
    
    pthread_t t;
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_t cond;
    pthread_cond_init(&cond, NULL);


    data *thread_data = (data*)args;
    
    gui_data *threadData = (gui_data*)malloc(sizeof(gui_data));
    threadData->bin = thread_data->tid;
    threadData->done = (int*)malloc(sizeof(int));
    *(threadData->done) =  0;
    threadData->mutex = &mutex;
    threadData->cond = &cond;
    
    // 用线程内的显式变量实现计数机制,而非外部变量
    for (int i = 0; i < 2; ++i)
    {
        *(threadData->done) = 0;
        pthread_mutex_lock(&mutex);
        pthread_create(&t, NULL, gui, threadData);

        if (threadData->done == 0)
        {
            pthread_cond_wait(threadData->cond, threadData->mutex);
        }
        nanosleep(&ts, NULL); 
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_join(t,NULL);

    free(threadData->done);
    free(threadData);
    pthread_exit(NULL);
}

int main()
{
    int n = 100;
    pthread_t tid[n];
    data args[n];

    for (int i = 0; i < n; i++)
    {
        args[i].tid = i;
    }
    
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 100000000;

    for (int i = 0; i < n; ++i)
    {
        nanosleep(&ts, NULL);
        pthread_create(&tid[i], NULL, thread_function, &args[i]);
    }
    return EXIT_SUCCESS;
}

gui2.c

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <pthread.h>

#include "display.h"

#define PI2 6.28318530717

typedef struct gui_data {
    int bin;
    int *done;
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} gui_data;

void *gui(void *count)
{
    gui_data *guiData = (gui_data*)count;

    pthread_mutex_lock(guiData->mutex);
    int x, y;
    char panel[HEIGHT][WIDTH];
    
    fill(panel);
    x = ((guiData->bin / 10) % 10);
    y = (guiData->bin % 10);
    set(panel, x, y, 'X');
    
    system("clear");
    display(panel);
    printf("\n");
    
    *(guiData->done) = 1;
    pthread_mutex_unlock(guiData->mutex);
    pthread_exit(NULL);
}

gui2.h

#ifndef GUI_H
#define GUI_H

void *gui(void *count);

#endif

display.c

#include <stdio.h>
#include <string.h>
#include "display.h"

void set(char buf[HEIGHT][WIDTH], int i, int j, char c)
{
    buf[j][i] = c;
}


void fill(char buf[HEIGHT][WIDTH])
{
    for (int j = 0; j < HEIGHT; j++)
    {
        for (int i = 0; i < WIDTH; i++)
        {
            buf[j][i] = '`';
        }
    }
}


void display(char buf[HEIGHT][WIDTH])
{
    char frame[HEIGHT*(WIDTH+1)+1];
    
    copy_frame(frame, buf);
    
        printf("%s", frame);

}


void copy_frame(char f[], char b[HEIGHT][WIDTH])
{
    for (int i = 0; i < HEIGHT; i++)
    {
        strncpy(
            &(f[i*(WIDTH+1)]),
            b[i],
            WIDTH
        );
        f[i*(WIDTH+1) + WIDTH] = '\n';
    }
    f[HEIGHT*(WIDTH+1)] = '\0';
}

display.h

#ifndef HEADER_DISPLAY
#define HEADER_DISPLAY

#define WIDTH 10
#define HEIGHT 10

void set(char buf[HEIGHT][WIDTH], int, int, char);
void fill(char buf[HEIGHT][WIDTH]);
void display(char buf[HEIGHT][WIDTH]);
void copy_frame(char[], char buf[HEIGHT][WIDTH]);

#endif

问题分析与修复方案

1. 核心段错误原因:指针与值的错误比较

在thread_function中,这段代码是致命错误:

if (threadData->done == 0)

threadData->done是指向int的指针,此处错误地将指针本身与0(空指针)比较,而实际需要检查的是它指向的变量值是否为0。正确写法应为:

if (*(threadData->done) == 0)

原写法会导致条件永远为真(因为done指针已通过malloc分配,不为空),线程进入pthread_cond_wait后,后续的信号可能无法正确触发,加上循环内线程资源未正确回收,最终引发段错误。

2. 线程资源泄漏与重复创建问题

thread_function的for循环中,每次迭代都会复用同一个pthread_t t变量创建新线程,但仅在循环结束后join一次,导致前序创建的gui线程资源泄漏,且无法正确回收。

修复方法:将pthread_t t放入循环内,每次创建后及时join:

for (int i = 0; i < 2; ++i)
{
    *(threadData->done) = 0;
    pthread_mutex_lock(&mutex);
    pthread_t t; // 每次循环创建新的线程ID变量
    pthread_create(&t, NULL, gui, threadData);

    if (*(threadData->done) == 0)
    {
        pthread_cond_wait(threadData->cond, threadData->mutex);
    }
    nanosleep(&ts, NULL); 
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    pthread_join(t, NULL); // 及时回收当前gui线程
}

3. main函数未等待子线程结束

main函数创建100个thread_function线程后直接返回,会导致进程提前终止,子线程资源无法正确释放。需在main末尾加入循环,等待所有子线程结束:

// 创建子线程的循环保持不变
for (int i = 0; i < n; ++i)
{
    nanosleep(&ts, NULL);
    pthread_create(&tid[i], NULL, thread_function, &args[i]);
}

// 新增:等待所有子线程完成
for (int i = 0; i < n; ++i)
{
    pthread_join(tid[i], NULL);
}
return EXIT_SUCCESS;

4. malloc提示的处理

malloc: nano zone abandoned...是macOS下AddressSanitizer的常见提示,与系统内存分配机制相关,不影响程序功能。修复上述核心问题后,该提示会自然减少或消失,无需额外处理。

5. 优化内存管理

可以去掉done的动态分配,直接将其作为gui_data的成员,减少malloc/free的开销与风险:

// 修改gui_data结构体
typedef struct gui_data {
    int bin;
    int done; // 直接用int成员,替代指针
    pthread_mutex_t *mutex;
    pthread_cond_t *cond;
} gui_data;

对应修改相关代码:

  • thread_function中:threadData->done = 0;、if (threadData->done == 0)
  • gui函数中:guiData->done = 1;
    同时删除free(threadData->done);语句。

6. 销毁同步对象

在thread_function末尾,销毁初始化的mutex和cond,避免资源泄漏:

pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);

修复后的关键代码示例

修改后的thread_function

void *thread_function(void *args)
{
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 100000000;
    
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_t cond;
    pthread_cond_init(&cond, NULL);

    data *thread_data = (data*)args;
    
    gui_data *threadData = (gui_data*)malloc(sizeof(gui_data));
    threadData->bin = thread_data->tid;
    threadData->done = 0; // 使用int成员替代动态分配
    threadData->mutex = &mutex;
    threadData->cond = &cond;
    
    for (int i = 0; i < 2; ++i)
    {
        threadData->done = 0;
        pthread_mutex_lock(&mutex);
        pthread_t t;
        pthread_create(&t, NULL, gui, threadData);

        if (threadData->done == 0)
        {
            pthread_cond_wait(threadData->cond, threadData->mutex);
        }
        nanosleep(&ts, NULL); 
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        pthread_join(t, NULL); // 及时回收线程
    }

    free(threadData);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    pthread_exit(NULL);
}

修改后的gui函数

void *gui(void *count)
{
    gui_data *guiData = (gui_data*)count;

    pthread_mutex_lock(guiData->mutex);
    int x, y;
    char panel[HEIGHT][WIDTH];
    
    fill(panel);
    x = ((guiData->bin / 10) % 10);
    y = (guiData->bin % 10);
    set(panel, x, y, 'X');
    
    system("clear");
    display(panel);
    printf("\n");
    
    guiData->done = 1; // 直接修改成员变量
    pthread_mutex_unlock(guiData->mutex);
    pthread_exit(NULL);
}

内容的提问来源于stack exchange,提问作者cptalpdeniz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 01:11:08