LINUX.ORG.RU

fork() и возможность шарить уже выделенные страницы памяти

 ,


0

1

Возможно ли после fork'а расшарить между процессами выделенные процессу ДО fork'а участки памяти?

С моей тчоки зрения, это было бы разумным дополнением к Copy On Write стратегии: помимо концепции «копирования при необходимости» должна быть стратегия «совместно используемых участков памяти».

Это бы в корне отличалось от IPC Shared Memory, поскольку последняя system-wide и должна выделяться специально обученными методами, а тут - мы просто имеем дело с куском памяти, совместно используемым мастер-процессом и его потомками.

Где-то как это реализовано, я говорю о том, что всегда было и самоочевидно или же «я опять хочу невозможного»?

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

Есть страницы памяти процесса

[4,5,6], которые замаплены на физические [16,17,18] Я хочу, чтобы у всех потомков процесса точно так же были страницы [4,5,6], замапленные на [16,17,18], но без CoW-логики. CoW-логика работает так: взводится бит в таблице виртуальных страниц поорждённого процесса, вызывающий исключение при первом обращении на запись к виртуальной странице, например 5-й. Процесс пытается записать в вирт.страницу 5, возникает исключение, обрабатываемое 0-м кольце защиты - процессу выделяется страница физической памяти, в эту страницу копируется вся виртуальная страница 5 (т.е. содержимое физической страницы 17) и в результате порождённый процесс, записывая в вирт.страницу , в действительноси уже пишет в физическую страницу... скажем, 23.

Так вот, если такой вариант выделения памяти порождённому процессу, когда никакой бит не взводится, вообще ничего не делается и просто родитель, потомок и все его братья-сёстры могут читать/писать одну и ту же физическую страницу 17?

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

Спасибо!

Вот это реально здорово:

MAP_ANONYMOUS The mapping is not backed by any file; its contents are initialized to zero. The fd and offset arguments are ignored; however, some implementations require fd to be -1 if MAP_ANONYMOUS (or MAP_ANON) is specified, and portable applications should ensure this. The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is only supported on Linux since kernel 2.4.

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

Сэр, Вы крайне невыдержаны и не вполне умны.

Вы не читали вопрос и полезли отвечать.

Да, теоретически для решения моей задачи подойдёт и mmap (я честно просто не знал раньше, что mmap'ом не только мерзофайлики мэпят).

Но нет, на данный конкретный вопрос этот ответ - неверный. Потому что внезапно память процесса - это, например, память, выделенная под статические переменные. Это ещё и память, выделенная malloc. А что, если я хочу расшарить между процессами память из сегмента данных? А почему,с какой стати, я не могу этого сделать?

Т.е. вопрос был о том, что вот есть процесс, у него есть память, которую он занял при загрузке, есть память, выделенная динамически, этот процесс выглядит и ведёт себя как самый обычный однопоточный, никуда не параллелящийся. Задача: форкнуть этот процесс так, чтобы часть страниц его памяти осталась нетронутой, без каких-либо коров (CoW) сверху. Вот Вы, сэр, задумывались о том, что хорошо бы получить такой механизм, безо всяких функций мапленья файлов, которые при определённом хитром сочетании параметров, вдруг перестают мапить файлы и начинают просто выделять шареную память (сама по себе идея бредовая ИМХО: одна функция «для всего»?).

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

Вот это было вам отвечено совсем недавно по тому же поводу

«Язык, активно использующий IPC Shared Memory без плясок с бубном?» - этот тот же повод, seriously?! Сэр, Вам я могу со всей ответственностью заявить, что вот Вы-то точно дурак!

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

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

А теперь разберем по частям. Первое. Мой ответ был не на вопрос в шапке темы, а на твой пост в самой теме:

Спасибо!
Вот это реально здорово

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

Второе.

Т.е. вопрос был о том, что вот есть процесс, у него есть память, которую он занял при загрузке, есть память, выделенная динамически, этот процесс выглядит и ведёт себя как самый обычный однопоточный, никуда не параллелящийся. Задача: форкнуть этот процесс так, чтобы часть страниц его памяти осталась нетронутой, без каких-либо коров (CoW) сверху. Вот Вы, сэр, задумывались о том, что хорошо бы получить такой механизм, безо всяких функций мапленья файлов, которые при определённом хитром сочетании параметров, вдруг перестают мапить файлы и начинают просто выделять шареную память (сама по себе идея бредовая ИМХО: одна функция «для всего»?).

Это ТВОЯ задача и ТЕБЕ за неё, судя по всему, платят деньги. Если бы такой механизм был бы крайне нужен, его бы изобрели.
Я бы смотрел бы на 2 варианта решения:
1) Если ты знаешь, что некий участок памяти после malloc() должен быть позже расшарен с потомком, почему бы не получить часть адресного пространтсва mmap-ом вместо malloc-а? Отредактируйте ВАШ код и сделайте его, блин, корректным.
2) man 2 clone

