Хобрук: Ваш путь к мастерству в программировании

Ncurses с неблокирующим вводом и форком

У меня проблемы с правильным чтением и записью между процессом с fork() и ncurses. У меня есть процесс (позвольте мне назвать его сыном), который управляет вводом в приложение, а также у меня есть процесс (родительский), который управляет логикой приложения.

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

Из-за неблокирующего характера я добавил ожидания и сигналы для взаимного исключения процесса при чтении и записи.

Покажу код :)

    //---- pipes, read i write no bloquejants ---------
    int flags_f0 = fcntl(pip_fill[0],F_GETFL);
    int flags_f1 = fcntl(pip_fill[1],F_GETFL);
    int flags_p0 = fcntl(pip_pare[0],F_GETFL);
    int flags_p1 = fcntl(pip_pare[1],F_GETFL);
    int flags_read = fcntl(0,F_GETFL);
    int flags_write = fcntl(1,F_GETFL);


    fcntl(pip_fill[0],F_SETFL,flags_f0 | O_NONBLOCK);
    fcntl(pip_fill[1],F_SETFL,flags_f1 | O_NONBLOCK);
    fcntl(pip_pare[0],F_SETFL,flags_p0 | O_NONBLOCK);
    fcntl(pip_pare[1],F_SETFL,flags_p1 | O_NONBLOCK);
    fcntl(0,F_SETFL,flags_read | O_NONBLOCK);
    fcntl(1,F_SETFL,flags_write | O_NONBLOCK);
    //-------------------------------------------------

    //---- semàfors ----
    int id_Sem;

    id_Sem = shmget (IPC_PRIVATE, sizeof(int), IPC_CREAT | 0600);
    if (id_Sem == -1)
    {
        write(2,"Error!\n",7);
        Sortir(&ll_a_r,&ll_res,&ll_mis,&ll_n_mis,&c,&c_write);
        exit (0);
    }


if(SEM_constructor(&sem1) < 0)
{
       shmctl(id_Sem, IPC_RMID, NULL);
    }

    if(SEM_constructor(&sem2) < 0)
{
        shmctl(id_Sem, IPC_RMID, NULL);
        SEM_destructor (&sem1);
    }

    SEM_init (&sem1, 0);
    SEM_init (&sem2, 0);

//------------------



//la primera vegada que entri en el fill posarem les següents dades a la var valor

Afegir_Cadena(&c,"M0:");    //on M0: menú principal

pantalla(w);
pinta_menu_principal(w,a_menus);    //pinta el menú principal per pantalla

pid = fork();

