2017-02-02 8 views
-1

Моя задача - написать программу на языке C, которая выполняет команду «ls -l/bin/?? | grep rwxr-xr-x | sort». Существует 3 дочерних процесса, каждый из которых выполняет одну из команд отдельно и отправляет результат через канал в следующий дочерний процесс. Я использую шведскую модифицированную версию debian, поэтому сообщение об ошибке на шведском языке, но я переведу ошибку, которую я получаю, это что-то вроде строк: sort: failed to status -: unknown fileidentifier.Трубы в c linux для подключения 3 процессов для выполнения команды

Возможно, это мои трубы, которые не работают должным образом, я не слишком уверен в командах close(). Я уверен, что ошибка исходит из труб. Был бы признателен, если бы кто-то мог запустить программу и получить сообщение об ошибке на английском языке.

#include <stdio.h> 
#include <sys/types.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <assert.h> 
#include <errno.h> 
#include <string.h> 


int main() 
{ 
    int ret; 
    int fds1[2], fds2[2], fds3[2]; 
    char buf[20]; 

    pid_t pid; 

    ///initiating pipes 
    ret=pipe(fds1); 

    if(ret == -1){ 
     perror("could not pipe"); 
     exit(1); 
    } 

    ret=pipe(fds2); 

    if(ret == -1){ 
     perror("could not pipe"); 
     exit(1); 
    } 

    ret=pipe(fds3); 

    if (ret == -1){ 
     perror("could not pipe"); 
     exit(1); 
    } 

    pid=fork(); 

    if(pid==-1){ 

     fprintf(stderr,"fork failed"); 
     exit(0); 
    } 

    if(pid==0){ 
     ///CHILD 1 

     close(1); 
     dup(fds1[1]); 

     close(fds1[0]); 
     close(fds1[1]); 

     close(0); 
     execlp("/bin/sh","bin/sh", "ls-l /bin/??", (char *)NULL); 
    } 
    else{ 
     wait(0); 
    } 

    pid=fork(); 

    if(pid==-1){ 

     fprintf(stderr,"fork failed"); 
     exit(0); 
    } 

    if(pid==0){ 

     close(0); 
     dup(fds1[0]); 

     close(fds1[0]); 
     close(fds1[1]); 

     close(1); 
     dup(fds2[1]); 

     close(fds2[0]); 
     close(fds2[1]); 

     execlp("/usr/share/grep/", "grep", "rwxr-xr-x", NULL); 
    } 
    else{ 
     wait(0); 
    } 

    close(fds1[0]); 
    close(fds1[1]); 

    pid=fork(); 

    if(pid==-1){ 

     fprintf(stderr,"fork failed"); 
     exit(0); 
    } 

    if(pid==0){ 

     close(0); 
     dup(fds2[0]); 
     close(fds2[0]); 
     close(fds2[1]); 

     execlp("sort", "sort", NULL); 
    } 
    else{ 
     wait(0); 
    } 

    close(fds2[0]); 
    close(fds2[1]); 
} 
+0

Извините за комментарий к коду на шведском языке, забыл изменить его, и я здесь новый, не знаю, как отредактировать свой текст сейчас, когда он опубликован. В любом случае, последняя команда execlp, которую я знаю, определенно неверна. Any1 умеет запускать сортировку с помощью execlp? –

+0

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

+0

«p» в 'execlp' означает, что он автоматически ищет путь, поэтому вы можете просто указать' execlp («sort», «sort», NULL); 'without path. –

ответ

1

Ваш код имеет несколько проблем, но прежде чем я их обсуждать, позвольте мне представить вам аромат одного из моих любимых макросов препроцессора:

#define DO_OR_DIE(x, s) do { \ 
    if ((x) < 0) { \ 
     perror(s); \ 
     exit(1); \ 
    } \ 
} while (0) 

Используя этот макрос, где это применимо может прояснить ваш код, заменив всю проверку ошибок шаблона. Например, это:

ret=pipe(fds1); 

if(ret == -1){ 
    perror("could not pipe"); 
    exit(1); 
} 

становится просто

DO_OR_DIE(pipe(fds1), "pipe"); 

Это делает его гораздо легче увидеть и сосредоточиться на ключевых частей кода, и это проще набрать, слишком , В результате это также уменьшает соблазн пропустить проверку ошибок, например, для ваших вызовов до dup().


Теперь, что касается вашего кода. Для меня, он показывает не только один проступок теперь вы описали в своем вопросе, но три:

  • Он излучает сообщение об ошибке «бен/ш: LS-л/бен/??: Нет такого файла или каталога ».
  • Он испускает сообщение об ошибке, которое вы описываете, «sort: stat failed: -: Bad file descriptor».
  • Не завершается.

