Не се получават всички линии от тръба

Работя върху задача, в която имам нужда от няколко процеса (родител и деца), за да комуникирам. Родителят изпраща файлови пътеки на децата и те трябва да стартират linux file (/usr/bin/file) върху тях, връщайки изхода на бащата.

Засега все още се опитвам да накарам едно дете да работи, така че засега предполагам, че има едно дете.

Възнамерявам да изпратя множество файлови пътища до всяко дете (партида от файлове) и след това да прочета изхода на file.

Проблемът:

Използвам цикъл за write няколко пътя на файла, но когато read изходната тръба на детето, не получавам целия изход, който трябва.

Кодът:

#define Read            0
#define Write           1
#define ParentRead      read_pipe[0]
#define ParentWrite     write_pipe[1]
#define ChildRead       write_pipe[0]
#define ChildWrite      read_pipe[1]
#define PIPE_BUF_LEN    4096

using namespace std;
int main()
{
    /** Pipe for reading for subprocess */
    int read_pipe[2];
    /** Pipe for writing to subprocess */
    int write_pipe[2];
    char buffer[PIPE_BUF_LEN] = "";
    if (pipe(read_pipe) == 0 && pipe(write_pipe) == 0)
    {
        pid_t pid = fork();
        if (pid == -1)
        {
            fprintf(stderr, "Fork failure");
            exit(EXIT_FAILURE);
        }
        else if (pid == 0) //Child process
        {
            close(ParentRead);
            close(ParentWrite);
            dup2 (ChildRead, STDIN_FILENO); /*redirect ChildRead to stdin*/
            dup2 (ChildWrite, STDOUT_FILENO); /*redirect stdout to ChildWrite*/
            char* paramArgs[]={"/usr/bin/file","-n","-f-",NULL};
            execv("/usr/bin/file",paramArgs);
            exit(EXIT_FAILURE);
        }
        else { //Parent process
            close(ChildRead);
            close(ChildWrite);

            for (int i=0; i < 3 ;i++)
            {
                /*write to processes which are ready for writing: */
                fd_set rfds;
                int retval;
                FD_ZERO(&rfds);
                FD_SET(ParentWrite, &rfds);
                retval = select(10, NULL, &rfds, NULL, NULL);
                if (retval == -1)
                {
                    perror("select()");
                }
                else if (retval)
                {
                    write(ParentWrite, "file1\nfile2\n", 12);
                }
                /*read from processes which are ready for reading*/
                FD_ZERO(&rfds);
                FD_SET(ParentRead, &rfds);
                retval = select(10, &rfds, NULL, NULL, NULL);
                if (retval == -1)
                {
                    perror("select()");
                }
                else if (retval)
                {
                    read(ParentRead, buffer, PIPE_BUF_LEN);
                    cout << buffer;
                }
            }
        }
    }
    exit(EXIT_SUCCESS);
}

В този случай се опитвам да стартирам file на file1\nfile2\n (забележете използваните флагове -n -f-) в цикъл от 3 итерации, очаквайки да получа шест реда, но получавам само три:

file1: ГРЕШКА: не може да отвори `file1' (няма такъв файл или директория)

file2: ГРЕШКА: не може да отвори `file2' (няма такъв файл или директория)

file1: ГРЕШКА: не може да отвори `file1' (няма такъв файл или директория)

Когато не пренасоча изхода на детето към канала (оставя го да пише в std_out), получавам всичките шест реда.

Всяка помощ ще бъде оценена.


person Paz    schedule 12.03.2014    source източник
comment
Не е гарантирано, че ще получите толкова байтове, колкото поискате при четене. Ако разпечатате колко байта чете родителят, ще видите, че получавате кратки четения.   -  person Duck    schedule 13.03.2014
comment
Да, но защо получавам тези кратки четения? Как мога да получа всички редове, изведени от файловите извиквания?   -  person Paz    schedule 13.03.2014


Отговори (1)


Планировчикът. Вие сте в цикъл, който моли детето (т.е. pgm file) да търси два файла три пъти. Правите само едно четене на цикъл в родителя.

  • Ако детето е планирано и без прекъсване, то извършва първото търсене на файл, записва канала, прави второто търсене, пише в канала. Родителят се планира и вие четете пълния изход на детето с едно четене. Ти си златен (но късметлия).
  • Детето е планирано, прави първото търсене и пипане и бива прекъснато. Родителят чете един изход. Детето се пренасрочва и прави 2-ро търсене и пише канала. Родителят чете един изход. Тъй като четете само 3 пъти, в крайна сметка четете само 3 от 6 изхода.
  • Вариации на горното. Може би ще прочетете 1 резултат, след това 3 и след това 1. Просто не знаете.

Обърнете внимание, че причината поне да получавате пълен дъщерен изход при всяко четене е, че записите в тръба са гарантирани, че са атомарни, ако са под дължината на PIPE_BUF, каквито са тези. Ако сте използвали този вид рутина, да речем, в сокет, може да получите някаква частична част от съобщение при всяко четене.

Решение: Трябва да четете в цикъл, докато получите очаквания брой байтове (или пълно съобщение). В този случай предполагам, че резултатът от file винаги е един низ, завършващ на нов ред. Но вие искате два file изхода всеки път през цикъла. Така че четете, докато това условие е изпълнено, а именно докато прочетете два пълни низа.

person Duck    schedule 13.03.2014