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

实现数据链路协议时fclose()触发段错误问题求助

段错原因分析:fclose()触发崩溃与文件打开时机的关联

问题背景

实现数据链路协议时,通过虚拟串口传输文件,调用fclose()触发段错误。输出文件已创建但无内容,即使改为先将文件缓冲到内存再写入仍未解决;将文件打开时机从函数开头移至写入前,问题解决。

相关代码

int app_rx(const char* outputFile){
    file_data_t fileData;
    if(!receive_ctrl_pckt(&fileData)){
        printf("Could not receive control packet\n");
        return FAILURE;
    }
    printf("Receiving file [%s]\nFile size: %d\n", fileData.fileName, fileData.fileSize);
    unsigned char fileBuffer[fileData.fileSize];
    int fileIndex = 0;
    int totalBytes = 0;
    int stop = 0;
    size_t sqNo = 0;
    int bytes;
    FILE* out = fopen(outputFile, "w");
    do{
        activeBuffer = (activeBuffer + 1) % 2;
        printf("Receiving packet %lu\n", sqNo);
        bytes = llread(BUFFERS[TMP_BUFFER]);
        if(bytes == DUP_ERR){
            printf("Duplicate data. Discarding packet\n");
        }
        else if (bytes == WH_ERR){
            printf("Invalid header. Discarding packet\n");
        }
        else if (bytes == WD_ERR){
            printf("Corrupted data. Awaiting retransmission\n");
        }
        else{
            printf("Packet %lu successfully received\n", sqNo);
            int retrieveRes = retrieve_payload(sqNo);
            switch(retrieveRes){
                case CTRL_END: {
                    stop = 1;
                    break;
                }
                case FAILURE: {
                    printf("Unknown error\nExiting...");
                    exit(1);
                }
                case SQ_ERR:{
                    printf("Unsynchronized packets\nExiting");
                    exit(1);
                }
                default:{
                    for(int i = 0; i < retrieveRes; i++){
                        fileBuffer[fileIndex] = BUFFERS[activeBuffer][i];
                        fileIndex++;
                    }
                    sqNo = (sqNo + 1) % 255;
                    totalBytes += bytes;
                    break;
                }
            }
        }
    }while(!stop);
    printf("file index: %d\n", fileIndex);
    fwrite(fileBuffer, sizeof(unsigned char), fileIndex, out);
    printf("here\n");
    fclose(out);
    if(totalBytes == fileData.fileSize){
        return SUCCESS;
    }
    else{
        return FAILURE;
    }
}

标准输出

Packet 9 successfully received
Receiving packet 10
Asserting data integrity
Packet 10 successfully received
Receiving packet 11
Asserting data integrity
Packet 11 successfully received
file index: 10968
here
make: *** [Makefile:35: run_rx] Segmentation fault (core dumped)

核心原因分析

1. 栈溢出破坏FILE指针

代码开头定义了栈上的大数组:

unsigned char fileBuffer[fileData.fileSize];

系统栈的默认空间有限(Linux下通常为8MB,Windows下约1MB),当fileData.fileSize较大时,这个数组会占用大量栈空间,触发栈溢出。

栈的分配顺序是从高地址向低地址延伸:先定义的fileBuffer占据高地址区域,后续的FILE* out指针位于低地址区域。如果fileBuffer的大小超过栈剩余可用空间,会直接覆盖out指针的内存,使其变成野指针。后续调用fclose(out)时,访问野指针就会触发段错误。

将fopen移至写入前,out指针的赋值发生在文件接收完成后,栈上的溢出风险已经过去,指针不会被破坏。

2. 长时间持有句柄导致的内存污染

在文件接收的循环中,你调用了llread、retrieve_payload等底层串口操作函数。如果这些函数存在内存越界、指针错误等问题,可能会意外修改out指向的FILE结构体内容。

文件句柄从函数开头到接收完成被长时间持有,暴露在内存污染风险下的窗口更长。而将fopen移至写入前,句柄仅在写入和关闭时被持有,污染概率大幅降低。

3. 虚拟串口驱动的资源冲突

部分虚拟串口驱动可能与标准C库的文件IO共享底层资源(如文件描述符表、内存缓冲区)。长时间打开文件句柄可能引发驱动与C库的资源竞争,破坏FILE结构体的内部状态,最终导致fclose崩溃。


验证与修复建议

  • 检查栈大小:Linux下用ulimit -s查看当前栈大小,若fileData.fileSize接近或超过该值,说明栈溢出是主因。
  • 改用堆分配:将fileBuffer改为malloc分配,避免栈溢出:
    unsigned char* fileBuffer = malloc(fileData.fileSize);
    if (!fileBuffer) { /* 处理内存分配失败 */ }
    // 使用完成后调用free(fileBuffer);
    
  • 调试指针完整性:在fopen后和fclose前打印out的地址,若地址不一致,说明指针被破坏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 14:12:04