C语言fork和execve实现简单Shell:错误处理与优化

本文介绍如何使用C语言的forkexecve函数实现一个简单的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)等待任意子进程退出,而不是指定等待特定的子进程。

优化方案

为了解决上述问题,我们需要对代码进行以下优化:

  1. **使用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;}

总结

通过上述修改,我们解决了初始代码中存在的错误处理问题,并优化了代码的健壮性。在编写涉及进程控制的代码时,务必谨慎处理各种潜在的错误情况,以确保程序的稳定性和可靠性。

C语言fork和execve实现简单Shell:错误处理与优化

原文地址: http://www.cveoy.top/t/topic/bBFn 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录