C语言fork和execve实现简单Shell:错误处理与优化
C语言fork和execve实现简单Shell:错误处理与优化
本文介绍如何使用C语言的fork和execve函数实现一个简单的Shell,并着重探讨代码中的错误处理和优化。
初始代码及问题分析
以下代码实现了一个基本的Shell功能,可以接受用户输入的命令并执行。c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/wait.h>#include <pwd.h>#include <unistd.h>
#define TRUE 1#define MAX_HOSTNAME_LEN 256#define MAX_PATH_LEN 1024
void type_prompt(char *prompt) { struct passwd *pwd; char hostname[MAX_HOSTNAME_LEN]; char pathname[MAX_PATH_LEN];
pwd = getpwuid(getuid()); gethostname(hostname, MAX_HOSTNAME_LEN); getcwd(pathname, MAX_PATH_LEN); sprintf(prompt, '%s@%s:', pwd->pw_name, hostname);}
int main(int argc, char *argv[]) { char prompt[100]; char input[1000]; while (TRUE) { type_prompt(prompt); printf('%s', prompt); fgets(input, sizeof(input), stdin); // read input from stdin
char *token = strtok(input, ' '); // tokenize the input string while (token != NULL) { if (strcmp(token, 'quit') == 0 || strcmp(token, 'exit') == 0) { break; // exit the loop if user types 'quit' or 'exit' } pid_t pid = fork(); // create a child process if (pid == 0) { // child process code char *args[] = {token, NULL}; execve(token, args, NULL); // execute the command in the child process printf('Command not found: %s', token); // print error message if execve fails exit(EXIT_FAILURE); } else if (pid > 0) { // parent process code waitpid(-1, NULL, 0); // wait for the child process to exit } else { // error occurred when forking the process printf('Fork failed.'); exit(EXIT_FAILURE); } token = strtok(NULL, ' '); // get the next token } } return 0;}
问题:
这段代码存在一个错误处理问题:当execve调用失败时(例如,用户输入了不存在的命令),子进程会打印错误信息并退出,但父进程无法得知子进程的退出状态,因为它使用了waitpid(-1, NULL, 0)等待任意子进程退出,而不是指定等待特定的子进程。
优化方案
为了解决上述问题,我们需要对代码进行以下优化:
- **使用
waitpid等待特定的子进程:**将waitpid(-1, NULL, 0)修改为waitpid(pid, NULL, 0),确保父进程等待的是调用execve的子进程。2. **使用fprintf输出错误信息:**将错误信息输出到标准错误流(stderr),而不是标准输出流(stdout),以便更好地区分错误信息和正常输出。3. **使用_exit终止子进程:**在execve调用失败时,使用_exit(EXIT_FAILURE)终止子进程,以便父进程可以正确获取子进程的退出状态。
修改后的代码
以下是修改后的代码:c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/wait.h>#include <pwd.h>#include <unistd.h>
#define TRUE 1#define MAX_HOSTNAME_LEN 256#define MAX_PATH_LEN 1024
void type_prompt(char *prompt) { struct passwd *pwd; char hostname[MAX_HOSTNAME_LEN]; char pathname[MAX_PATH_LEN];
pwd = getpwuid(getuid()); gethostname(hostname, MAX_HOSTNAME_LEN); getcwd(pathname, MAX_PATH_LEN); sprintf(prompt, '%s@%s:', pwd->pw_name, hostname);}
int main(int argc, char *argv[]) { char prompt[100]; char input[1000]; while (TRUE) { type_prompt(prompt); printf('%s', prompt); fgets(input, sizeof(input), stdin); // read input from stdin
char *token = strtok(input, ' '); // tokenize the input string while (token != NULL) { if (strcmp(token, 'quit') == 0 || strcmp(token, 'exit') == 0) { break; // exit the loop if user types 'quit' or 'exit' } pid_t pid = fork(); // create a child process if (pid == 0) { // child process code char *args[] = {token, NULL}; execve(token, args, NULL); // execute the command in the child process fprintf(stderr, 'Command not found: %s
', token); // print error message to stderr _exit(EXIT_FAILURE); // terminate the child process using _exit } else if (pid > 0) { // parent process code waitpid(pid, NULL, 0); // wait for the specific child process to exit } else { // error occurred when forking the process fprintf(stderr, 'Fork failed. '); exit(EXIT_FAILURE); } token = strtok(NULL, ' '); // get the next token } } return 0;}
总结
通过上述修改,我们解决了初始代码中存在的错误处理问题,并优化了代码的健壮性。在编写涉及进程控制的代码时,务必谨慎处理各种潜在的错误情况,以确保程序的稳定性和可靠性。
原文地址: http://www.cveoy.top/t/topic/bBFn 著作权归作者所有。请勿转载和采集!