自制C语言Shell出现Segmentation Fault问题排查求助
解决Shell实现中的段错误问题
问题描述
用C语言编写可在PuTTY运行的Shell,支持ls、pwd等带参数的命令,但执行参数数量不同的命令时(如先ls再ls -l)会触发Segmentation Fault,怀疑是tokenizer函数的问题。
核心错误分析
指针覆盖导致空指针访问:
原tokenizer函数最后执行toks[i] = (char *)0;,直接将argv数组的元素设为NULL,覆盖了之前通过malloc分配的内存地址。当第二次执行参数更多的命令时,尝试向已被设为NULL的argv元素复制数据,触发段错误,同时造成内存泄漏。未处理空输入场景:
用户输入空行时,tokenizer会使argv[0]为NULL,后续strcmp(argv[0], "exit")会访问空指针,引发段错误。输入截断的越界风险:
原代码直接修改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
相关产品推荐
相关产品推荐

