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


Автор: Xonxt 29.3.2008, 22:07
Доброе время суток.

Нужна помощь по теме.  Проблема в принципе похожа на "читателя-писателя".

Есть текстовый файл. В нем какая-то информация.  С файлом работает несколько процессов читателей и 1 процесс –писатель. Читатели читают из файла. А писатель с клавиатуры файл "обновляет".  Когда работат писатель, то ни один другой процесс работать не может, но процессы читатели могут работать вместе (параллельно). 

Задачу решить нужно с помощью fcntl().

Нужен приблизительный пример такой конструкции.  smile smile

Заранее благодарю.  smile 

Автор: RasenHerz 30.3.2008, 02:52
Функция fcntl() использюется в пяти различных случаях:
1) Создание дубликата существующего дескриптора
2) Получение/установка флагов дескриптора
3) Получение/установка флагов состояния файла
4) Проверка/установка владельца для асинхронных операций IO
5) Получение/установка блокировки на отдельную запись в файле

Решение поставленной тобой задачи использованием одной лишь ф-цией fcntl() хоть и возможно. но думаю излишне мудрено(она ведь всего лишь меняет "св-ва" уже открытого файла.
т.е. тебе надо лишь "грамотно" открыть файл, а там уже дело техники:
Код

int fdes = open("file.txt", O_RDWR | O_TRUNC | O_CREAT | O_SYNC, 0) /*просто синхронизируем операции чтения-записи*/
/*далее просто используй атомарные операции чтения-записи, которые гарантируют, что во время записи(или чтения)
не произойдет чтения(записи) в файл в другом процессе/потоке.
   вот эти операции:
         ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
         ssize_t pwrite(int filedes, void *buf, size_t nbytes, off_t offset);
*/

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

Автор: MAKCim 30.3.2008, 09:49
Цитата(Xonxt @  29.3.2008,  22:07 Найти цитируемый пост)
Задачу решить нужно с помощью fcntl().

Код

pid_t pids[READERS];
int index = 0;
int fd = open(PATH, O_RDWR);
if (fd < 0) {
    /* error */
}
for (; index < READERS; ++index) {
    if ((pids[index] = fork()) > 0)
        reader(fd);
    else if (pids[index] < 0) {
        /* error */
    }
}
writer(fd);
...
static void reader(int fd) {
    struct flock lock = {
        .l_whence = SEEK_SET,
        .l_start = 0,
        .l_len = 1
    };
    while (1) {
        lock.l_type = F_RDLCK;
        if (fcntl(fd, F_SETLKW, &lock) < 0) {
            if (errno == EINTR) continue;
            /* error */
        }
        /* читаем */
        lock.l_type = F_UNLCK;
        if (fcntl(fd, F_SETLK, &lock) < 0) {
            if (errno == EINTR) continue;
            /* error */
        }
    }
}
...
static void writer(int fd) {
    struct flock lock = {
        .l_whence = SEEK_SET,
        .l_start = 0,
        .l_len = 1
    };
    while (1) {
        lock.l_type = F_WRLCK;
        if (fcntl(fd, F_SETLKW, &lock) < 0) {
            if (errno == EINTR) continue;
            /* error */
        }
        /* пишем */
        lock.l_type = F_UNLCK;
        if (fcntl(fd, F_SETLK, &lock) < 0) {
            if (errno == EINTR) continue;
            /* error */
        }
    }
}


Добавлено через 2 минуты и 30 секунд
RasenHerz, 
O_SYNC испольуется не для синхронизации

Автор: Xonxt 30.3.2008, 11:15
MAKCim, ага, большое спасибо. Я примерно так и пытался сделать, но я не додумался устанавливать блокировку чтения перед собственно чтением smile smile 

Автор: Xonxt 30.3.2008, 11:56
Гм..
очень странно, что как только в функцию reader я добавляю какие-либо действия по считыванию из файла, как походу писатель полностью игнорируется. Например:
Код

static void reader(int fd, int pid) 
{
    struct flock lock; 
    char string[200];    
    ...
        /* читаем */

        if(!read(fd, string, 10))
            printf("Reader %d>> %s\n", pid, string);
            ...
}


Т.е. добавление любых действий по чтению или записи (с файлом или с консолью) в reader() отправляет читателя в бесконечный цикл, а "писатель" writer() вообще игнорируется...

Автор: bsa 30.3.2008, 12:33
Xonxt, а ты lock снять после чтения не забываешь?

Автор: Xonxt 30.3.2008, 12:40
Конечно нет! Я же просто фрагмент привел...

Автор: bsa 30.3.2008, 12:46
паузу поставь перед тем, как лочить будешь. А потом, добавь опцию O_SYNC в писатель, а то пока буфер не сбросится, данные не обновятся на диске (другие процессы не увидят их).

Автор: MAKCim 30.3.2008, 13:09
bsa, 
кэш файла не зависит от процесса (структуры struct file)
страница кэша, в которой храняться данные, будет в любом случае обновляться при любой попытке записи
поэтому чтение будет происходить из кэша
O_SYNC здесь вообще не причем

Автор: bsa 30.3.2008, 13:11
Цитата(MAKCim @ 30.3.2008,  13:09)
bsa, 
кэш файла не зависит от процесса
O_SYNC здесь вообще не причем

странно. Вот запускаю что-то через nohup и смотрю с помощью tail -f, так вывод дается рывками (поблочно). Или это особенность nohup?

Автор: MAKCim 30.3.2008, 13:13
Цитата(bsa @  30.3.2008,  13:11 Найти цитируемый пост)
странно. Вот запускаю что-то через nohup и смотрю с помощью tail -f, так вывод дается рывками (поблочно). Или это особенность nohup? 

что запускаешь?

Автор: bsa 30.3.2008, 13:39
Цитата(MAKCim @ 30.3.2008,  13:13)
что запускаешь?

Например:
Код
$ nohup ./configure ... &
$ tail -f nohup.out

  smile
Думаю, что раз вывод происходит в файл, а не терминал, то реакции на \n (сброс буфера) характерной для терминалов нет. Поэтому вывод производится по мере заполнения буфера...

Автор: Xonxt 30.3.2008, 14:01
Гмм... 

Ув. MAKCim,  можно ли попросить Вас в приведенном Вами выше примере кода вставить реальные примеры чтения и записи. В reader() текст читается из файла и выводится на экран, а в writer() - считыается с клавиатуры и записыается в файл. 

А то что-то программа на этих моментах тормозит (читай, уходит в бесконечный цикл).  smile 

Added:  и вообще, походу при каждом новом запуске программы оно выдает разные результаты... Наверное, из-за того, что каждый раз процессы отрабатывают в разном порядке... Вот сейчас только что программа отработала как положено! А в следующий раз - опять reader() ушел в бесконечный цикл...

Автор: MAKCim 30.3.2008, 14:21
Xonxt, 
файл вначале пустой или содержит какие-либо данные?

Автор: Xonxt 30.3.2008, 14:23
Изначально содержит данные.

Похоже, что для того, чтобы реализовать еще и ввод текста с клавиатуры, надо делать процессы не fork'ом, а просто отдельными приложениями...

Автор: Xonxt 30.3.2008, 15:58
Ага... примерно получилось решит ьзадачу с помощью добавления семафоров..

Типа этого:
Код
struct sembuf sopen[1]={0,1,0}, sclose[1]={0,-1,0};

int main(){
    pid_t child[3];
    int semaf;
    char string[10];
    
    semaf = semget (SEM, 1, IPC_CREAT|IPC_EXCL|0666);
    semctl(semaf, 0, SETVAL, 0);
    
    for(int i = 0; i < READERS; i++)    {
        if((child[i] = fork()) == 0){
            while(1){
                semop(semaf, sclose, 1);
                printf("Превед! %d\n", i);                
                //sleep(1);
            }    
        }
    }
    
    while(1){
        semop(semaf, sopen, 1);
        scanf("%s", &string);
    }
    return 0;
}


Но сталкиваюсь с дркгой пролблемой, правда, не очень серъезной. Теперь при запуске программы сначала "писатель" отрабатывает один раз, а затем сразу все 5 читателей отрабатывают по 2 раза. Потом писатель отрабатыавет раз 5, и сразу читатели по 3-4 раза.... 

Автор: bsa 30.3.2008, 23:10
Xonxt, а ты в файл данные добавляешь, или пишешь в середину (например)? Просто если добавляешь, то можно попробовать воспользоваться inotify

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