开发者

Program hangs connecting to FIFOs (named pipes)

I'm trying to implement pipelining in an Operating Systems shell project by creating a number of FIFO pipes using mkfifo(). Currently, the program seems to hang after opening the first FIFO for writing. Additionally, the FIFO appears when doing an ls on the working directory. Am I using freopen() wrong in this instance?

void execute_external()
{
    int backgrounding = 0;
    if (raw_command[strlen(raw_command)-1] == '&')
    {
        pmesg(2, "Backgrounding requested.\n");
        raw_command[strlen(raw_command)-1] = '\0';
        raw_command = realloc(raw_command, strlen(raw_command)-1);
        backgrounding = 1;
    }

    int numCommands = 1;
    char **commands;
    commands = malloc(sizeof(char *));

    if(strstr(raw_command, "|") != NULL)        
    {
        numCommands = separate_pipeline_command开发者_高级运维s(commands);
    }
    else
    {
        commands[0] = malloc(strlen(raw_command) * sizeof(char));
        commands[0] = raw_command;
    }

    int i;
    char *fifo = malloc(MAXFIFOLEN);
    for (i = 0; i < numCommands; i++)
    {
        char **parameters_array = malloc(strlen(commands[i]) * sizeof(char *));
        int num_params;
        num_params = str_to_str_array(commands[i], parameters_array);
        pmesg(2, "Command is %s.\n", commands[i]);

        if (numCommands > 1)
        {
            int j;
            for (j = 0; j < numCommands - 1; j++)
            {
                sprintf(fifo, "fifo%i", j);
                mkfifo(fifo, S_IWUSR | S_IRUSR );
            }
        }

        pid_t pid = fork();

        pmesg(2, "Process forked. ID = %i. \n", pid);
        if (pid < 0)
        {
            fprintf(to_write_to, "Could not fork a process to complete the external command.\n");
            exit(EXIT_FAILURE);
        }
        int status;
        if (pid == 0) // This is the child process
        {
            pmesg(1, "input: [%s] output: [%s] input: %d, output: %d backgrounding is %d\n",input_file_name, output_file_name, redirect_input, redirect_output, backgrounding);
            if (numCommands > 1 && i == 0) // we may be pipelining and this is the first process
            {
                sprintf(fifo, "%s%i", "fifo", i);
                printf("Initial output: %s.\n", fifo);
                freopen(fifo, "w", stdout);
                //~ unlink(fifo);
            }
            else if (numCommands > 1 && i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process
            {
                sprintf(fifo, "%s%i", "fifo", i-1);
                printf("Input for process %i: %s.\n", i, fifo);
                freopen(fifo, "r", stdin);
                //~ unlink(fifo);
                sprintf(fifo, "%s%i", "fifo", i+1);
                printf("Output for process %i: %s.\n", i, fifo);
                freopen(fifo, "w", stdout);
                //~ unlink(fifo_2);
            }
            else if (numCommands != 1 &&i == numCommands)
            {
                char *fifo = malloc(strlen("fifo")+2);
                sprintf(fifo, "%s%i", "fifo", i);
                freopen(fifo, "r", stdin);  
                free(fifo);             
            }
            if(redirect_output == 1)
            {
                freopen(output_file_name, "w", stdout);
            }
            if(redirect_input == 1)
            {
                freopen(input_file_name, "r", stdin);
            }
            if (backgrounding != 0)
            {
                freopen("/dev/null", "w", stdout);
            }
            pmesg(2, "This is the child process, running execlp.\n");
            pmesg(1, "Command: [%s]\n", parameters_array[0]);
            if (execvp(parameters_array[0], parameters_array) < 0)
            {
                fprintf(to_write_to, "Could not execute the external command. errno: %i.\n", errno);
                exit(EXIT_FAILURE);
            }
            else    { pmesg(2, "Executed the child process.\n");}
        }
        else
        {
            if (backgrounding != 0)
            {
                enqueue(&process_queue, pid, clock(), 0, 0);
                printQueue(process_queue);
            }
            if (backgrounding == 0) { while(wait(&status) != pid); }// Wait for the child to finish executing
        } 
        pmesg(1, "The child has finished executing.\n");
        free(parameters_array);
    }
    free(commands);
}


I've placed some comments in-line to your code below, but XAder's summary is worth reading first, to help understand the big picture before my detailed nitpicks.

I suggest writing out, in very long form, the exact code you would execute for a pipeline of two commands, then the code for a pipeline of three commands, and then four.. and then place a loop around the duplicated code. (The fact that you've only got one fork() suggests to me that you did the code for two commands, but not three. :)

Hope this helps.

/* if numCommands == 1 or 0, the rest is probably useless too
   so this should guard the entire routine, not just the routine
   that creates FIFOs */
if (numCommands > 1)
{
    int j;
    for (j = 0; j < numCommands - 1; j++)
    {
        char *fifo = malloc(strlen("fifo")+1);  /* BUG #1 */
        sprintf(fifo, "%s%i", "fifo", j); /* BUG #2 */
        mkfifo(fifo, S_IWUSR | S_IRUSR );
        free(fifo); /* messy */
    }
}

/* Bug #1 The malloc(strlen()+1) is only good for "fifo", doesn't
   actually allocate space for your number. Don't forget the
   ascii NUL character at the end of the string. Add another +1. */
/* Bug #2 works for numCommands < 10; with 10 commands, you'll need to +2
   for two characters, and so on */
/* messy -- maybe just have a 'char fifo[MAXFIFOLEN];' allocation,
   and set MAXFIFOLEN to 255 or 4096 or something. No need to allocate
   and free a simple little buffer a dozen times. */

pid_t pid = fork();

/* You should probably place fork() in a for loop; one new process
   per command. Another choice is to write this whole routine recursively
   which might be adding too many moving parts into one program */

pmesg(2, "Process forked. ID = %i. \n", pid);
/* Note that parent and child get different values of 'pid', so printing
   the value of pid here may be very confusing */
int status;
if (fork < 0) /* BUG #3 fork() is a function; should be pid */
{
    fprintf(to_write_to, "Could not fork a process to complete the external command.\n");
/* perror() is a wonderful routine! it helps users know _why_ something failed */
    exit(EXIT_FAILURE);
}

if (pid == 0) // This is the child process
/* only ONE path below will ever execute; this is why your fork()
   should be in a loop over numCommands, so each command's process
   can get its own individual stdin and stdout hooked together */
{
    pmesg(1, "input: [%s] output: [%s] input: %d, output: %d backgrounding is %d\n",input_file_name, output_file_name, redirect_input, redirect_output, backgrounding);
    if (numCommands > 1 && i == 0) // we may be pipelining and this is the first process
    {
        char *fifo = malloc(strlen("fifo")+2);
        sprintf(fifo, "%s%i", "fifo", i); /* could just be sprintf(fifo, "fifo%i", i); */
        printf("Initial output: %s.\n", fifo);
        freopen(fifo, "w", stdout);
        //~ unlink(fifo);
        free(fifo);
    }
    else if (numCommands > 1 && i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process
    {
        char *fifo = malloc(strlen("fifo")+2);
        sprintf(fifo, "%s%i", "fifo", i-1);
        printf("Input for process %i: %s.\n", i, fifo);
        freopen(fifo, "r", stdin);
        //~ unlink(fifo);
        char *fifo_2 = malloc(strlen("fifo")+2);
        sprintf(fifo_2, "%s%i", "fifo", i+1);
        printf("Output for process %i: %s.\n", i, fifo);
        freopen(fifo_2, "w", stdout);
        //~ unlink(fifo_2);
        free(fifo);
        free(fifo_2);
    }
    else if (numCommands != 1 &&i == numCommands)
    {
        char *fifo = malloc(strlen("fifo")+2);
        sprintf(fifo, "%s%i", "fifo", i);
        freopen(fifo, "r", stdin);  
        free(fifo);             
    }


In general:

  • bad memory allocation for mkfifo filename
  • according to the code there's logic only for the child process and it can go only by one of 3 paths so always only one process opens file and according to the man pages it shall be blocked until two parties will open fh.
  • i, numofcommands and logic are very unclear - Try this approach
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