C中的Shell程序具有奇数叉行为

时间:2014-02-09 05:19:39

标签: c linux shell fork

我正在编写一个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;
}

2 个答案:

答案 0 :(得分:1)

全部放在一起: 您需要检查forkexecv是否有失败。 在exit失败后(可能在execv失败后),您应该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;
}

答案 1 :(得分:0)

您应检查 execv 是否失败,并确保在子块结尾处退出()

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

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

此外,请注意在此上下文中使用 strcpy 等函数,因为它们可能会导致缓冲区溢出。如果不受信任的攻击者类型正在与您的shell通信,则此类安全问题可能会让他们突破“沙箱”。