Господа, такой вопросик, даже два.
Выполняю асинхронную запись в файл через mmap, примерно так:
msync(ptr, 4096, MS_ASYNC);
msync(ptr + 4096, 4096, MS_ASYNC);
1. Гарантируется ли в этом случае последовательность записи данных на диск, то есть сначала будет записана первая страница, затем вторая? Либо для асинхронных опреаций в общем случае никакой последовательности не соблюдается?
2. Если сразу после второго вызова msync (или во время его выполнения в другом потоке), вторая страница будет перезаписана, то какие данные в конечном итоге попадут в файл? Те, которые были на момент вызова msync, или на момент физической записи на диск?
Здравствуйте, valuea, Вы писали:
V>1. Гарантируется ли в этом случае последовательность записи данных на диск, то есть сначала будет записана первая страница, затем вторая? Либо для асинхронных опреаций в общем случае никакой последовательности не соблюдается?
Не гарантируется и не соблюдается.
V>2. Если сразу после второго вызова msync (или во время его выполнения в другом потоке), вторая страница будет перезаписана, то какие данные в конечном итоге попадут в файл? Те, которые были на момент вызова msync, или на момент физической записи на диск?
В конечном итоге попадут в файл те данные, которые были на момент вызова munmap.
msync с флагом MS_ASYNC — это вызов-подсказка. Он не влияет на данные, он лишь сообщает ОС что пора бы уже начать их сбрасывать на диск. Этот вызов никак не влияет на то что будет записано на диск «в конечном итоге», он лишь может влиять на время когда это произойдёт. То есть какие-то отличия в содержимом файла могут проявится разве что только в аварийных ситуациях вроде внезапного отключения электропитания. Впрочем с практической точки зрения это почти бесполезно — гарантированно избечь повреждений в такой ситуации всё равно нельзя.
И ещё раз, этот вызов лишь подсказка, а подсказки ОС может игнорировать. Например, некоторые ядра linux не инициируют запись вообще при наличии флага MS_ASYNC — и это вполне допустимое поведение.
Здравствуйте, watchmaker, Вы писали: W>Не гарантируется и не соблюдается.
То есть, соблюсти последовательность кроме синхронного вызова с ожиданием подтверждения — никак не получится. Возможно ли тогда для экономии ресурсов применить MAP_NOSYNC, дабы ОС не скидывала данные самостоятельно, а только тогда, когда потребуется приложению?
W>msync с флагом MS_ASYNC — это вызов-подсказка. Он не влияет на данные, он лишь сообщает ОС что пора бы уже начать их сбрасывать на диск.
Ок, понял, наверно это просто добавление команды на сброс кэша в какую-нибудь AIO-очередь. Но в какой-то момент при синхронизации "грязные" страницы все-равно должны быть заблокированы на запись?
Здравствуйте, valuea, Вы писали:
V>1. Гарантируется ли в этом случае последовательность записи данных на диск, то есть сначала будет записана первая страница, затем вторая? Либо для асинхронных опреаций в общем случае никакой последовательности не соблюдается?
Нет никаких гарантий.
V>2. Если сразу после второго вызова msync (или во время его выполнения в другом потоке), вторая страница будет перезаписана, то какие данные в конечном итоге попадут в файл? Те, которые были на момент вызова msync, или на момент физической записи на диск?
Нет никаких гарантий. В зависимости от ядра и настроек, могут оказаться любые данные.
Здравствуйте, valuea, Вы писали:
V>Ок, понял, наверно это просто добавление команды на сброс кэша в какую-нибудь AIO-очередь.
Конкретно MS_ASYNC — это просто сигнал на то, что надо начать сбрасывать writeback-буфер. Никаких барьеров не ставится.
V>Но в какой-то момент при синхронизации "грязные" страницы все-равно должны быть заблокированы на запись?
В текущей инкарнации в Линуксе в драйвер диска уйдут те данные, которые окажутся в этот момент на странице. Никакой блокировки не происходит.
Sapienti sat!
Re: mmap асинхронная запись
От:
Аноним
Дата:
31.07.14 19:53
Оценка:
Здравствуйте, valuea, Вы писали:
Здравствуйте, valuea, Вы писали:
Здесь реализация из линукса. Видно, что при маппинге файла с MAP_SHARED
флаг MS_SYNC имеет тот же эффект что и сискол fsync.
V>То есть, соблюсти последовательность кроме синхронного вызова с ожиданием подтверждения — никак не получится. Возможно ли тогда для экономии ресурсов применить MAP_NOSYNC, дабы ОС не скидывала данные самостоятельно, а только тогда, когда потребуется приложению?
Тут уже пора бы переходить к описанию задачи, которую ты хочешь решить.
В первую очередь, зачем вообще тебе нужно соблюдать порядок записи? Почему бы не оставить эту задачу на усмотрение ОС?
W>>msync с флагом MS_ASYNC — это вызов-подсказка. Он не влияет на данные, он лишь сообщает ОС что пора бы уже начать их сбрасывать на диск. V>Ок, понял, наверно это просто добавление команды на сброс кэша в какую-нибудь AIO-очередь.
Добавление задания в отложенную очередь — это допустимая реализация. Но и игнорирования вызова — тоже.
V> Но в какой-то момент при синхронизации "грязные" страницы все-равно должны быть заблокированы на запись?
Нет, незачем что-то блокировать.
Грязные страницы скидываются на диск. Если после этого в неё произойдёт запись, то страница снова станет грязной и будет скинута на диск ещё раз когда-нибудь в будущем.
Здравствуйте, watchmaker, Вы писали: W>В первую очередь, зачем вообще тебе нужно соблюдать порядок записи? Почему бы не оставить эту задачу на усмотрение ОС?
Ну задачка очень простая. Файл содержит две вресии данных и заголовок, который определяет какая версия данных является актуальной. Версии данных по очереди периодически обновляются и указатель в заголовке переключается на новую актуальную версию. Соответсвенно если в первую очередь изменится указатель на версию данных, а во время записи данных произойдет ошибка, соответственно указатель будет указывать на версию с ошибочными данными, и определить эту ситуацию никак нельзя, CRC не вычислить — объем данных слишком большой. Соответсвенно всегда нужно: сначала гаранитрованно выполнить сохранение новой версии данных, затем переключить указатель в заголовке. В принципе указатель можно переключать и асинхронно.
Здравствуйте, valuea, Вы писали:
V>Ну задачка очень простая. Файл содержит две вресии данных и заголовок, который определяет какая версия данных является актуальной.
Ну то есть обычный shadow paging?
V>Версии данных по очереди периодически обновляются и указатель в заголовке переключается на новую актуальную версию. Соответсвенно если в первую очередь изменится указатель на версию данных, а во время записи данных произойдет ошибка, соответственно указатель будет указывать на версию с ошибочными данными, и определить эту ситуацию никак нельзя,
Погоди-ка. Так ты всё-таки хочешь чтобы программа переживала внезапные сбои вроде пропадания электроэнергии, kernel panic, выхода из строя разного оборудования, вот это вот всё?
Просто при нормальной работе системы никакие msync для решения твоей задачи не нужны. Достаточно просто сначала записать данные в новую область, поставить memory barrier (если архитектура требует), и сделать вторую запись по переключению версии. А ОС сама позаботится об том, чтобы на диске оказалось потом те же данные что и в памяти, и что бы другие процессы при чтении получали непротиворечивую информацию независимо от того, успела произойти запись на диск или нет.
То есть управление порядком записи на диск нужно лишь для повышения вероятности, что после сбоя ОС (вроде внезапной перезагрузки) там окажется консинстентная структура. Если с этим нужно бороться, то да, синхронная синхронизация будет нужна.
V> CRC не вычислить — объем данных слишком большой.
Тогда, отвечая на твой предыдущий вопрос, MAP_NOSYNC совсем не нужен. Во-первых, если данных много, то и сбрасывать их стоит начинать пораньше — даже если не вызывать явно синхронную синхронизацию, то хотя бы позволить ОС заняться этим в свободное время. Во-вторых, если данных совсем много, то MAP_NOSYNC будет их стараться накапливать в памяти, что может вытеснить, скажем, другие полезные страницы и просадить производительность (а комбинация FreeBSD+ftruncate+MAP_NOSYNC вообще способна подвесить систему на несколько часов при неудачном стечении обстоятельств).
W>Ну то есть обычный shadow paging?
Да, похоже. Буду знать как по научному называется.
W>Погоди-ка. Так ты всё-таки хочешь чтобы программа переживала внезапные сбои вроде пропадания электроэнергии, kernel panic, выхода из строя разного оборудования, вот это вот всё? W>То есть управление порядком записи на диск нужно лишь для повышения вероятности, что после сбоя ОС (вроде внезапной перезагрузки) там окажется консинстентная структура. Если с этим нужно бороться, то да, синхронная синхронизация будет нужна.
Именно, но при этом не хотелось-бы вынуждать вызвающую сторону дожидаться окончания записи данных на диск, тем более при каждом новом вызове запись произоводится поочередно в различные участки файла, которые между собой не пересекаются. При этом не очень хочется создавать отдельный поток (дабы не держать поток вызывающей стороны), который занимался бы такой последовательной синхронизацией, а как раз таки переложить эту задачу "на плечи" ОС, что то типа асинхронную записи, но с выполнением условия последовательности. Если без отдельного потока не получитя, насколько я понимаю нужно будет копать в сторону kqueue (да, таки исключительно под FreeBSD).
W>а комбинация FreeBSD+ftruncate+MAP_NOSYNC вообще способна подвесить систему на несколько часов при неудачном стечении обстоятельств.
Да, как раз мой случай, только сейчас обратил внимание на предупреждение man-а.
Здравствуйте, valuea, Вы писали:
W>>Погоди-ка. Так ты всё-таки хочешь чтобы программа переживала внезапные сбои вроде пропадания электроэнергии, kernel panic, выхода из строя разного оборудования, вот это вот всё? W>>То есть управление порядком записи на диск нужно лишь для повышения вероятности, что после сбоя ОС (вроде внезапной перезагрузки) там окажется консинстентная структура. Если с этим нужно бороться, то да, синхронная синхронизация будет нужна. V>Именно, но при этом не хотелось-бы вынуждать вызвающую сторону дожидаться окончания записи данных на диск, тем более при каждом новом вызове запись произоводится поочередно в различные участки файла, которые между собой не пересекаются. При этом не очень хочется создавать отдельный поток (дабы не держать поток вызывающей стороны), который занимался бы такой последовательной синхронизацией, а как раз таки переложить эту задачу "на плечи" ОС, что то типа асинхронную записи, но с выполнением условия последовательности. Если без отдельного потока не получитя, насколько я понимаю нужно будет копать в сторону kqueue (да, таки исключительно под FreeBSD).
Конкретно в случае FreeBSD msync вообще не приводит к записи на диск с любыми ключами.
Только fsync заставит ОС сбросить все страницы на диск и только с его помощью можно обеспечить последовательность записи в данном случае.
И даже munmap не гарантирует записи, кстати.
Утешением может служить то, что в случае FreeBSD fsync сбрасывает именно грязные страницы одного указанного файла, а не как в Win...
Собственно при сбросе страниц на диск будут записаны данные на момент выполнения этой операции, т.к. в VM нет версионности, а страницы виртуальной памяти вообще прибиты намертво к страницам физической.
На самом деле ситуация такая — если нужны гарантии на выполнение транзакции, надо ждать ее завершения. Если не ждать — нет гарантий.
Здравствуйте, VVB16, Вы писали: VVB>Конкретно в случае FreeBSD msync вообще не приводит к записи на диск с любыми ключами. VVB>Только fsync заставит ОС сбросить все страницы на диск и только с его помощью можно обеспечить последовательность записи в данном случае.
Я конечно могу ошибаться, но вот что вижу
boolean_t vm_object_sync(vm_object_t object, vm_ooffset_t offset, vm_size_t size, boolean_t syncio, boolean_t invalidate) {
...
if (syncio && !invalidate && offset == 0 &&
OFF_TO_IDX(size) == object->size) {
/*
* If syncing the whole mapping of the file,
* it is faster to schedule all the writes in
* async mode, also allowing the clustering,
* and then wait for i/o to complete.
*/
flags = 0;
fsync_after = TRUE;
} else {
flags = (syncio || invalidate) ? OBJPC_SYNC : 0;
flags |= invalidate ? (OBJPC_SYNC | OBJPC_INVAL) : 0;
fsync_after = FALSE;
}
VM_OBJECT_WLOCK(object);
res = vm_object_page_clean(object, offset, offset + size,
flags);
VM_OBJECT_WUNLOCK(object);
if (fsync_after)
error = VOP_FSYNC(vp, MNT_WAIT, curthread);
VOP_UNLOCK(vp, 0);
vn_finished_write(mp);
Где VOP_FSYNC как раз и есть
int VOP_FSYNC(struct vnode *vp, int waitfor, struct thread *td);
DESCRIPTION
This call flushes any dirty file system buffers for the file. It is used
to implement the sync(2) and fsync(2) system calls.
А также writes in async mode, and then wait for i/o to complete.
VVB>На самом деле ситуация такая — если нужны гарантии на выполнение транзакции, надо ждать ее завершения. Если не ждать — нет гарантий.
Ну я подразумеваю все-таки немного разные ситуации:
1. Гарантия консистентности данных
2. Гарантия актуальный ответа вызывающей системе о состоянии данных в результате выполнения транзакции
То есть вызывающая система получает ответ об успешном завершении транзакции, хотя изменения в рамках данной тарнзакции могут быть и не сохранены. Данная гарантия теряется в угоду производительности, но гарантия консистентности данных все-же сохраняется.
Здравствуйте, valuea, Вы писали:
V>Здравствуйте, VVB16, Вы писали: V>Я конечно могу ошибаться, но вот что вижу
...
V>А также writes in async mode, and then wait for i/o to complete.
О, интересно. А это не частный случай, когда все сбрасывается?
Я в таких случаях ориентируюсь на man, а там указано, что записываются в fs, на не на storage device.
А вот в fsync(2) явно написано то, что надо. Собственно я делал msync и следом fsync, для гарантии соотвествия букве манула.
VVB>>На самом деле ситуация такая — если нужны гарантии на выполнение транзакции, надо ждать ее завершения. Если не ждать — нет гарантий. V>Ну я подразумеваю все-таки немного разные ситуации: V>1. Гарантия консистентности данных V>2. Гарантия актуальный ответа вызывающей системе о состоянии данных в результате выполнения транзакции V>То есть вызывающая система получает ответ об успешном завершении транзакции, хотя изменения в рамках данной тарнзакции могут быть и не сохранены. Данная гарантия теряется в угоду производительности, но гарантия консистентности данных все-же сохраняется.
Консистентность в памяти будет, на диске — нет, пока fsync не вернулся.
Надо ж учитывать, что переупорядочить запись может много кто (контроллер, сам диск) и только правильно реализованный fsync даст гарантию, что на диске лежит то, что мы записали.
Можно делать так: заголовок обновлять периодически, в отдельном потоке, вместе со сбросом данных на диск. А клиентам возвращать состояние в памяти.
Точнее даже так — сбрасываем данные fsync'ом, потом только обновляем заголовок и опять fsync. Тогда заголовок содержит заведомо правильную информацию.
В свое время я с этими тонкостями разбирался глядя на решения в BerkeleyDB, как там тот же журнал сделан, например.
VVB>О, интересно. А это не частный случай, когда все сбрасывается? VVB>Я в таких случаях ориентируюсь на man, а там указано, что записываются в fs, на не на storage device. VVB>А вот в fsync(2) явно написано то, что надо.
Да, я к тому, что судя по исходника VM FreeBSD, в случае вызова msync для всего пространства, выполняется тот же самый fsync. И пердполагаю (лень ковыряться в vm_object_page_clean с flags=OBJPC_SYNC), что даже при вызове msync для отдельной страницы выполняется операция записи на диск, аналогичная вызову fsync.
VVB>Консистентность в памяти будет, на диске — нет, пока fsync не вернулся. VVB>Надо ж учитывать, что переупорядочить запись может много кто (контроллер, сам диск) и только правильно реализованный fsync даст гарантию, что на диске лежит то, что мы записали. VVB>Можно делать так: заголовок обновлять периодически, в отдельном потоке, вместе со сбросом данных на диск. А клиентам возвращать состояние в памяти.
Я собственно этого и добиваюсь, но предполагал что ОС все-таки сможет обеспечить нужную последовательность при асинхронной записи на диск, без отдельного потока.