Первое сообщение об ошибке относится к нескольким проблемам в аргументах вашего первого вызова execlp(). Если вы хотите запустить оболочку и указать команду для ее запуска, в отличие от файла, из которого следует , выполните команды, тогда вы должны передать ему -c. Кроме того, вы исключили обязательные пробелы между ls и его аргументами. Похоже, что вы хотите:

execlp("/bin/sh","sh", "-c", "ls -l /bin/??", (char *)NULL); 

Отложив вторую проблему на данный момент, давайте обратимся к невозможности прекратить.У вас есть несколько проблем в этой области, относящиеся к этим категориям:

  • Держа концы труб открыты, где вы должны обеспечить их закрытыми
  • Вызова wait() в неправильных точках

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

Процесс на считываемом конце трубы не будет видеть EOF на этой трубе до все дескрипторы открытых файлов на конце записи закрыты. Программы, выполняющие дочерние процессы, такие как grep и sort, которые читают свой ввод до конца, будут зависать бесконечно, если конец записи не полностью закрыт.

Это может быть особенно извращенной проблемой, когда ребенок, читающий трубу, также имеет копию конца записи этой трубы, неиспользованную, или если один из ее братьев и сестер делает.

Кроме того, вся точка конвейера заключается в том, что задействованные процессы выполняются одновременно. Если вы начинаете с wait(), перед запуском следующего, то, как минимум, вы избегаете такого параллелизма. Хуже того, что также может привести к зависанию вашей программы, потому что канал имеет конечную емкость буфера. Если ребенок записывает вывод в трубу, но никто его не читает, буфер буфера может заполняться до емкости, после чего дочерний блок блокируется. Если родитель ждет, пока ребенок закончит работу, прежде чем запускать процесс, который вытолкнет трубу, тогда у вас есть тупик. Поэтому сначала вы должны запустить все процессы в конвейере, а затем ждать их всех.

Зафиксировав такие проблемы в вашем коде, я считаю, что программа выдает другую ошибку для меня:

функций имеют: Нет такой файл или каталог

(Специфика этого сообщения выводит от характера моих исправлений.) Это должно быть особенно актуально, потому что если execlp() терпит неудачу, то он возвращается в процессе, в котором он был вызван. В ваших случаях управление будет выпадать прямо из вашего оператора if в код, предназначенный только для родителя. По этой причине необходимо обрабатывать ошибки от execlp(). Как минимум, добавьте звонок сразу после exit() или _Exit().

Но что не получается? Ну, на этот раз это grep. Обратите внимание, что вы указываете команду для выполнения как "/usr/share/grep/" - что конечный / ошибочен, и сам путь является подозрительным. В моей системе, правильный путь /usr/bin/grep, но так как мы используем execlp, который решает исполняемый в пути, мы могли бы также опустить путь в целом:

execlp("grep", "grep", "rwxr-xr-x", (char *) NULL); 

вуаля! Сделав эту коррекцию, ваша программа работает для меня.


Дополнительный совет: не используйте dup(), когда вы заботитесь, что дескриптор файла номер, который вы хотите дублировать, чтобы, например, когда вы пытаетесь DUP на один из стандартных потоков. Используйте для этого dup2(), что имеет дополнительное преимущество, что вам не нужно сначала закрывать указанный файловый дескриптор.

+0

Спасибо, Джон. Я заменил свои вызовы exec своим и убрал мои ожидания, и теперь у меня получается похожий (не идентичный) результат запуска моей программы по сравнению с написанием «ls -l/bin/?? | grep rwxr-xr-x | sort» в терминал. В моей программе отсутствует первая строка, строка «bin/cp». Поэтому я подозреваю, что что-то по-прежнему не так. Моя программа не заканчивается, как вы упомянули выше, как проблема. Я думаю, что неправильно обрабатываю трубы. Если у вас есть правильная версия моей программы, я был бы очень благодарен, если бы вы могли ее разместить здесь, чтобы я мог узнать, как обрабатывать трубы. Спасибо –

+0

Редактировать: строка «bin/cp» есть, но в строке командной строки, прочитайте мой ответ ниже для обновления, я использовал dup2() вместо dup, как вы рекомендовали. Но моя программа не заканчивается, я думаю, что что-то по-прежнему не так с моими трубами. –

+0

Я не согласен с вашим макросом (я нахожу это менее ясным, чем выписанная обработка ошибок - ну, за исключением бессмысленного присвоения 'ret' в оригинале), но я подтвердил ваш ответ, поскольку остальная часть его превосходна , –