FILE* для буфферизированного доступа. Дёргать write(2) на каждые несколько записанных байт крайне медленно. Ну и write(2) когда нужно произвести одну операцию записи на дескриптор
Первые, чтобы эта libc’шная кривожопая буферизация не мешалась под ногами, и чтобы не пользоваться API придуманным идиотами, например вспоминать какой режим для fopen что значит, и достаточно ли вызвать fflush перед закрытием записываемого файла.
зависит от задачи. выше уже написали про буферизацию. а иногда и mmap нужно применять, для большей эффективности. если же просто в небольшой файл писать, то достаточно fopen/fwrite/fseek и же с ними. это часть стандарта С. прочие реализации могут зависеть от платформы.
я обычно FILE. А в целом без разницы, потому что они во-первых конвертируются друг в друга, во-вторых, по-хорошему конечно лучше использовать mmap, если возможно. А еще лучше - dio через какой-нибудь io_uring, а еще лучше - купить специальное железо с поддержкой ZNS :))
Из этих двух - сисколлы с fd. Для некоторых частных случаев (печать логов и других append-only потоков данных в файл, или построчное чтение файла) - свои обёртки.
Дёргать write(2) на каждые несколько записанных байт крайне медленно.
write() гарантирует что данные попадут в page cache ярда. А попадут ли данные физически на диск – зависит от ядра и файловой системы. Даже вызов fsync() / fdatasync() не гарантируют, что данные физически окажутся на диске. К примеру, на ZFS данные попадут в ZIL. Фактически сброс на диск произойдёт позже, в рамках транзакционного commit’а.
Да, использование сисколов не гарантирует что буфферизации не будет и данные попадут на диск. Но зато гарантирует, переключение в ядро с соответствующим оверхедом. На мелких порциях данных это может понизить производительность в десятки раз и оверхед от аллокации буффера для stdio на фоне этого окажется незначительным. Ну и не стоит забывать, что stdio - стандартный для си, а вот open/read/write - только в рамках posix и на не-posix системах могут отсутствовать или вести себя иначе (в windows это скорее совместимость с dos, а не posix)
90% случаев с FILE *, читать, форматировано писать, перемещаться по файлу, всё что нужно, есть, это просто удобно и не накладно. Дескрипторы, редко, если они изначально явно нужны, или требуется в них «сконвертировать», настолько редко что каждый раз в справку лезу.
Юзаем open(), close(), write(), read() на базе int fd. Буфферизацию реализуем своими средствами, так как-то нативнее и кошернее и понятнее. Обычно слой, который хочет записывать в файл, отдаёт уже цепочки блоков по 4 КБ или длиннее, так что проблем нет. Буфферизация в серьёзном приложении всегда какая-то реализована, потому что иначе бы у нас получилась зависимость от библиотеки, которая даёт доступ к файлу. От библиотеки требуется минимум фич - никаких там буферов, а прямой сискол и все довольны. Условно, мы делаем что-то типа MySQL, а там чтение целыми страницами да и записи то же, но обычно более длинными кусками и в самой софтине есть «кеш страниц» и короче всё в страницах меряется и пишется, ничего меньше страницы читать ниоткуда смысла никогда нет. А чаще и десятками мегабайт. Нахрен все эти буферизованые fopen() не уссались нам. Так в целом как-то проще и понятнее жить - не зависишь от буферов в библиотеке, знаешь что их там просто нет. Ну и не факт, что политика «буферирования» внутри libc-шного fread() прям очень оптимальна для твоей приложеньки.
Про эту штуку я привык думать не более чем как про fence, ну типа барьер - никакие write() после fsync() не будут на диске раньше, чем те write() которые были до fsync()… Возможно это правильное понимание.
Про эту штуку я привык думать не более чем как про fence, ну типа барьер - никакие write() после fsync() не будут на диске раньше, чем те write() которые были до fsync()… Возможно это правильное понимание.
Я не думаю что они хоть где-то есть - слишком дорого. А тот кто пишет в те же блоки что другой процесс / поток в это время пытается засинкать - ну что ж, ССЗБ.
Ну это вообще минимально разумные гарантии. fsync() обычно по-сути ради них и вызывают (в мире движков СУБД, например). Если он прям на диск физически ничего не запишет, то хотя-бы эту гарантию даст, её хватит всем как 640 кб. Чтобы НЕ начинать запись чего-то, что требует наличия уже чего-то другого записанного на диск - ведь fsync() логически вызывают именно ради этого, не для чего-то ещё. Нет, ещё бывает что от fsync() хотят прям типа гарантии «записалось» в банках, чтобы если три разных сервера физически записали что-то, то можно клиенту отвечать что бабло перевелось - но там и железо специальное, где все мамой поклялись, что между fsync и железом есть взаимопонимание. Да и вообще банки строят свои IT-решения скорее вокруг термина «последовательно», а не термина «гарантированно записалось». Ну всмысле, сохранение порядка операций важнее, чем их сохранность. Реальное состояние бабла всё равно подбивают по ночам последовательным перепрочитыванием логов, а не прямо сразу в базе днём во время перевода денег.
А тот кто пишет в те же блоки что другой процесс / поток в это время пытается засинкать - ну что ж, ССЗБ.
Какие блоки? Блоки диска что-ли физические? Речь вообще не про это. Если в одни и те же блоки диска кто-то пытается писать «параллельно», то это уже дурка по определению, race condition называется и просто психи, тут fsync уже не поможет, только морг