Во-первых, не пишите эту часть кода на ассемблере, особенно. не сначала. Напишите функцию C для обработки этой части и вызовите ее из ассемблера. Если вам нужно настроить часть, которая запускается только тогда, когда пришло время сбросить еще 16 МБ, вы можете настроить ее вручную. Программирование на системном уровне сводится к проверке ошибок, возвращаемых системными вызовами (или функциями C stdio), и делать это на ассемблере было бы болезненно.
Очевидно, что вы можете написать что угодно на ассемблере, поскольку выполнение системных вызовов не является чем-то особенным по сравнению с C. MFENCE
вокруг замка.
Во всяком случае, я рассмотрел три варианта того, что именно вы хотите сделать с вашим буфером:
- Перезаписать тот же буфер на место (
mmap(2)
/msync(2)
)
- Добавить снимок буфера в файл (либо с идеей
write(2)
, либо с вероятно неработающей нулевой копией vmsplice(2)
+ splice(2)
).
- Запустить новый (обнуленный) буфер после записи старого.
mmap(2)
последовательных фрагментов выходного файла.
На месте перезаписывает
Если вы просто хотите каждый раз перезаписывать одну и ту же область диска, mmap(2)
файл и используйте его в качестве массива. (Периодически вызывайте msync(2)
, чтобы принудительно записать данные на диск.) Однако метод mmapped не гарантирует согласованного состояния файла. Записи могут быть сброшены на диск иначе, чем по запросу. IDK, если есть способ избежать этого с какой-либо гарантией (т. е. не просто выбирать таймеры сброса буфера и т. д., чтобы ваши страницы обычно не записывались, кроме как msync(2)
.)
Добавить снимки
Самый простой способ добавить буфер в файл — это просто вызвать write(2)
, когда вы хотите, чтобы он был записан. write(2)
делает все, что вам нужно. Если ваша программа является многопоточной, вам может потребоваться заблокировать данные перед системным вызовом, а затем снять блокировку. Я не уверен, как быстро вернется системный вызов записи. Он может вернуться только после того, как ядро скопирует ваши данные в кеш страницы.
Если вам просто нужен снимок, но все записи в буфер являются атомарными транзакциями (т. е. буфер всегда находится в согласованном состоянии, а не в парах значений, которые должны быть согласованы друг с другом), то вам не нужно брать блокировку перед вызовом write(2)
. В этом случае будет небольшое смещение (данные в конце буфера будут немного более поздними, чем данные в начале, при условии, что ядро копирует по порядку).
IDK, если write(2)
возвращается медленнее или быстрее при прямом вводе-выводе (нулевое копирование, обход кэша страниц). open(2)
ваш файл с O_DIRECT
, write(2)
нормально.
Где-то в процессе должна быть копия, если вы хотите записать снимок буфера, а затем продолжать его изменять. Или еще трюк с копированием при записи MMU:
Добавление моментальных снимков с нулевым копированием
Существует API для записи без копирования пользовательских страниц в файлы на диске. Linux vmsplice(2)
и splice(2)
в таком порядке позволит вам указать ядру отображать ваши страницы в кеш страниц. Я предполагаю, что без SPLICE_F_GIFT
они настраиваются как копирование при записи. (упс, на самом деле справочная страница говорит, что без SPLICE_F_GIFT
придется копировать следующие splice(2)
. Итак, IDK, если есть механизм для получения семантики копирования при записи.)
Предполагая, что существует способ получить семантику копирования при записи для ваших страниц, пока ядро не закончит запись их на диск и не сможет их освободить:
Для дальнейших операций записи может потребоваться, чтобы ядро копировало в память одну или две страницы до того, как данные попадут на диск, но не копируйте весь буфер целиком. Программные ошибки страниц и накладные расходы на манипуляции с таблицами страниц в любом случае могут не стоить того, если ваш шаблон доступа к данным не очень пространственно локализован в течение коротких периодов времени, пока запись не попадет на диск и не будут освобождены страницы для записи. (Я думаю, что API, который работает таким образом, не существует, потому что нет механизма для выпуска страниц сразу после их попадания на диск. Linux хочет забрать их и сохранить в кэше страниц.)
Я никогда не использовал vmsplice, поэтому могу ошибаться в некоторых деталях.
Если есть способ создать новое сопоставление копирования при записи для той же памяти, возможно, путем mmap
создания нового сопоставления временного файла (в файловой системе tmpfs, вероятно, /dev/shm
), это даст вам моментальные снимки, не удерживая блокировку в течение длинная. Затем вы можете просто передать моментальный снимок в write(2)
и отменить сопоставление как можно скорее, прежде чем произойдет слишком много ошибок копирования при записи страниц.
Новый буфер для каждого чанка
Если вы можете начать с обнуленного буфера после каждой записи, вы можете mmap(2)
последовательных фрагментов файла, чтобы генерируемые вами данные всегда были в нужном месте.
- (необязательно)
fallocate(2)
немного места в выходном файле, чтобы предотвратить фрагментацию, если ваш шаблон записи не является последовательным.
mmap(2)
вашего буфера до первых 16 МБ вашего выходного файла.
- работать нормально
- When you want to move on to the next 16MiB:
- take a lock to prevent other threads from using the buffer
munmap(2)
ваш буфер
mmap(2)
следующие 16 МиБ файла по тому же адресу, поэтому вам не нужно передавать новый адрес авторам. Эти страницы будут предварительно обнулены, как того требует POSIX (ядро не может открывать доступ к памяти).
- открыть замок
Возможно, mmap(buf, 16MiB, ... MAP_FIXED, fd, new_offset)
мог бы заменить пару munmap
/mmap
. MAP_FIXED
отбрасывает старые mmap
ings, которые перекрываются. Я предполагаю, что это не означает, что изменения в файле/разделяемой памяти отбрасываются, а скорее фактические изменения отображения, даже без munmap
.
person
Peter Cordes
schedule
18.07.2015
write(2)
системных вызовов для чего угодно. Копирование себя не будет быстрее, чем позволить ядру сделать это во времяwrite(2)
. На самом деле медленнее, поскольку ядру все еще приходится копировать ваши данные дляwrite(2)
, если только вы не используете ввод-вывод с нулевым копированием для копии. - person Peter Cordes   schedule 18.07.2015