【问题标题】:Shell program in C has odd fork behaviourC 中的 Shell 程序具有奇怪的分叉行为
【发布时间】:2014-02-09 05:19:38
【问题描述】:

我正在编写一个 C 程序来模拟一个简单的 shell。这个 shell 基本上会像任何其他 shell(ls、cat 等)一样评估命令,以及处理流水线和重定向。

目前,我正在尝试从获取用户输入、对其进行标记并执行提供的命令开始(例如,仅执行“ls”而不是“ls -l”)。但是,我在分叉时遇到了很多困难。似乎每次我分叉时,都会出现问题并创建数百个相同的进程,导致我的计算机死机并且我不得不重新启动。代码似乎是正确的,但我不知道是什么导致了这种行为。以下是我的代码的相关部分(主要方法和输入标记器方法)。

int main() {
    char inputLine[512];    //user input
    char *args[10];         //arguments
    char* pathVar = "/bin/";//path for argument
    char programPath[512];  //pathVar + args[0]
    int n;                  //count variable

    //loop
    while (1) {
        //print prompt, get input
        printf("input> ");
        fgets(inputLine, 512, stdin);
        n = tokenizer(inputLine, args);

        //fork process
        pid_t pid = fork();

        if (pid != 0) {     //if parent
            wait(NULL);
        } else {            //if child
            //format input for execution
            strcpy(programPath, pathVar);
            strcat(programPath, args[0]);

            //execute user command
            int returnVal = execv(programPath, args);
        }
    }

    return 0;
}

int tokenizer(char *input, char *args[]) {
    char *line;             //current line
    int i = 0;              //count variable

    line = input;
    args[i] = strtok(line, " ");
    do {
        i++;
        line = NULL;
        args[i] = strtok(line, " ");
    } while (args[i] != NULL);

    return i;
}

【问题讨论】:

  • “出错”是什么意思?您是否尝试过在 exec 中使用更简单的方法(或替换 exec)来隔离问题?您是否尝试打印出 programPath 以确保它是您认为应该的样子?
  • 您显示的代码中似乎没有任何内容会导致您描述的内容。
  • 实际上,您应该测试fork 的返回值是否有错误(-1),并且您还应该在execv 之后exit 以防万一它失败(尽管保存它的返回值是无用的,因为如果成功则不会返回,如果失败则总是返回 -1)。
  • 我注意到的一件事是,因为fgets 将换行符留在字符串中并且您没有将其解析出来,所以您正在尝试exec 类似“ls\n”的东西。这似乎也让我的系统感到头晕目眩。我不确定什么我的系统在做什么,但是磁盘开始像疯了一样颠簸,在我把它彻底关闭之前我永远无法控制系统。我要去睡觉了,但如果你还没有解决它,我感到很勇敢,我会在早上再看一遍。
  • @Duck 好一个! @user41419 您可以通过在 strtok 分隔符中添加换行符来最轻松地解决 Duck 发现的问题。

标签: c linux shell fork


【解决方案1】:

把它们放在一起: 您需要检查 forkexecv 是否失败。 您应该在execv 失败后exit(也可能在fork 失败之后)。 并且您需要将\n 添加到strtok 分隔符(或以其他方式从输入行中删除换行符)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAXARGS 10
#define PATH "/bin/"

int main() {
    char inputLine[BUFSIZ];
    char *args[MAXARGS];
    char programPath[BUFSIZ + sizeof(PATH) + 10];

    while (1) {
        printf(":-> ");
        if (fgets(inputLine, BUFSIZ, stdin) == NULL) /* ctrl-D entered */
            break;

        tokenize(inputLine, args);

        pid_t pid = fork();
        if (pid == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (pid != 0) {  /* parent */
            wait(NULL);

        } else {         /* child */
            strcpy(programPath, PATH);
            strcat(programPath, args[0]);

            execv(programPath, args); /* will not return unless it fails */

            perror("execv");
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}

int tokenize(char *input, char *args[]) {
    int i = 0;

    args[0] = strtok(input, " \n");
    for (i = 0; args[i] && i < MAXARGS-1; ++i)
        args[++i] = strtok(NULL, " \n");

    return i;
}

【讨论】:

  • 谢谢你,这非常棒!不再沮丧或重新开始。
【解决方案2】:

您应该检查 execv 是否失败,并确保在子块的末尾 exit()

            //execute user command
            int returnVal = execv(programPath, args);

            // check return from execv
            if (returnVal < 0) {
                perror("execv");
                exit(1);
            }

另外,请注意在这种情况下使用像 strcpy 这样的函数,因为它们可能会导致缓冲区溢出。如果一个不受信任的攻击者类型正在与您的 shell 对话,这种类型的安全问题可能会让他们突破“沙盒”。

【讨论】:

  • 捕获execv()的返回值是没有意义的。如果成功,则不返回;如果返回,则失败。在失败的execv() 之后放置错误消息和退出是一个非常好的主意。它只是不需要在条件代码中。
  • 公平的乔纳森。还有,你好!希望你一切都好。
猜你喜欢
  • 2017-01-17
  • 1970-01-01
  • 2020-09-03
  • 2022-09-29
  • 2021-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多