2010-08-19 6 views
6

Помещение: Напишите программу для запроса пользователя для двух входных строк. Каждая входная строка должна быть командой unix с разрешенными аргументами. Например, вход 1 может быть ls -l, а вход 2 может быть more. Затем программа создаст канал и два дочерних процесса. Первый дочерний процесс будет запускать команду, указанную в первом входе. Он будет выводиться на трубу вместо стандартного вывода. Второй дочерний процесс будет запускать команду, указанную во втором входе. Он будет принимать свой вход от трубы, а не от стандартного ввода. Родительский процесс будет ждать, пока его двое детей закончат, тогда все это повторится. Выполнение прекращается, когда символ «@» вводится в качестве первой команды. Вот код, который у меня есть:Трубы и процессы

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

int main(){ 

    /* Program Termination Symbol */ 
    const char terminate = '@'; 

    /* String delimiter */ 
    const char delimiter = ' '; 

    /* Pipe file ID's */ 
    int fileID[2]; 

    /* Parent ID's */ 
    int pid1, pid2; 

    /* String token */ 
    char * token, * token2; 

    /* User input */ 
    char * user_input, line[100]; 

    user_input = (char *) malloc(100); 

    /* Unix Commands */ 
    char * command1[10], *command2[10]; 

    for (int i=0; i<10; i++) 
    { 
    command1[i] = (char *)malloc(100*sizeof(char)); 
    command2[i] = (char *)malloc(100*sizeof(char)); 
    } 

    /* Begin main program logic */ 

    printf("Please enter the first command: \n"); 

    user_input = gets(line); 

    while (user_input[0] != terminate) 
    { 
    token = (char *) malloc(100*sizeof(char)); 

    for (int i=0; i<10; i++) 
     { 
     if (i == 0) 
     { 
     token = strtok(user_input, &delimiter); 
     } else { 
     token = strtok(NULL, &delimiter); 
     } 

     if (token != NULL) 
     { 
     strcpy(command1[i], token); 
     } else { 
     command1[i] = 0; 
     } 
     } 

    printf("Please enter the second command: \n"); 
    user_input = gets(line); 

    token2 = (char *) malloc(100*sizeof(char)); 

    for (int i=0; i<10; i++) 
    { 
     if (i == 0) 
     { 
     token2 = strtok(user_input, &delimiter); 
     } else { 
     token2 = strtok(NULL, &delimiter); 
     } 

     if (token2 != NULL) 
     { 
     strcpy(command2[i], token2); 
     } else { 
     command2[i] = 0; 
     } 
    } 


    /* Pipe and execute user commands */ 

    /* Create pipe */ 
    pipe(fileID); 

    /* Create child processes */ 

    pid1 = fork(); 

    if (pid1 != 0) 
    { 
     pid2 = fork(); 
    } 

    /* First child process */ 
    if (pid1 == 0) 
    { 
     dup2(fileID[1], 1); 
     execvp(command1[0], command1); 
    } 

    /* Second child process */ 
    if (pid2 == 0) 
    { 
     dup2(fileID[0], 0); 
     execvp(command2[0], command2); 
    } 

    /* Wait for children to terminate */ 
    wait(&pid1); 
    wait(&pid2); 

    /* Repeat */ 
     printf("Please enter the first command: \n"); 
    user_input = gets(line); 
    } 

    return 0; 
} 

Проблема, с которой я сталкиваюсь, связана с моими ожиданиями. Если у меня есть и то, и другое, что имеет смысл для меня (одно ожидание на ребенка), тогда программа зависает после выполнения первого канала. Если я удалю второе ожидание, программа снова начнет цикл, но не будет принимать ввод с клавиатуры, кроме ввода, и выдает segfault. Таким образом, при обоих ожиданиях вход и выход ...

Please enter the first command: 
ls 
Please enter the second command: 
more 
Pipe 
Pipe.c 
Pipe.c~ 

... а затем он блокируется. Если я удалю второе ожидание, вход/выход будет ...

Please enter the first command: 
ls 
Please enter the second command: 
more 
Pipe 
Pipe.c 
Pipe.c~ 
Please enter the first command: 
(I hit enter, nothing else will work) 
Segmentation fault 

У кого-нибудь есть предложения? Это явно связано с ожиданием двух процессов, но я не понимаю, как с этим справиться.


Эта программа теперь функциональна на 100% - большое вам спасибо за помощь, всем! Переполнение стека было одним из лучших ресурсов в Интернете. Большое вам спасибо за то, что нашли время, чтобы посмотреть мой код и дать мне свои предложения.