switch (pid)
{
    case -1: //ERROR
        write(2,"Error!\n",7);
        Sortir(&ll_a_r,&ll_res,&ll_mis,&ll_n_mis,&c,&c_write);
        exit(-1);
            break;

    case 0: //FILL
        close(pip_fill[0]); //tanquem l'extrem de la pipe que no usem (la de lectura)
        close(pip_pare[1]); //tanquem l'extrem de la pipe que no usem (la d'escriptura)

        while(1)
        {
                cc = getch();
                if(cc != ERR)
            {
                if(cc == 0x0A)
                {
                    printf("Enter [%s]\n", Retorna_Cad(&c_write));
                    SEM_wait (&sem1);
                    tmp = write(pip_fill[1],Retorna_Cad(&c_write),Retorna_Longitud(&c_write) + 1);  //el fill escriu a la pipe la variable c
                    SEM_signal (&sem1);                                                     //longitud + 1: el +1 es per el \0
                    //printf("Ret: %d",tmp);
                    Esborra_Cadena(&c_write);
                    actualitza_pantalla(w);
                }
                else
                {
                    Afegir_Caracter(&c_write,cc);
                    cc = ERR;
                }
            }

    //***** READ PARE *********
            SEM_wait (&sem2);
            nbytes = read(pip_pare[0],valor,256); //el fill llegeix de la pipe la var un cop ha estat tractada per el pare
            SEM_signal (&sem2);

            if (nbytes > -1)
            {   
                Inserir_Cad(&c,valor);
                //tractar el missatge del pare
        Tractar_Missatge_del_Pare(&ll_mis,&ll_res,&c,w,a_menus,&ll_a_r);
            }
        }
        break;

    default: //PARE
        close(pip_fill[1]); //tanquem l'extrem de la pipe que no usem (la d'escriptura)
        close(pip_pare[0]); //tanquem l'extrem de la pipe que no usem (la de lectura)

        while(1)
        {
            temps_inici = (float)clock();
            SEM_wait (&sem1);
            nbytes = read(pip_fill[0],valor,256);//el pare llegeix de la pipe la var c 
            SEM_signal (&sem1);

            if (nbytes > -1 || tmp > 0)
            {   //vol dir que hem llegit algo per la pipe pip_fill
                tmp++;
                Inserir_Cad(&c,valor);
                Tractar_Missatge_del_Fill (&ll_mis,&c,&ll_n_mis,w);
                SEM_wait (&sem2);
                   write(pip_pare[1],Retorna_Cad(&c),Retorna_Longitud(&c) + 1); //el pare escriu a la pipe la var tractada
                SEM_signal (&sem2);

                actualitza_pantalla(w);
            }


            temps_final = (float)clock();
            temps_per_bucle = (float)(temps_final - temps_inici) / (float)CLOCKS_PER_SEC;

            Esborra_Cadena(&aux);
            Afegir_Float (&aux, temps_per_bucle);
            //mvwprintw(w[4],8,1,Retorna_Cad(&aux));

        }
        break;
    }
}
else
    {
    exit(1);    //login incorrecte --> sortim de l'aplicacio
}

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

Может быть, я теряю что-то, чего я не вижу прямо сейчас. Дело в том, что сын будет писать в 'pipe_fill', а родитель будет читать из этого пайпа, также родитель будет писать в 'pipe_pare', а потом сын будет читать из него для отправки информации.

Также я использую ncurses для рисования всего окна.

Спасибо за вашу помощь :)