Ты же, как я понял, просишь что-то типа такого:


typedef struct {
    ...
} my_cool_storage;

int main() {
    void *ptr = malloc(sizeof(my_cool_storage));
    ...
    make_it_shared(ptr, sizeof(my_cool_storage));
    pid_t mypid = fork();
    ...

}

Но это херня.

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

Но это херня.

Твои заскорузлые догматичные мозги - херня. Я думаю о том, как должно быть - а значит, могу реализовать то, как должно быть. А ты просто строчишь код по шаблонам. Я - креативный класс, а ты - нет.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от reprimand

Но это херня.

Это не херня, потому что эта идея проста как пять копеек, а не реализована по той же причине, по которой уродлив вызов mmap и поистине ужасна файловая система UNIX, в которой после установки программы все её файлы поносом разносит во все стороны.

Никому не нужно. Все деньги получают итак. Зачем стремится что-то делать лучше, если итак «хорошо»? Зачем летать в космос, зачем развивать фундаментальную науку, зачем вообще задумываться о том, что не касается горизонта ограниченной видимости?

И да, я не предлагаю именно так, как в этом куске кода. Я предлагаю изначально разделять данные на shared и не shared, а при выделении памяти просто указывать дополнительным параметром, должна память выделяться в расшаренных страницах или в обычных.

А «Но это херня» - появилось исключительно из непонимания того, как в принципе устроена страничная память.

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

Я думаю о том ... значит, могу

Найс подрыв креакла))

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

Вендузный баттхёрт от отсутствия програм файлс

фундаментальную

Да ты просто самородок))

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

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

Ты несёшь пургу.

Память управляется страницами. Страницы на x86 обычно имеют размер 4096 байт. Из этого следует, что нельзя расшарить меньше 4096 байт.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

В ассемблере есть такое вполне традиционное понятие - сегмент данных. Собственно, хотя откровенно уродливый с т.з. семантики код на Си и «прячет» этот самый сегмент данных, но фактически он есть, и в нём размещаются статичные строки, массивы и проч символы, объявленные в компайл-тайме. Предположим, дескриптор сегмента данных загружается в регистр ds. Тогда почему бы дескриптор сегмента расшаренных данных не загрузить в gs, поместив сами данные на границу страницы памяти? Занимают они 100 байт - выделяется 1 страница, занимают они 5200 байт - выделяется 2 страницы. В чём принципиальная техническая трудность-то?

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

А я предлагал шарить меньше страницы? Где?

Потому что внезапно память процесса - это, например, память, выделенная под статические переменные. Это ещё и память, выделенная malloc.

Сложи два и два.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Так а в чём проблема шарить памяти больше, чем выделено?

Дано: размер исходно выделенной процессу shared-data-area равен, положим, 4-м страницам.

При размере страницы 4Кб (хотя он может быть и 4Мб, насколько я помню) - это 16Кб.

malloc с флагом «shared» выделяет процессу память из незанятой статическими переменными (которые, положим, занимают первые 2900 байт) области shared-data. Если количество памяти, запрошенное у malloc превышает размер области, добавляются страницы памяти оттуда, откуда они могут быть добавлены. Кстати, насколько я помню, одна страница не может быть заполнена данными двух разных процессов, поэтому в любом случае выделенная процессу память кратна размеру страницы - и никак иначе.

В чём проблема-то? Именно техническая проблема с точки зрения архитектуры процессора, а не проблема текущей реализации конкретно в Linux?

Профит по-моему вполне очевиден: не нужно ничего никуда изощрённо мэпить, используется самое обычное адресное простнаство процесса, на части которого не действует безальтернативный сейчас CoW.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от anonymous

Разве в Linux сейчас не используется вообще плоская адресация, при которой ds, fs, gs, es и всё прочее устанавливаются в начало адресного пространства процесса?

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

