/* Copyright © 2025 TP-Link Systems Inc. * * file shell_main.c * brief * details * * author Zhu QingQing * version * date 06Aug25 * * history \arg */ /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #define SIZE_CMMD 1024 #define MAX_NUM_ARG 128 /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* EXTERN_PROTOTYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_PROTOTYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* VARIABLES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ /**************************************************************************************************/ /* * fn static int exec_child(char *argv[]) * brief fork an child process to execute command * details * * param [in] argv - commands arr used for exec function * param [out] / * * return 0 - normal exit / -1 abnormal exit * retval * * note */ static int exec_child(char *argv[]) { /* fork child process to execute command */ int fds[2] = {-1 ,-1}; pid_t pid = -1; char dataPipe[SIZE_CMMD] = ""; int lenDataPipe = 0; if (pipe(fds) < 0) /* pipe: 0 - read data from pipe; 1 - write data to pipe */ { printf("pipe create faild.\n"); return -1; } pid = fork(); if (pid < 0) { printf("fork failed.\n"); return -1; } else if (0 == pid) { /* child proc */ /* write data from stdin to pipe */ close(fds[0]); if (dup2(fds[1], STDOUT_FILENO) < 0) { printf("child proc write data from stdin to pipe failed.\n"); return -1; } close(fds[1]); execvp(argv[0], argv); /* if execv successfully, child proc will be replaced */ printf("exec failed.\n"); /* else printf fail info */ return -1; } else { /* parent proc */ /* read port of pipe -> stdout */ close(fds[1]); lenDataPipe = read(fds[0], dataPipe, SIZE_CMMD); close(fds[0]); if (lenDataPipe < 0) { printf("pipe write failed.\n"); return -1; } /* the last char is '\n' */ dataPipe[lenDataPipe - 1] = '\0'; /* print data from child proc */ printf("%s\n", dataPipe); if (0 == strcmp(dataPipe, "stop")) { /* stop, then close child proc */ if (0 == kill(pid, SIGTERM)) { printf("close the child process successfully.\n"); return 0; } else { printf("close the child process failed.\n"); return -1; } } waitpid(pid, NULL, 0); } return 0; } /**************************************************************************************************/ /* PUBLIC_FUNCTIONS */ /**************************************************************************************************/ /**************************************************************************************************/ /* GLOBAL_FUNCTIONS */ /**************************************************************************************************/ /* * fn int main() * brief main function to achieve shell sample * details * * param [in] / * param [out] / * * return 0 * retval * * note */ int main() { printf("This is a shell sample ...\n"); printf("\n"); char cmmdShell[SIZE_CMMD] = ""; char *p_tmpArgs = NULL; char *p_saveArgs = NULL; char *p_endCharCmmd = NULL; char *argsCmmd[MAX_NUM_ARG] = {NULL}; /* save args that have been parsed */ int argsCnt = 0; /* number of args */ while (1) { /* obtain the standard input */ printf("root@shell: "); fflush(stdout); /*del \n in buffer*/ /* for scanf, '\n' will in stdin-buffer */ if (NULL == fgets(cmmdShell, SIZE_CMMD, stdin)) { printf("fgets error.\n"); break; } /* only \n, goto next loop */ if (0 == strcmp(cmmdShell, "\n")) { continue; } /* for fgets, 'cmmd\n' -> 'cmmd\n\0' */ p_endCharCmmd = strchr(cmmdShell, '\n'); if (NULL == p_endCharCmmd) { printf("fgets error.\n"); break; } else { *p_endCharCmmd = '\0'; /* not strlen -- end at \0 */ } /* exit */ if (0 == strcmp(cmmdShell, "exit")) { printf("Exit successfully.\n"); break; } /* parse command */ p_tmpArgs = strtok_r(cmmdShell, " ", &p_saveArgs); /* argsCnt < MAX_NUM_ARG - 1 -- last ele must be NULL */ while (p_tmpArgs != NULL && argsCnt < MAX_NUM_ARG - 1) { argsCmmd[argsCnt] = p_tmpArgs; argsCnt++; p_tmpArgs = strtok_r(NULL, " ", &p_saveArgs); /* only first use func strtok need pass para */ } argsCmmd[argsCnt] = NULL; /* end of argv[] in func exec must be NULL */ exec_child(argsCmmd); } return 0; } 哪里有问题
时间: 2025-08-10 11:57:52 AIGC 浏览: 11
这段代码实现了一个简单的 shell,但存在一些潜在问题和改进空间:
### 1. **内存泄漏**
- 在 `exec_child` 函数中,如果 `execvp` 执行失败,子进程会直接返回 `-1`,但父进程的管道文件描述符 `fds[0]` 可能未正确关闭(尽管 `waitpid` 会回收子进程资源,但显式关闭更安全)。
- 在 `main` 函数中,`argsCmmd` 数组每次循环都会重新解析,但没有重置 `argsCnt`,导致后续循环可能越界访问。
**修复建议**:
```c
// 在每次循环开始时重置 argsCnt
argsCnt = 0;
```
### 2. **管道逻辑问题**
- 父进程读取管道数据时,假设 `lenDataPipe - 1` 是 `\n`,但如果没有换行符(如命令输出不带换行),`dataPipe[lenDataPipe - 1] = '\0'` 会导致越界访问。
**修复建议**:
```c
if (lenDataPipe > 0) {
dataPipe[lenDataPipe] = '\0'; // 确保字符串终止
}
```
### 3. **信号处理不完全**
- 子进程可能被信号中断(如 `SIGINT`),但父进程没有正确处理这种情况,可能导致僵尸进程或异常退出。
**修复建议**:
```c
// 在父进程中检查 waitpid 的返回值
int status;
waitpid(pid, &status, 0);
if (WIFSIGNALED(status)) {
printf("Child process terminated by signal %d\n", WTERMSIG(status));
}
```
### 4. **输入验证不足**
- 未检查 `fgets` 是否读取到完整输入(如果输入超过 `SIZE_CMMD`,会截断且可能残留未读数据)。
- `strtok_r` 解析时未处理连续空格或空命令的情况。
### 5. **代码健壮性**
- `execvp` 失败时,子进程直接 `printf` 并返回 `-1`,但父进程可能无法区分是命令错误还是系统错误。
- 管道和文件描述符的关闭顺序可以优化(例如,父进程应在 `fork` 后立即关闭未使用的写端)。
### 6. **潜在死锁**
- 如果子进程的输出超过 `SIZE_CMMD`,管道可能阻塞,导致父子进程相互等待。
---
阅读全文
相关推荐



















