Вот довольно краткий способ выполнения конкатенации:
>>> s = "hello world"
>>> ''.join(str(ord(c)) for c in s)
'10410110810811132119111114108100'
И своего рода забавная альтернатива:
>>> '%d'*len(s) % tuple(map(ord, s))
'10410110810811132119111114108100'
man 2 write
в моей системе прекрасно подсвечивает его:
Обратите внимание, что не все файловые системы соответствуют POSIX.
blockquote>Вот цитата из недавнее обсуждение в списке рассылки
ext4
:В настоящее время одновременные чтения / записи являются атомарными только по отдельным страницам, однако их нет в системном вызове , Это может привести к тому, что
blockquote> blockquote>read()
вернет данные, смешанные с несколькими различными записями, что, по-моему, не является хорошим подходом. Мы можем утверждать, что приложение, выполняющее это, нарушено, но на самом деле это то, что мы можем легко сделать на уровне файловой системы без существенных проблем с производительностью, поэтому мы можем быть последовательными. Кроме того, POSIX упоминает об этом, а файловая система XFS уже имеет эту функцию.Это явное указание на то, что
ext4
- для обозначения только одной современной файловой системы - t соответствуют POSIX.1-2008 в этом отношении.
Изменить: Обновлено Aug 2017 с последними изменениями в поведении ОС.
Во-первых, O_APPEND или эквивалентный FILE_APPEND_DATA в Windows означает, что приращения максимальной длины файла (длина файла ») являются атомарными под параллельными авторами. Это гарантируется POSIX, и Linux, FreeBSD, OS X и Windows реализуют его правильно. Samba также реализует его правильно, NFS до v5 не делает, поскольку ему не хватает возможности форматирования каналов для атомарного добавления. Поэтому, если вы откроете свой файл только с помощью append-only, одновременные записи не будут разрываться друг относительно друга на любой основной ОС , если не задействована NFS.
Это ничего не говорит о том, читает ли когда-либо увидит разрывную запись, и на этом POSIX говорит следующее об атомарности read () и write () для обычных файлов:
Все следующие функции должны быть атомарными относительно друг друга в эффектах, указанных в POSIX.1-2008, когда они работают с обычными файлами или символическими ссылками ... [many functions] ... read () ... write () ... Если два потока каждый вызывает один из эти функции, каждый вызов должен либо увидеть все указанные эффекты другого вызова, либо ни один из них. [Источник]
blockquote>и
Записи могут быть сериализованы относительно других чтений и записей. Если чтение () данных файла может быть доказано (каким-либо образом) после записи () данных, оно должно отражать эту запись (), даже если вызовы выполняются разными процессами. [Источник]
blockquote>, но наоборот:
В этом томе POSIX.1-2008 не указано поведение одновременной записи в файл из нескольких процессов. Приложения должны использовать некоторую форму контроля параллелизма. [Источник]
blockquote>Безопасная интерпретация всех трех этих требований предполагает, что все записи, перекрывающиеся в объеме в одном файле, должны быть сериализованы относительно друг друга и читать таким образом, что порванные записи никогда не появляются читателям.
Менее безопасным, но все же допускаемым толкованием может быть то, что чтение и запись только сериализации друг с другом между потоками внутри одного и того же процесса, а между процессами записи - сериализован только для чтения (т. е. последовательно выполняется согласование ввода / вывода между потоками в процессе, но между процессами i / o - это только получение-релиз).
Итак, как работают популярные ОС и файловые системы на этом? Как автор предлагаемого Boost.AFIO асинхронной файловой системы и библиотеки файлов i / o C ++, я решил написать эмпирический тестер. Следующие результаты для многих потоков в одном процессе.
Нет O_DIRECT / FILE_FLAG_NO_BUFFERING:
Microsoft Windows 10 с NTFS: update atomicity = 1 байт до 10.0 включительно. 10240, от 10.0.14393 не менее 1 Мб, возможно, бесконечно по спецификации POSIX.
Linux 4.2.6 с ext4: update atomicity = 1 байт
FreeBSD 10.2 с ZFS: обновление atomity = не менее 1 Мб, вероятно, бесконечно в соответствии с спецификацией POSIX.
O_DIRECT / FILE_FLAG_NO_BUFFERING:
Microsoft Windows 10 с NTFS: обновление атомарности = до 10.0.10240 до 1096 байты, только если выровнены по странице, иначе 512 байт, если FILE_FLAG_WRITE_THROUGH выключен, а еще 64 байта. Обратите внимание, что эта атомарность, вероятно, является отличительной чертой PCIe DMA, а не разработанной. С 10.0.14393, по крайней мере, 1 Мб, вероятно, бесконечно по спецификации POSIX.
Linux 4.2.6 с ext4: update atomicity = не менее 1 Мб, вероятно, бесконечно по спецификации POSIX. Обратите внимание, что ранее Linuxes с ext4 определенно не превышал 4096 байт, XFS, безусловно, использовала пользовательскую блокировку, но похоже, что последняя Linux окончательно устранила эту проблему в ext4.
FreeBSD 10.2 с ZFS: update atomicity = at менее 1 Мб, вероятно, бесконечно по спецификации POSIX.
Итак, во-первых, FreeBSD с ZFS и очень недавние Windows с NTFS соответствует POSIX. Очень недавний Linux с ext4 - это POSIX, соответствующий только O_DIRECT.
Вы можете увидеть исходные результаты эмпирического теста на странице https://github.com/ned14/afio/tree/master/programs/fs-probe . Обратите внимание, что мы тестируем разрывы смещений только на 512 байт-кратных, поэтому не могу сказать, будет ли частичное обновление 512-байтового сектора разрываться во время цикла чтения-изменения-записи.
Вы неправильно интерпретируете первую часть указанной вами спецификации:
Либо дескриптор файла, либо поток называются «дескриптором» в описании открытого файла, к которому он относится; описание открытого файла может иметь несколько дескрипторов. [...] Вся активность приложения, влияющая на смещение файла на первом дескрипторе, приостанавливается до тех пор, пока он снова не станет активным дескриптором файла. [...] Ручки не обязательно должны быть в одном и том же процессе для применения этих правил.
blockquote>Это не устанавливает никаких требований к реализации для обработки параллельного доступа. Вместо этого он ставит требования к приложению не делать параллельный доступ даже из разных процессов, если вы хотите четко определенный порядок вывода и побочных эффектов.
Единственная временная атомичность гарантируется для труб, когда размер записи подходит для
PIPE_BUF
.Кстати, даже если вызов
write
был атомарным для обычных файлов, за исключением случаев записи в каналы, которые вписываются вPIPE_BUF
,write
всегда может возвращаться с частичной записью (т.е. записав меньше запрошенного количества байтов). Эта меньшая, чем требуется, запись будет атомарной, но это не поможет ситуации в отношении атомарности всей операции (ваше приложение должно было бы повторно вызватьwrite
для завершения).
Некоторое неверное истолкование того, что означает стандартные требования, происходит от использования процессов против потоков, и что это означает для ситуации с «ручкой», о которой вы говорите. В частности, вы пропустили эту часть:
Ручки могут быть созданы или уничтожены явным действием пользователя, не затрагивая описание открытого файла. Некоторые из способов их создания включают fcntl (), dup (), fdopen (), fileno () и
blockquote>fork()
. Они могут быть уничтожены как минимум fclose (), close () и функциями exec. [...] Обратите внимание, что после fork () две ручки существуют там, где они существовали раньше.из раздела спецификации POSIX, приведенного выше. Ссылка на «create [handles using]
fork
» не уточняется далее в этом разделе, но спецификация дляfork()
добавляет немного деталей:У дочернего процесса должна быть своя копия дескрипторов файла родителя. Каждый из описателей файла ребенка должен ссылаться на одно и то же описание открытого файла с соответствующим файловым дескриптором родителя.
blockquote>Соответствующие биты здесь:
- у ребенка есть копии дескрипторов файла родителя
- копии ребенка относятся к той же «вещи», к которой родитель может получить доступ через указанный файл fds
- ors и файл дескриптор ионов - это , а не то же самое; в частности, дескриптор файла является дескриптором в указанном выше смысле.
Это то, что первая цитата относится к тому, когда он говорит: «
fork()
создает [. ..] обрабатывает "- они создаются как копии , и, следовательно, с этой точки отсоединены и больше не обновляются в режиме блокировки.В вашей примерной программе каждый дочерний процесс получает свою собственную копию, которая начинается в том же состоянии, но после акта копирования эти filedescriptors / handle стали независимыми экземплярами , и поэтому они пишут друг с другом. Это вполне приемлемо относительно стандарта, потому что
write()
только guarentees:В обычном файле или другом файле, который можно искать, фактическое письмо данных должно продолжаться из позиции в файле, указанной смещением файла, связанным с файлами. Перед успешным возвратом из write () смещение файла должно быть увеличено на количество фактически записанных байтов.
blockquote>Это означает, что, хотя все они начинают писать с одинаковым смещением (потому что fd копия была инициализирована как таковая), они могут, даже в случае успеха, писать разные суммы (нет стандарта по стандарту, что запрос на запись в
N
байтах будет писать точноN
, это может преуспеть для чего-либо0 <=
actual<= N
), и из-за того, что упорядочение неупорядоченных операций не выполняется, вся приведенная выше примерная программа имеет неуказанные результаты. Даже если записана общая запрашиваемая сумма, все вышеприведенное значение говорит о том, что смещение файла увеличивается - оно не говорит, что оно атомарно (только один раз) увеличивается, и не говорит, что фактическая запись данных будет происходить в атомном режиме.. Одна вещь гарантирована, хотя - вы никогда не увидите ничего в файле, который не был там до какой-либо из записей, или который не попал ни в одну из данных написанной любой из записей. Если вы это сделаете, это будет коррупция и ошибка в реализации файловой системы. То, что вы наблюдали выше, может быть так, что ... если окончательные результаты не могут быть объяснены переупорядочением частей записей.
Использование
O_APPEND
исправляет это, потому что использование что еще раз - см.write()
, делает:Если установлен флаг O_APPEND флагов состояния файла, смещение файла должно быть установлено до конца файл перед каждой записью и никакая промежуточная операция изменения файла не происходит между изменением смещения файла и операцией записи.
blockquote>, который является «до» / «без промежуточного» поведения сериализации, который вы ищете.
Использование потоков частично изменило бы поведение, потому что потоки при создании не получали копии filedescriptors / handle, но работать с фактическим (общим). Нити не будут (обязательно) начинать писать с одного и того же смещения. Но вариант для частичной записи-успеха по-прежнему будет означать, что вы можете видеть чередование, как вы могли бы не видеть. Тем не менее, он может быть полностью совместим со стандартами.
Мораль: не считайте, что стандарт POSIX / UNIX является по умолчанию . Спецификации преднамеренно ослаблены в общем случае и требуют вы, как программист , быть ясными относительно ваших намерений.