Если ты знаешь, что некий участок памяти после malloc() должен быть позже расшарен с потомком, почему бы не получить часть адресного пространтсва mmap-ом вместо malloc-а?

Исторически mmap не предназначался для выделения памяти, это получилось случайно, когда встал вопрос, что должна делать mmap для /dev/zero. Так и родился вначале malloc, который может реально освобождать память (vs (s)break) вызовом munmap как mmap(/dev/zero), а потом пришла следующая гениальная мысль, а нафига вообще файл, давайте новый ключик сделаем: MAP_ANONYMOUS.

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

fs и gs используются для thread local storage, fs в x86-64 sysv abi, gs в i386 sysv abi. По адресу fs:0/gs:0 хранится линейный адрес tls. А линейные адреса это уже адреса в том самом плоском адресном пространстве, которое доступно идентично через cs, ds и es.

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

Это у аллокатора флаги. Не будешь же их менять перед каждым вызовом malloc. Тем более, всё поломается, если захочется взять другой аллокатор.

i-rinat ★★★★★
()
Ответ на: комментарий от vodz

Исторически mmap не предназначался для выделения памяти

Исторически объявление переменных в Си вообще были после круглых скобок - прям паскальчиком пахло!

reprimand ★★★★★
()

clone(2) как раз даёт эффект который тебе нужен.

       CLONE_VM (since Linux 2.0)
              If CLONE_VM is set, the calling process and the child process
              run in the same memory space.  In particular, memory writes
              performed by the calling process or by the child process are
              also visible in the other process.  Moreover, any memory
              mapping or unmapping performed with mmap(2) or munmap(2) by
              the child or calling process also affects the other process.

              If CLONE_VM is not set, the child process runs in a separate
              copy of the memory space of the calling process at the time of
              clone().  Memory writes or file mappings/unmappings performed
              by one of the processes do not affect the other, as with
              fork(2).

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

clone(2) как раз даёт эффект который тебе нужен.

Увы, первый вариант делает ВСЁ расшаренным, применяется для создания ядерных тредов, второй - обычный fork. ТСу надо что-то среднее. Если б он ещё понимал толком что его не устраивает в COW и MAP_SHARED...

vodz ★★★★★
()
Ответ на: комментарий от i-rinat

Тем более, всё поломается, если захочется взять другой аллокатор.

Не, получится настолько узкоспециализированная хренотень, что сломать не получится, оно просто на другом аллокаторе скажет, что вот этого флага не знаю. :)

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

Это если флаги разных аллокаторов не пересекаются. Разве есть какой-то центральный реестр флагов?

i-rinat ★★★★★
()
Ответ на: комментарий от vodz

Если б он ещё понимал толком что его не устраивает в COW и MAP_SHARED...

Очень чётко понимаю: если можно максимально унифицировать однопоточный и многопоточный (я имею в виду не thread'ы, а именно task'и, просто «однопроцессный» - звучит откровенно уродливо) режим работы, нужно так и делать - это первое. Второе - описанный подход позволяет вполне ощутимо повысить производительность, поскольку он предполагает, что нужно не делать что-то дополнительное, как в случае с mmap, а наоборот - НЕ ДЕЛАТЬ что-то лишнее, а именно не ставить биты защиты страниц от записи и не вызывать никаких исключений при первой попытке записи. Кстати, сами по себе эти исключения - огромный косяк, на мой взгляд. Я очень не люблю ситуации с «непредсказуемыми магическими неожиданностями», когда одна запись в память занимает, условно, 3 микросекунды, а другая, в то же самое место - почему-то одну. С точки зрения программы эти записи равнозначны и должны занимать примерно одинаковое количество времени, не должно быть разницы в 3 раза из-за того, что где-то там запустился обработчик. Правда, я мыслю идеализированными категориями программ реального времени, в этом плане ядро Linux в принципе не очень подходит: описанная ситуация, например, на 3-4 порядка проще и безобиднее совершенно издевательской ситуации, когда программа пишет в страницу памяти, а эта страница на самом деле сброшена на диск, что отражено очередным чудесным битом в записи таблицы страниц - и тоже приводит к исключению, которое может обрабатываться хоть полсекунды, поскольку диск - абсолютно непредсказуемое древнее механическое г.мамонта, со всеми вытекающими.

DRVTiny ★★★★★
() автор топика
Последнее исправление: DRVTiny (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.