Использование flock, открытие и закрытие файла для реализации многих читателей одной блокировки писателя
У меня есть проект, который состоит из нескольких процессов, которые могут читать или записывать в одну базу данных. Я хочу реализовать блокировку с одним писателем / несколькими считывателями, синхронизированную файлом блокировки с использованием системных вызовов flock / open / close.
В случае сбоя блокировки любая повторная попытка снова взять блокировку будет выполняться на более высоком уровне, который запрашивал блокировку (в отличие от спин-блокировки).
К сожалению, во время тестирования этой модели она потерпела неудачу в сценарии разблокировки, которому не предшествовала блокировка. возможно, вы можете помочь мне найти то, что я сделал не так:
// keep read and write file descriptors as global variables.
// assuming no more than 1 thread can access db on each process.
int write_descriptor=0;
int read_descriptor=0;
int lock_write() {
if((write_descriptor = open(LOCKFILE, O_RDWR|O_CREAT,0644))<0) {
return LOCK_FAIL;
}
if(flock(write_descriptor, LOCK_EX)<0) {
close(write_descriptor);
write_descriptor = 0;
return LOCK_FAIL;
}
return LOCK_SUCCESS;
}
int unlock_write() {
if(!write_descriptor) {
// sanity: try to unlock before lock.
return LOCK_FAIL;
}
if(flock(write_descriptor,LOCK_UN)<0) {
// doing nothing because even if unlock failed, we
// will close the fd anyway to release all locks.
}
close(write_descriptor);
write_descriptor = 0;
return LOCK_SUCCESS;
}
int lock_read() {
if((read_descriptor = open(LOCKFILE,O_RDONLY))<0) {
return LOCK_FAIL;
}
if(flock(read_descriptor, LOCK_SH)<0) {
close(read_descriptor);
return LOCK_FAIL;
}
return LOCK_SUCCESS;
}
int unlock_read() {
if(!read_descriptor) {
// sanity : try to unlock before locking first.
return LOCK_FAIL;
}
if(flock(read_descriptor, LOCK_UN)<0) {
// doing nothing because even if unlock failed, we
// will close the fd anyway to release all locks.
}
close(read_descriptor);
read_descriptor = 0;
return LOCK_SUCCESS;
}
int read_db() {
if(lock_read() != LOCK_SUCCESS) {
return DB_FAIL;
}
// read from db
if(unlock_read() != LOCK_SUCCESS) {
// close fd also unlock - so we can fail here (can i assume that ?)
}
}
int write_db() {
if(lock_write() != LOCK_SUCCESS) {
return DB_FAIL;
}
//write to db.
if(unlock_write() != LOCK_SUCCESS) {
// close fd also unlock - so we can fail here (can i assume that ?)
}
}
1 ответ
В обоих lock_read
а также lock_write
добавьте это как первую строку:
assert ((read_descriptor == 0) && (write_descriptor == 0));
В unlock_read
, Добавь это:
assert (read_descriptor != 0);
И в unlock_write
, Добавь это:
assert (write_descriptor != 0);
И изменить код как:
if(flock(read_descriptor, LOCK_SH)<0) {
close(read_descriptor);
return LOCK_FAIL;
}
чтобы:
if(flock(read_descriptor, LOCK_SH)<0) {
close(read_descriptor);
read_descriptor = 0;
return LOCK_FAIL;
}
Сделайте то же самое для кода записи, чтобы каждый раз, когда дескриптор закрывался, соответствующий глобал устанавливался в ноль. (Вы действительно должны использовать -1 для неверного файлового дескриптора, поскольку ноль допустим.)
Сделайте отладочную сборку и запустите ее. Когда assert
поездки, у вас будет свой преступник.