21.06.2011

  • Почему бы вам просто не использовать блокировку ввода-вывода вместо того, чтобы возиться с семафорами? Ваш родитель блокирует семафор, пытается читать, терпит неудачу, разблокирует, снова блокирует, пытается читать и т. д., а бедный сын никогда не получает возможности что-либо написать, потому что он всегда ждет SEM_wait. 21.06.2011
  • если я блокирую только O / I, может ли родитель читать из канала до того, как сын закончит запись в тот же канал? Я думаю, что могу поставить sleep(1) в конце родительского процесса, но мне не разрешено использовать эту функцию для этого приложения :( 21.06.2011
  • Блокировка ввода-вывода сделает именно то, что вы хотите. Просто выбросьте все свои семафоры и fcntls. Вы не делаете этого, когда используете stdio, и здесь вам это не нужно. read будет ждать, пока в канале появятся данные. Не обязательно все 256 байт, но хотя бы 1 байт. 21.06.2011
  • Я с вами согласен, но мне нужна неблокирующая система, то есть мне не нужно, чтобы родители ждали чего-то в трубе. Я думаю, что в этом случае мне нужны fcntls и семафоры, потому что взаимное исключение. Поправьте меня, если я ошибаюсь. 21.06.2011
  • Вам не нужны семафоры, потому что вам не нужны взаимоисключения. Вы можете подумать, что хотите установить порядок: сначала писатель, потом читатель. Это не взаимное исключение, и это невозможно сделать с помощью механизмов взаимного исключения. Но и порядок не нужен, труба волшебным образом все сделает сама. Если вы не хотите, чтобы читатель ждал, тогда сделайте его неблокирующим (но тогда почему вы заставили его ждать семы?) трубка. Он также должен знать, когда он должен прекратить выполнение этой работы и вернуться, чтобы проверить трубу. 21.06.2011
  • Итак, как вы думаете, если поставить «sleep(1)» в конце родительского кода, он будет работать (в неблокирующей среде)? Мое намерение состоит в том, чтобы иметь неблокирующее поведение и только немного подождать, если кто-то пишет, прежде чем другие прочитают. Чего я боюсь, так это того, что сын начинает записывать 100 байт, и после того, как 20 байт записаны, родитель читает и находит только 20 байтов из 100. 21.06.2011
  • Читатель должен зацикливаться, пока не прочитает все сообщение. Писателю нужно зацикливаться, пока он не напишет все сообщение. Вы не можете предположить, что можете прочитать или написать все сообщение за один раз. Как правило, если вы обнаружите, что звоните sleep, чтобы исправить неисправный IPC, вы делаете что-то не так, и IPC останется неисправным. Конечно, если есть определенное требование протокола для ожидания определенного времени, тогда sleep в порядке. 21.06.2011
  • 2 Думает: 1.- Если я уберу сему из кода, родитель ничего не прочитает у сына, я не знаю почему :( 2.- Что касается поведения кода, я думаю, что нужно использовать другой подход, я имею в виду , я добавляю все, что читаю из канала, во временной буфер и периодически проверяю этот буфер, чтобы получить полное сообщение для обработки.Меня больше всего беспокоит то, что родитель "без" sema ничего не читал :( 21.06.2011
  • Родитель ничего не читал, потому что сын ничего не писал. Надо ждать, пока сын что-нибудь напишет. Вы можете сделать это, используя блокирующий IO API, предоставляемый ОС, или вы можете имитировать его, используя неблокирующий ввод-вывод и какой-либо внешний механизм ожидания/сигнализации. Конечный результат тот же, нужно подождать. Я предлагаю вам начать с блокирующего ввода-вывода, и как только он заработает, подумать о том, как вы его модифицируете на неблокирующий. Что касается (2), то это правильный подход. 21.06.2011
  • Прямо сейчас у меня нет семафоров в коде, и запись от сына говорит мне, что он пишет N байтов (то же самое, что я передаю ему), но родитель без семафоров ничего не читал. 21.06.2011
  • Используете ли вы блокирующий или неблокирующий ввод-вывод? Вам нужно начать использовать блокировку. Нет смысла использовать неблокирующий ввод-вывод, а затем ждать завершения ввода-вывода, используя какой-либо внешний механизм ожидания. Blocking IO обеспечивает всю необходимую синхронизацию. Если вы уже используете блокировку, покажите полный минимальный компилируемый пример проблемы. 21.06.2011
  • если у меня есть блокирующий ввод-вывод, которым управляет сын, сын не сможет регулярно читать данные от родителя, поэтому мне нужно, чтобы cc = getch() не блокировал. Кроме того, я использую ncurses, и если я использую функцию «чтения», по какой-то причине она не работает. 21.06.2011
  • Хорошо, теперь у вас есть простой тупик. Родитель ждет, пока ребенок закончит писать, а ребенок ждет родителя. Если вы будете использовать какой-то другой механизм ожидания, взаимоблокировка не исчезнет. Вам нужно решить, кто клиент, а кто сервер. Клиент начинает с вопроса, затем ждет ответа; сервер начинает с ожидания вопроса, затем обрабатывает его и записывает ответ. 21.06.2011
  • Но с NO sema и неблокирующим вводом-выводом я думаю, что нет возможных взаимоблокировок, потому что любая запись или чтение останавливает выполнение. Я не знаю, почему родители ничего не получили. 21.06.2011
  • Взаимоблокировок нет, но и синхронизации нет. Вам не гарантируется, что вы сможете читать после того, как кто-то другой что-то написал. Вам нужно упорядочить вещи. Сначала пишет одна сторона, потом читает другая. Затем другая сторона пишет, а первая сторона читает. Следовательно, вам нужно, чтобы первая сторона выполняла запись/чтение, а вторая сторона выполняла чтение/запись в этом порядке и никак иначе. Тогда блокировка ввода-вывода волшебным образом расставит события в правильном порядке. 21.06.2011
  • Ок, попробую поставить SEM_init(&sem1, 1); (примечание «1»), чтобы разблокировать все семы. Думаю этим я добьюсь синхронизации: Сын будет читать родителя. Когда сын вводит какие-то данные, он напишет родителю, а затем родитель напишет сыну. Поправьте меня, если я ошибаюсь 21.06.2011
  • Я сдаюсь. Должен быть другой способ сказать не используйте семафоры, они вам не помогут, но я его не вижу. 21.06.2011
  • Хорошо, пожалуйста, объясните, как я могу создать родителя, который будет читать данные сына и данные сокета в одном и том же цикле, не дожидаясь данных сына и сокета. Дело в том, что с семафорами или без, сын пишет в трубу, а родитель ничего не видит, и я не знаю почему. 21.06.2011

Ответы:


1

Итак, вот код, который я имел в виду.

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

int main ()
{
  int pipePC[2];
  int pipeCP[2];

  pipe(pipePC);
  pipe(pipeCP);

  char buf[11];
  int n;

  switch (fork()) {
  case -1:
    exit (1);
    break;
  case 0: /* child */
    close(pipePC[1]);
    close(pipeCP[0]);
    while (1) { /* read, then write */
        n = read(pipePC[0], buf, 10); /* read the question */
        if (n > 0) {
            buf[n] = 0;
            printf ("child got '%s'\n", buf); /* calculate the answer here */
            write(pipeCP[1], "foobar", 6); /* write the answer */
        }
        else {
            printf ("child got nothing\n");
            exit (1);
        }
        sleep(2); /* only to slow down the output */
    }
    break;
  default: /* parent */
    close(pipePC[0]);
    close(pipeCP[1]);
    while (1) { /* write, then read */
        write(pipePC[1], "barfoo", 6); /* ask a question */
        n = read(pipeCP[0], buf, 10); /* get the answer */
        if (n > 0) {
            buf[n] = 0;
            printf ("parent got '%s'\n", buf);
        }
        else {
            printf ("parent got nothing\n");
            exit (1);
        }
        sleep(3); /* only to slow down the output */
    }
    break;
  }
}
21.06.2011
  • С помощью этого кода, если вы заблокируете сына, ожидающего ввода, родитель все еще будет зацикливаться? 22.06.2011
  • Нет. В вашем исходном коде и родитель, и дочерний элемент ждут, пока другой закончит писать, и они делают это в моем коде. Если вы хотите, вы можете сделать ввод-вывод неблокирующим с одной или обеих сторон, просто удалите вызовы exit после сообщения ничего не получил. Он по-прежнему будет работать, никаких семафоров не требуется. Иногда они печатают got ничего, а иногда печатают части сообщения или несколько сообщений вместе, например rfoobarfoo. Но вы все равно не должны полагаться на то, что сможете прочитать все сообщение одним вызовом read. 22.06.2011
  • если я удалю только exit, это не предоставит мне неблокирующее поведение для родителя, если сын ожидает ввода, потому что, если в канале нечего читать, родитель будет ждать, пока это не произойдет. Мне нужно, чтобы родитель все еще зацикливался, если нет данных от сына. 22.06.2011
  • Конечно, вам нужно добавить fcntl для неблокирующего ввода. 22.06.2011
  • И затем, если вы представляете, что я удаляю только семафоры в моем исходном коде, почему родитель ничего не получил от сына, если сын правильно пишет в канал? 22.06.2011
  • Новые материалы

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..

    Разбор строк запроса в vue.js
    Иногда вам нужно получить данные из строк запроса, в этой статье показано, как это сделать. В жизни каждого дизайнера/разработчика наступает момент, когда им необходимо беспрепятственно..

    Предсказание моей следующей любимой книги 📚 Благодаря данным Goodreads и машинному обучению 👨‍💻
    «Если вы не любите читать, значит, вы не нашли нужную книгу». - J.K. Роулинг Эта статья сильно отличается от тех, к которым вы, возможно, привыкли . Мне очень понравилось поработать над..

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..