Заключване на файлове. Какво да направим първо? Отключване или затваряне?

Отключвам и след това затварям. Един процес добавя записи ABCDEFGHIJ. Друг процес чете същия файл и пише ZZZZ. Нормалният файл трябва да бъде ZZZZ ABCDEFGHIJ

Но само веднъж видях следното: ZZZZEFGHIJ Значи записът ABCDEFGHIJ е повреден с ZZZZ.

Изглежда заключването ми не работи. Но го тествах. Един процес наистина чака друг. Може ли друг процес да пише във файла между отваряне и заключване? Или между отключване и затваряне?

Кодът е по-долу

int MyOpenWrite(char *name,int flags) {
int fd
fd = open(name,flags,S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
LockFile(fd);
return fd;
 }

int WriteFile(char *name, char *data) {
...
fd = MyOpenWrite(fullName,O_CREAT|O_WRONLY|O_TRUNC);
write(fd,data,strlen(data));
UnLockFile(fd);
close(fd);
}

int ReadFile(char *name, char *data, int maxLength) {
fd = open(name,O_RDONLY);
LockFile(fd);
...
UnLockFile(fd);
close(fd);
}

int AppendFile(char *name, char *data) {
fd = MyOpenWrite(fullName,O_WRONLY|O_APPEND);
...
len=write(fd,data,strlen(data));
UnLockFile(fd);
close(fd);
 }

int LockFile(int fd) {
struct flock    lock;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
return fcntl(fd,F_SETLKW,&lock);
}

int UnLockFile(int fd)  {
struct flock    lock;
lock.l_type = F_UNLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
return fcntl(fd,F_SETLKW,&lock);
}

Опитах да затворя (fd) UnLock (fd). Изглежда, че работи. Но така или иначе има забавяне между отваряне и заключване. Благодаря.


person nms    schedule 19.03.2013    source източник
comment
Може би трябва да изчистите файла, преди да го отключите?   -  person Lily Ballard    schedule 19.03.2013
comment
Хм. Съмнявам се. Flush просто пише на HDD. Мисля, че ако файлът не е записан на HDD и вторият процес го прочете, той трябва да го прочете от пари, а не от HDD.   -  person nms    schedule 19.03.2013
comment
Не, fflush-ирането на файл всъщност записва файла.   -  person Basile Starynkevitch    schedule 19.03.2013
comment
fflush изчиства stdio буферите. Това не гарантира, че битовете са физически записани на диска от ядрото. Имате нужда от fsync за това.   -  person rra    schedule 19.03.2013
comment
Нямате гаранция, че процес A и B споделят кеш за I/O на файлове.   -  person user7116    schedule 19.03.2013
comment

Един от тези въпроси има директен отговор; за съжаление другият не го прави.

Ще започна с лесното – фактът, че

ractive.data.messages[ "stream_id1" ].stream[1].message
ractive.data.messages[ "stream_id2" ].stream[1].message

принадлежат към същия DOM елемент. Прав сте, че Ractive актуализира съществуващите елементи, вместо да ги премахва и създава нови - това е основна част от неговия дизайн. В този случай това е нежелано поведение, но можете да го заобиколите така:

// instead of immediately switching to a new stream ID like this...
ractive.set( 'current_stream_id', 'stream_id2' );

// you can set it to a non-existent ID. That will cause the existing DOM
// to be removed. When you set it to an ID that *does* exist, new DOM
// will be created:
ractive.set( 'current_stream_id', null );
ractive.set( 'current_stream_id', 'stream_id2' );

// or, if you'd like the initial transitions to complete first...
ractive.set( 'current_stream_id', null ).then(function () {
  ractive.set( 'current_stream_id', 'stream_id2' );
});

Другият проблем - че merge() не се слива, а вместо това се държи така, сякаш правите ractive.set('messages.stream_id1.stream', new_message_stream) - е по-труден. Проблемът е, че докато вие и аз знаем, че {{#messages[ current_stream_id ]}} се равнява на messages.stream_id1, когато current_stream_id === 'stream_id1, Ractive не знае.

Това, което знае, е, че имаме израз, чиято стойност се определя от messages и current_stream_id. Когато стойността на която и да е от тези препратки се промени, изразът се преизчислява и ако тази стойност се промени, DOM се актуализира - но с помощта на стандартен set(). Когато правите ractive.merge('messages.stream_id1.stream', ...), Ractive актуализира всички неща, които зависят от ключови пътеки, които са "нагоре" или "надолу по веригата" на messages.stream_id1.stream - което включва messages. Ето как изразът знае, че има нужда от преоценка.

Възможно е бъдеща версия на Ractive да може да се справи с този случай по по-интелигентен начин. Може би може да отбележи масиви, които са обект на операции по сливане, и да провери резултатите от оценителя, за да види дали са идентични с един от тези масиви, и ако е така, използвайте merge() вместо set(). Може би може да анализира функцията по някакъв начин, за да види дали секцията {{#messages[ current_stream_id ]}} трябва да се регистрира като зависима от messages.stream_id1 толкова дълго, колкото current_stream_id === 'stream_id1', а не вътрешно генерираната ${messages-current_stream_id-} пътека на ключ.

Нищо от това обаче не ви помага междувременно. Единственият начин да използвате merge() в текущата си ситуация е да имате отделна препратка, която не използва израз, и малко магия с наблюдатели на шаблони:

основен_шаблон:

{{#current_messages}} <!-- rather than `messages[ current_stream_id ]` -->
    {{>render_message_stream}}
{{/current_messages}}

render_message_stream:

{{#current_message_stream}} <!-- rather than `stream` -->
    <div class="stream">
        {{>render_message}}
    </div>
{{/current_message_stream}}

код:

ractive.observe( 'current_stream_id', function ( id ) {
    var current_messages = this.get( 'messages.' + id );

    this.set( 'current_messages', current_messages );

    // hide existing stream, then show new stream
    this.set( 'current_message_stream', null ).then(function () {
        this.set( 'current_message_stream', current_messages.stream );
    });
});

// when ANY message stream changes, we see if it's the current one - if so, we
// perform a merge on the top-level `current_message_stream` array
ractive.observe( 'messages.*.stream', function ( new_stream, old_stream, keypath, id ) {
    // the value of any * characters are passed in as extra arguments, hence `id`

    if ( id === this.get( 'current_stream_id' ) ) {
        this.merge( 'current_message_stream', new_stream, {
            compare: function ( item ) {
                return item.id;
            }
        });
    }
});

Настроих JSFiddle, демонстриращ това. Надявам се, че има смисъл, уведомете ме, ако не - и съжалявам, че не успях да отговоря на този въпрос много по-рано.

  -  person LtWorf    schedule 19.03.2013


Отговори (1)


Докато не write(2) към файл (след отключването), няма значение дали първо го отключвате или close(2) първо.

Въпреки това, close(2)-ing на файл ще го отключи (поне ако никой друг процес не споделя същия отворен файлов дескриптор).

Вижте fcntl(2), който казва

As well as being removed by an explicit F_UNLCK, record locks are
automatically released when the process terminates or if it closes any
file descriptor referring to a file on which locks are held.

добавки: трябва да проверява за грешки

Забележете, че в кода ви липсва проверка за грешки. Почти всяка библиотечна функция или системно повикване, по-специално fcntl, read, write, може да се провали (и да настрои errno да се показва от например perror или strerror(errno) в някакъв журнал или печат). Вие не проверявате успеха или неуспеха на fcntl във вашите LockFile или UnlockFile функции и не проверявате нито една от двете в повикващия.

person Basile Starynkevitch    schedule 19.03.2013
comment
Разбирам. Но какво не е наред в ключалката ми, ако не работи? - person nms; 19.03.2013
comment
Трябва да проверите за грешки, както току-що добавих. - person Basile Starynkevitch; 19.03.2013