Колко безопасни са картографираните в паметта файлове за четене на входни файлове?

Картографирането на входен файл в паметта и след това директно анализиране на данни от картографираните страници на паметта може да бъде удобен и ефективен начин за четене на данни от файлове.

Въпреки това, тази практика също изглежда фундаментално опасна, освен ако не можете да гарантирате, че никой друг процес не записва в картографиран файл, тъй като дори данните в частните картографии само за четене могат да се променят, ако базовият файл се записва от друг процес. (POSIX напр. не уточнява "дали модификациите на основния обект, извършени след съпоставянето MAP_PRIVATE, са установените са видими чрез картографирането MAP_PRIVATE".)

Ако искате да направите кода си безопасен при наличието на външни промени в картографирания файл, ще трябва да имате достъп до картографираната памет само чрез непостоянни указатели и след това да бъдете изключително внимателни как четете и валидирате входа, което изглежда непрактично за много случаи на употреба.

Правилен ли е този анализ? Документацията за API за картографиране на паметта обикновено споменава този проблем само мимоходом, ако изобщо го споменава, така че се чудя дали не пропускам нещо.


person Stephan Tolksdorf    schedule 22.01.2014    source източник
comment
Възможно е друг процес да модифицира файл, докато го използвате. Картографирането на паметта е един от многото начини за използване на файл - не е по-опасно от всеки друг.   -  person Casey    schedule 22.01.2014
comment
Копирането на файлово съдържание с нормалните API за четене на файлове в буфер на паметта е винаги безопасно, дори ако файлът е едновременно модифициран. За разлика от това, ако получите достъп до картографиран в памет файл чрез нормални указатели и външен процес го модифицира едновременно, получавате недефинирано поведение, защото това е извън ограниченията на модела на паметта C(++). Като цяло, ако паметта, която вие или вашият компилатор очаквате да бъде постоянно, се променя под краката ви, ще се случат лоши неща. Въвеждането, което потвърдите в един момент, може да стане невалидно в следващия момент.   -  person Stephan Tolksdorf    schedule 22.01.2014
comment
Вие тълкувате погрешно целта на MAP_PRIVATE. Това не означава дайте ми лично копие, а означава, че извършените от мен модификации са лични за мен. Има същите проблеми с паралелността като всеки друг метод за достъп до файл   -  person Petesh    schedule 22.01.2014
comment
@Petesh Къде написах или намекнах, че MAP_PRIVATE означава дай ми лично копие? Всъщност цитирах частта от спецификацията, в която се посочва обратното. Би било хубаво обаче, ако имаше опция, която действително гарантира, че картографираните страници не се променят от други процеси, след като са били достъпни.   -  person Stephan Tolksdorf    schedule 22.01.2014
comment
@StephanTolksdorf изглежда е същността на вашия въпрос. И „опцията да се гарантира, че картографираните страници не се променят от други процеси, след като са били осъществени достъп до тях“ се постига чрез записване на страницата. mmap не е нищо друго освен удобство при четене/запис   -  person Petesh    schedule 22.01.2014
comment
В Windows обикновено отваряте файла изключително или с помощта на FILE_SHARE_READ, така че други процеси да не могат да променят файла, докато го използвате.   -  person Harry Johnston    schedule 23.01.2014


Отговори (2)


Това всъщност не е проблем.

Да, друг процес може да промени файла, докато го картографирате, и да, възможно е да видите модификациите. Дори е вероятно, тъй като почти всички операционни системи имат унифицирани системи за виртуална памет, така че освен ако не се изисква небуферирано писане, няма начин да се пише без да се мине през буферния кеш и няма начин без някой да държи картографиране виждам промяната.
Това дори не е нещо лошо. Всъщност би било по-притеснително, ако не можете да видите промените. Тъй като файлът квази става част от вашето адресно пространство, когато го картографирате, има смисъл да виждате промени във файла.

Ако използвате конвенционален I/O (като read), някой все още може да модифицира файла, докато го четете. Казано по друг начин, копирането на файлово съдържание в буфер на паметта невинаги е безопасно при наличие на модификации. Той е „безопасен“, доколкото read няма да се срине, но не гарантира, че вашите данни са последователни.
Освен ако не използвате readv, нямате никакви гаранции относно атомарността (и дори с readv нямате гаранция, че това, което имате в паметта, е съвместимо с това, което е на диска, или че не се променя между две извиквания на readv). Някой може да модифицира файла между две read операции или дори докато сте в средата му.
Това не е нещо, което не е официално гарантирано, но „вероятно все още работи“ – - напротив, напр. под Linux записите доказано не са атомарни. Дори не случайно.

