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

自制C语言Shell出现Segmentation Fault问题排查求助

解决Shell实现中的段错误问题

问题描述

用C语言编写可在PuTTY运行的Shell,支持ls、pwd等带参数的命令,但执行参数数量不同的命令时(如先ls再ls -l)会触发Segmentation Fault,怀疑是tokenizer函数的问题。

核心错误分析

  1. 指针覆盖导致空指针访问:
    原tokenizer函数最后执行toks[i] = (char *)0;,直接将argv数组的元素设为NULL,覆盖了之前通过malloc分配的内存地址。当第二次执行参数更多的命令时,尝试向已被设为NULL的argv元素复制数据,触发段错误,同时造成内存泄漏。

  2. 未处理空输入场景:
    用户输入空行时,tokenizer会使argv[0]为NULL,后续strcmp(argv[0], "exit")会访问空指针,引发段错误。

  3. 输入截断的越界风险:
    原代码直接修改data[strlen(data)-1],若输入为空(极端情况)会导致strlen(data)为0,访问data[-1]触发越界;若输入无换行符(超长输入),会错误截断最后一个字符。

修复方案及代码

修复要点

  • 让argv指针数组直接指向strtok分割后的token,无需为每个元素分配内存,避免指针覆盖问题。
  • 处理空输入、malloc失败、fork失败等异常场景。
  • 限制参数数量,避免数组越界。
  • 释放已分配的内存,避免泄漏。

修复后的完整代码

#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

// 获取用户输入并返回,打印Shell提示符
char* intro(){
    printf("TamuccShell>> ");
    static char dataStr[200]; 
    if(fgets(dataStr, sizeof(dataStr), stdin) == NULL){
        printf("\n");
        return NULL;
    }
    return dataStr;
}

// 将用户输入分割为命令参数,填充到argv数组
void tokenizer(char** toks, char* data){
    int i = 0;
    size_t len = strlen(data);
    // 仅当最后一个字符是换行符时才替换为NULL
    if (len > 0 && data[len - 1] == '\n') {
        data[len - 1] = '\0';
    }

    char* token = strtok(data, " ");
    // 限制最多9个参数,留一个位置给终止NULL
    while (token != NULL && i < 9) { 
        toks[i] = token;
        token = strtok(NULL, " ");
        i++;
    }
    toks[i] = NULL; // 设置参数数组终止符
}

int main(){
    int pid;
    int status;
    // 分配命令路径内存并检查是否成功
    char* folder = malloc(200);
    if (folder == NULL) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }

    char* argv[10]; // 命令参数指针数组,无需为每个元素分配内存

    char* userInput;
    int active = 1;
    while(active){
        userInput = intro();
        if (userInput == NULL) {
            break; // 处理fgets读取失败(如Ctrl+D)
        }

        tokenizer(argv, userInput);

        if (argv[0] == NULL) {
            continue; // 处理空输入,直接回到提示符
        }
        
        if(strcmp(argv[0],"exit") == 0){
            active = 0;
            continue;
        }

        // 拼接命令完整路径
        strcpy(folder, "/bin/");
        strcat(folder, argv[0]);

        pid = fork();
        if(pid == 0){
            // 子进程执行命令
            execv(folder, argv);
            // 若execv返回,说明执行失败
            perror("execv failed");
            exit(EXIT_FAILURE);
        } else if (pid > 0){
            // 父进程等待子进程结束
            wait(&status);
        } else {
            perror("fork failed");
        }
    }
    free(folder); // 释放分配的内存
    return 0;
}

说明

  • 修复后的tokenizer利用strtok的原地分割特性,让argv直接指向分割后的字符串,避免了内存复制和指针覆盖问题。
  • 增加了多种异常场景的处理,提升了程序的稳定性。
  • 所有动态分配的内存均已释放,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 10:35:21