+0

Вам нужно подождать только ребенка в конце трубы. –

+0

@PeterRitchie Обратите внимание, что тег домашней работы устарел (читайте описание). Благодарю. – Tim

ответ

5

Я согласен со всем, что сказал torak, но для решения вашей проблемы вам нужно закрыть свои трубы. Я думаю, что вы «висели», потому что труба все еще открыта.

Так что в родительском доме, прямо перед «ожиданиями», я бы закрыл трубы.

close(fileID[0]); 
close(fileID[1]); 
wait(&pid_status); 
wait(&pid_status); 

Затем, прямо перед каждым execvp, я бы закрыть концы трубы ребенок не будет использовать:

close(fileID[0]); 
dup2(fileID[1], 1); 
execvp(command1[0], command1); 


close(fileID[1]); 
dup2(fileID[0], 0); 
execvp(command2[0], command2); 

Это должно решить повешение. В дополнение к предложениям, сделанным torak, я бы также рекомендовал fgets вместо того, чтобы предотвращать переполнение буфера.

+0

Это решило повесить, большое спасибо. Программа теперь функционирует (в основном) правильно. Это задание сбило меня с ума. На прошлой неделе мы обсуждали вопросы управления памятью, поэтому я был уверен, что наше программирование будет охватывать это ... но, нет, из левого поля, труб. – rybosome

4

Несколько вещей. Не уверен, что они являются причиной ваших проблем, но все же нужно рассмотреть, прежде чем отправлять домашнее задание.

  1. Я не думаю, что вы используете wait правильно. Согласно http://linux.die.net/man/2/wait он не принимает указатель pid в качестве аргумента.

  2. Каждый раз, когда цикл находится в цикле, вы вызываете malloc для token и token2, но я не вижу соответствующего выпуска памяти.

  3. Вы создали единую монолитную функцию. Хорошая практика кодирования предложила бы разбить ее на набор подпрограмм

  4. Наконец, и, возможно, это связано с точкой 3, в вашем коде дважды появляются две строки кода. Опять же, это не ошибка, а ненужное дублирование и неэлегантность.

    printf («Пожалуйста, введите первую команду: \ n"); user_input = получает (строка);

+0

Да, ожидание принимает статус int *. Рассмотрим использование waitpid, который принимает pid, статус и параметры в качестве аргументов. – Robb

+0

Я, вероятно, должен знать, но может кто-то объяснить мне, почему код в конце моего ответа не выходит правильно. – torak

+0

Вы помните, чтобы отступать от линии? –

1

Прежде всего, вы вызываете wait из дочерних процессов, а также [править: нет, это не так, так как каждый ребенок называет execvp].

Кроме того, wait не принимает указатель на pid ребенка, а на переменную, в которой будет записан статус процесса (что означает, что вы выбрасываете pid вашего ребенка).

Наконец, попробуйте использовать waitpid с опцией «WNOHANG». Он не будет висеть, вы можете поместить оба цикла в то время как вы делаете другие вещи, и вы можете проверить, завершили ли дочерние процессы проверку переменных состояния. man waitpid.

+0

Я считаю, что на самом деле я не вызываю wait из дочерних процессов - когда встречается оператор execvp, остальная часть кода в текущем процессе не выполняется. Он заменяется кодом вызова execvp. По крайней мере, это мое понимание этого. Что касается использования pid как переданного значения ... Меня не интересует состояние, которое возвращается из wait, поэтому я передаю ему значение, которое мне больше не нужно. По моему мнению, wait просто будет ждать одного дочернего процесса - нет способа контролировать, что с этой функцией. – rybosome

+0

Правда, execvp останавливает выполнение детей. Я исправлю свой ответ. Что касается pid, вам может не понадобиться это сейчас, но это очень * плохая форма для повторного использования переменных для различных целей, подобных этому, и вы можете в конечном итоге изменить свою программу таким образом, что вам понадобится pid позже. Поэтому, особенно для задания домашней работы, вы должны использовать разные переменные для статуса. Нет смысла пытаться быть экономным с именами переменных. И функция waitpid позволяет определить конкретный pid для ожидания. Взгляните на его справочную страницу. – rbp

+0

Вы правы, что это плохая практика - я должен просто сделать другую переменную. В наши дни это не похоже на одну переменную int - это ужасно необходимое пространство, которое должно быть сохранено любой ценой. Я изменил свою программу, чтобы сделать это. Я ценю ваше предложение! – rybosome

Смежные вопросы