LINUX.ORG.RU

file_mapping and fread


0

1

Значит, есть у меня класс обертка над boost'ким file_mapping (memmty mapped file), и есть к ней тесты.

Одни из тестов проверяет следующую логику: 1. файл маппиться в средствами boost

file_mapping mmfile(filename.c_str(), read_write);
MMFile file(&mmfile, 1);
MMFile предоставляет удобный способ работы c mapped_region, в виде read, write, seek, tell (полные аналоги fread,....).

2. открываю этот же файл fopen как readonly

3. через MMFile.write я делаю запись, что грубо говоря сводиться к memcpy в текущий спамленный регион.

4. через fread и считываю эти данные. И вот тут происходят казусы типа считанные fread данные могут быть просто нулями (хотя fread возвращает что было считано столько сколько нужно), или что часть данных как нули и другая нормальная.

В начале это казалось просто чем то фантастическим, потому что у меня первая пара действия возвращает нормальные данные, вторая возвращает нули... но после 2-х часов упорного тестирования до меня начала доходить следующая мысль:

file_mapping отображает память ядра в юзерспейс, тем самым избегая одного этапа копирования. А вот fread как раз в начале копирует данные из ядра в юзерспейс а потом только отображает в пространство процесса. И что возможно данные после определенного memcpy не успевают с копироваться в userspace для fread.

Но тогда для меня странно что на винде например этот тест проходит, а на лине нет. И что например при следующий действиях именно такое поведение:

mmfile = file_mapping(file);
region_ = mapped_region( mmfile, read_write, 0, windowSize_ );
file = fopen(file, "r");

memcpy(data, dest, len);
fread(dest, 1, len); // тут у нас данные целиком

memcpy(data, dest, len);
// если тут вставим memcpy(dest, readBuf, len) то следующая fread уже считает реальные данные вместо нулей
fread(dest, 1, len); // тут у нас уже вместо данных нули

проверить это в тестировании явно не получается, потому что в одном случае данные не нулевые, в другой нулевые, в третьем их только половина. А разница между кейсами нету, просто меняем порядок и количество операций.

Из документации буста, и это явно не уловил возможно там этот «ненормальный - попытка работать с файлом одновременно как с MM и fopen» кейс просто не указан.



Последнее исправление: Cupper (всего исправлений: 1)

Кусок кода какой-то непонятный. Что за dest, data, и как они относятся к маппингу? Вообще вроде до msync никто не гарантирует, что содержимое файла изменится.

ratatosk
()

- Рассуждения о работы mmap бредовые.

- Делать обёртку над обёрткой смысла нет.

- Напиши, откуда ты и как вызываешь чтение/запись (разные потоки/процессы?)

- Судя по твоим вопросам и знаниям в голове — у тебя, вероятно, просто бага в коде/тестах.

- Если ты уверен, что всё сделанно правиьно — бегом вычленять минимальный бажный сценарий и выносить его на суд общественности

anonymous
()
Ответ на: комментарий от anonymous

Забыл спросить, а как ты работаешь с буффером/кэшем, что в FILE*?

anonymous
()
Ответ на: комментарий от anonymous

ну... на счет mmap у меня не совсем ясное представление. Да я на счет fopen я думаю о меня весь смутные представления на уровне системы.

Поэтому что бы еще сильней не вносить смуты, набросайте схемку перемещение данных от харда до адресного пространства процесса в userspace для mmap и «fopen». А я пока попытаюсь сделать пример который смогу показать, а то пока это только внутри проекта и просто так выбрать не получится.

Cupper
() автор топика
Ответ на: комментарий от Cupper
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <stdio.h>

using namespace boost::interprocess;
using namespace boost::interprocess::detail;

int main()
{
        FILE* fin = fopen("test", "w+b");
        fclose(fin);
        file_mapping mmfile("test", read_write);
        truncate_file(mmfile.get_mapping_handle().handle,  mapped_region::get_page_size());
        mapped_region region = mapped_region(mmfile, read_write, 0, mapped_region::get_page_size());

        fin = fopen("test", "rb");

        char data[] = {'a', 'x', 'e', '3'};
        const size_t len = 4;

        char buf[len];
        memcpy(region.get_address(), data, len);
        fread(buf, sizeof(char), len, fin);
        std::cout << "fread = " << buf << std::endl;

        memcpy(region.get_address() + len, data, len);
        //fseek(fin, len, SEEK_SET);
        std::cout << "ftell = " << ftell(fin) << std::endl;
        fread(buf, sizeof(char), len, fin);
        std::cout << "fread = " << buf << std::endl;

        memset(buf, 0, len);
        memcpy(buf, region.get_address() + len, len);
        std::cout << "memcpy = " << buf << std::endl;

        memset(buf, 0, len);
        fseek(fin, len, SEEK_SET);
        fread(buf, sizeof(char), len, fin);
        std::cout << "fread = " << buf << std::endl;

        fclose(fin);
}

output:

$ ./test_mm
fread = axe3
ftell = 4
fread =
memcpy = axe3
fread = axe3
Если расскомитеть закоменченный fseek то все fread выдадут нормальные данные. Т.е. это по сути объясняет мои слова, после того как сделали memcpy по адресу mapped_region у нас данные сразу попали в ядро (поправьте меня, ведь именно в этом суть mmap?). Но они не успели отобразиться в userspace с которым работает fopen (fread).

Cupper
() автор топика
Ответ на: комментарий от Cupper

Во-первых никто не гарантирует, что после memcpy данные будут куда-то записаны, соответственно и fread не обязан считывать измененные данные. Во-вторых fread может буферизовать чтение, то есть, читать сразу много, отдавать по чуть-чуть, что и происходит. При fseek он видимо сбрасывает буффер.

ratatosk
()
Ответ на: комментарий от ratatosk

Во-первых никто не гарантирует, что после memcpy данные будут куда-то записаны

Я правильно понимаю что когда мы открываем файл, часть его копируется из харда в kernalspace.

Далее при работе с mmap, мы пишем сразу в это кернеловское «отображение» файла. Когда мы через fread мы читаем из userspace в которое было скопировано то самое отображение файла из ядра.

Если вот такой вопрось: если мы открыли файл на запись, и его же на чтение... то после операции записи не факт что мы при следующем чтении увидем только что записанные данные ? Или же все хитрей, и когда мы два раза открываем файл оба дескриптора будут работать с одним и тем же куском памяти в которой отображено содержимое файла.

Cupper
() автор топика
Ответ на: комментарий от Cupper

Я правильно понимаю

В двух словах всего не описать, поэтому либо кури подробно все механизмы работы с памятью и ФС, либо просто внимательно читай маны по используемым интерфейсам. Пока не дёрнешь msync, изменения могут быть не видны или видны не полностью другим читателям, в том числе через другие дескрипторы и отображения в том же процессе.

const86 ★★★★★
()
Ответ на: комментарий от Cupper

По первой части - это все на самом деле догадки об имплементации. Наверное, в большинстве случаев все так и устроено, но лучше на это на опираться. По второму вопросу - хрен знает, врать не буду.

P.S. мне кажется, что в программе в целом что-то не так, если ты одновременно используешь для одного файла оба механизма доступа (fread и mmap). Какую задачу преследуешь?

ratatosk
()
Ответ на: комментарий от ratatosk

P.S. мне кажется, что в программе в целом что-то не так, если ты одновременно используешь для одного файла оба механизма доступа (fread и mmap). Какую задачу преследуешь?

это был тест, оказался неверный тест :)

Cupper
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.