Добрата новина:
Обикновено процесите не просто отварят произволен произволен файл и започват да пишат в него. Когато се случи подобно нещо, обикновено това е или добре познат файл, който принадлежи на процеса (напр. лог файл), или файл, който изрично сте указали на процеса да пише (напр. запис в текстов редактор), или процесът създава нов файл (напр. компилаторът създава обектен файл) или процесът просто добавя към съществуващ файл (напр. db журнали и, разбира се, лог файлове). Или процесът може атомарно да замени файл с друг (или да прекрати връзката му).

Във всеки случай целият плашещ проблем се свежда до „няма проблем“, защото или сте наясно какво ще се случи (така че отговорността е ваша), или работи безпроблемно, без да се намесва.

Ако наистина не харесвате възможността друг процес може евентуално да пише във вашия файл, докато сте го картографирали, можете просто да пропуснете FILE_SHARE_WRITE под Windows, когато създавате манипулатора на файла. POSIX го прави донякъде по-сложно, тъй като трябва да fcntl дескриптора за задължително заключване, което не е необходимо да се поддържа или да е 100% надеждно на всяка система (например под Linux).

person Damon    schedule 22.01.2014
comment
Не съм загрижен за атомарността на четенето или писането. Когато копирате съдържанието на файла в буфер на паметта с помощта на read, можете да потвърдите прочетения вход след това и след това да сте сигурни, че съдържанието в буфера остава валидно. Когато входен файл се картографира в паметта, той обикновено се третира като постоянен буфер след това и се осъществява достъп и се анализира директно чрез нормални указатели. Ако тази предполагаема постоянна памет бъде променена от външен процес, тя нарушава очакванията на компилатора и разработчиците, което може да доведе до недефинирано поведение. - person Stephan Tolksdorf; 22.01.2014
comment
Точно така, ако това наистина е проблем, все пак можете да направите memcpy към анонимно картографиране (или към разпределен блок). Разбира се, тогава губите едно от основните предимства на картографирането на паметта: данните вече не са достъпни магически и безплатно. Но не бих се занимавал с копиране. Работата е там, че обикновено знаете, че когато модифицирате файл (или програма от ваше име), това не се случва на случаен принцип върху произволни файлове. - person Damon; 22.01.2014
comment
@Damon Това е изходен файл. Хората ги отварят в неочаквани моменти и им писват обратно, когато най-малко го очаквате. - person James Kanze; 22.01.2014
comment
@JamesKanze: Откъде знаеш, че въпросът е за изходен файл? Пропуснах ли нещо? - person Damon; 22.01.2014
comment
@Damon Той говори за анализиране. Така че това е източник за неговата програма. (Но е вярно, че човек анализира много неща... Аз съм анализирал лог файлове преди.) - person James Kanze; 22.01.2014

На теория вероятно имате сериозни проблеми, ако някой промени файла, докато го четете. На практика: вие четете знаци и нищо друго: никакви указатели или нещо, което може да ви създаде проблеми. На практика... формално мисля, че все още е недефинирано поведение, но не мисля, че трябва да се тревожите за него. Освен ако модификациите не са много малки, ще получите много грешки на компилатора, но това е краят.

Единственият случай, който може да създаде проблеми, е ако файлът е съкратен. Не съм сигурен какво се случва тогава, когато четеш отвъд края.

И накрая: системата няма произволно да отвори и модифицира файла. Това е изходен файл; ще бъде някой идиот програмист, който ще го направи и той заслужава това, което получава. В никакъв случай вашето недефинирано поведение няма да повреди системата или файловете на други хора.

Имайте предвид също, че повечето редактори работят върху лично копие; когато записват обратно, те го правят, като преименуват оригинала и създават нов файл. Под Unix, след като отворите файла, за да го mmap, всичко, което се брои, е номерът на inode. И когато редакторът преименува или изтрие файла, вие все още запазвате своето копие. Модифицираният файл ще получи нов inode. Единственото нещо, за което трябва да се притеснявате, е ако някой отвори файла за актуализация и след това го модифицира. Не много програми правят това върху текстови файлове, с изключение на добавянето на допълнителни данни към края.

Така че, макар формално да има известен риск, не мисля, че трябва да се тревожите за това. (Ако наистина сте параноик, можете да изключите разрешението за писане, докато сте mmaped. И ако наистина има вражески агент, който да ви вземе, той може веднага да го включи отново.)

person James Kanze    schedule 22.01.2014
comment
Не мисля, че OP говори за четене на изходни файлове сами по себе си, просто всеки файл, който програмата може да прочете по време на изпълнение. Като такава, вашата точка за грешките на компилатора изглежда неуместна. - person Marc Claesen; 22.01.2014
comment
@MarcClaesen Той спомена парсинга. Грешките на компилатора може да са малко силни, но всеки път, когато анализирате текст, трябва да можете да обработвате грешките във входа по някакъв начин. - person James Kanze; 22.01.2014