Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Программирование под Unix/Linux > непонятная блокировка


Автор: Anark1 17.12.2009, 02:27
Здравствуйте,
подзадача - между клиентом и сервером организован именованный канал и очередь сообщений, очередь сообщений для синхронизации, канал - для передачи данных.
В клиенте формируется команда (например edit, подразумевающая отправку серверу введенные с клавиатуры данные), далее клиент пишет в канал.
Собственно проблема. В самый первый раз все прекрасно работает, клиент пишет, сервер читает, но уже второй раз происходит блокировка.

Код

client:
#define FIFO_NAME "srv_fifo\0"
...
fifo = open(FIFO_NAME,O_RDWR);
...

while (ok)
    {
        printf("Введите команду\n");
        scanf("%s",menu);
        if (memcmp(menu,edit,strlen(menu)) == 0) //проверка, что введенная команда является edit
            {    
                msg.type = SC; //инициализация сообщения для сервера
                msg.code = MSG_EDIT;
                msgsnd(msid,(msg_buf*)(&msg),sizeof(int),0); //отправка сообщения
                printf("Введите элементы поля\n");
                for (i=1;i<=height*width;i++)
                {
                    scanf("%d",&num);
                    write(fifo,&num,sizeof(int)); //блокировка возникает во время второй итерации цикла
                }
            }
...


Все структуры сообщений и константы описаны и работа с очередью выполняется корректно

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

Код

server:

#define FIFO_NAME "srv_fifo\0"    
...
mkfifo(fifo_name, S_IFIFO | 0666);
fifo = open(FIFO_NAME, O_RDONLY);
...
msgrcv(msid,(msg_buf *)(&msg),sizeof(int),SC,0); //принимаем сообщение
switch (msg.code)
{
    case MSG_EDIT:
                ...
        LoadFromFIFO(fd, fifo,wrk_def,wrk_last,wrk_count,height); //пропущен блок синхронизации дочерних процессов
                ...

//функция LoadFromFIFO считывает данные из нашего именованного канала и распределяет их по неименованным каналам
void LoadFromFIFO(int * fd, int fifo, int wrk_def, int wrk_last, int wrk_count, int height)
{
    int i,j,num;
    for (i=1;i<wrk_count;i++)
    {
        for (j=1;j<=height*wrk_def;j++)
        {
            read(fifo,&num,sizeof(int));
            write(fd[2*(i-1)+1],&num,sizeof(int));
        }
    };
    
    for (j=1;j<=height*wrk_last;j++)
    {
        read(fifo,&num,sizeof(int));
        write(fd[2*(wrk_count-1)+1],&num,sizeof(int));
    };
}


fd - массив дескрипторов неименованных каналов.
Непонятно вообще почему происходит блокировка. И интересно, что если открывать канал в клиенте с флагом O_NONBLOCK то результат такой же.
Уже слегка замучался, буду очень благодарен.

Автор: Anark1 17.12.2009, 03:24
Странные вещи происходят. Немного продвинулся в решении проблемы. Оказывается при второй итерации цикла в очередь просто не отправляются\не читаются сообщения. Интересный момент, если выполнить вторую команду (сервер повисает, не может получить это сообщение), потом аварийно "убить" сервер и заново его запустить, то автоматически срабатывает та команда которая должна была быть выполнена в прошлый раз (на которой сервер повис).
Я уже совсем не понимаю как такое возможно.

Автор: svlary 17.12.2009, 06:19
У Вас запись в канал происходит вот столько раз :

Код

 for (i=1;i<=height*width;i++)
                    write(fifo,&num,sizeof(int));
   .   .  .

   А чтение  - вот столько :

Код

   for (i=1;i<wrk_count;i++)
 
        for (j=1;j<=height*wrk_def;j++)
            read(fifo,&num,sizeof(int));


   Почти на 100% уверен, что число запросов на ЧТЕНИЕ превышает число ЗАПИСЕЙ.
Вот Ваш сервер и виснет - на запросе чтения.

Автор: Anark1 17.12.2009, 10:07
svlary, 
я уже написал, что первый запрос проходит нормально и все читается как нужно. А то что вы привели, это все хорошо. Посмотрите код ниже. Поле размером height*width. Разбивается на полосы, их число wrk_count, высота у всех height, а ширина всех кроме последней wrk_def. У последней полосы ширина wrk_last (так как разбиение может быть неравномерным).

Код

void LoadFromFIFO(int * fd, int fifo, int wrk_def, int wrk_last, int wrk_count, int height)
{
    int i,j,num;
//данный цикл читает  общей сложности height*(wrk_count-1)*wrk_def

    for (i=1;i<wrk_count;i++)
    {
        for (j=1;j<=height*wrk_def;j++)
        {
            read(fifo,&num,sizeof(int));
            write(fd[2*(i-1)+1],&num,sizeof(int));
        }
    };

//данный цикл читает wrk_last*height; 
//итого в сумме с первым чтением height*((wrk_count-1)*wrk_def+wrk_last) == height*width;
    
    for (j=1;j<=height*wrk_last;j++)
    {
        read(fifo,&num,sizeof(int));
        write(fd[2*(wrk_count-1)+1],&num,sizeof(int));
    };
}


так что тут все в порядке

Автор: MAKCim 17.12.2009, 13:42
посмотрите через strace -f, на чем зависает программа и что к этому приводит

Автор: Anark1 17.12.2009, 14:07
Провозился, все сводится видимо к некорректности работы очереди сообщений. Эту тему наверное можно закрывать.

Автор: svlary 18.12.2009, 06:52
Цитата(Anark1 @  17.12.2009,  14:07 Найти цитируемый пост)
все сводится видимо к некорректности работы очереди сообщений.

  Э... Вы намекаете, что неправильно работают ОЧЕРЕДИ ? А Ваша программа - правильная. Так ? smile

Автор: Anark1 21.12.2009, 12:47
Цитата(svlary @ 18.12.2009,  06:52)
Цитата(Anark1 @  17.12.2009,  14:07 Найти цитируемый пост)
все сводится видимо к некорректности работы очереди сообщений.

  Э... Вы намекаете, что неправильно работают ОЧЕРЕДИ ? А Ваша программа - правильная. Так ? smile

Я намекаю на то, что в cygwin IPC ресурсы реализованы не лучшим образом.
У меня не очень большой опыт написания программ под Unix, однако уверен, что мои огрехи виноваты в меньшей степени. Перепроверив код миллион раз в конечном итоге я установил виртуальную машину с FreeBSD. Под FreeBSD все работало корректно, причем код не менял.
А косяков в cygwinе было полно. Висела очередь при обмене сообщениями разных размеров, необъяснимые блокировки FIFO (не знаю при чем тут FIFO), при удалении/добавлении назад некоторых операторов взаимодействия с IPC, не влияющих на дальнейшую работу программы иногда вылетало что-то вроеде fork: child хххх - died waiting for dll loading, которое работало до этого.
Погуглил на этот счет, не у меня одного такие проблемы.
Стоило сразу ставить виртуалку, сэкномоил бы кучу времени и сил.

Автор: MAKCim 21.12.2009, 12:49
Anark1, 
винить cygwin нужно винить только в очень крайнем случае ;)
99% что ошибка у вас

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)