Совместное использование памяти кучи с помощью fork () - PullRequest
8 голосов
/ 01 апреля 2012

Я работаю над реализацией сервера баз данных в C, который будет обрабатывать запросы от нескольких клиентов. Для этого я использую fork () для обработки соединений для отдельных клиентов.

Сервер хранит данные в куче, которая состоит из корневого указателя на хеш-таблицы динамически размещаемых записей. Записи являются структурами, которые имеют указатели на различные типы данных. Мне бы хотелось, чтобы процессы могли обмениваться этими данными, чтобы при внесении изменений в кучу изменения были видны другим клиентам.

Я узнал, что fork () использует COW (Копировать при записи) , и я понимаю, что он скопирует память кучи (и стека) родительского процесса, когда дочерний процесс попытается изменить данные в памяти.

Я обнаружил, что могу использовать библиотеку shm для совместного использования памяти.

-Достаточно ли поделиться корневым указателем базы данных или мне нужно сделать всю выделенную память общей?

-Если ребенок выделяет память, сможет ли родитель / другие дети получить к ней доступ?

-Также, если дочерний объект выделяет память и впоследствии уничтожается, выделенная память все еще останется в куче?

Так, например, будет ли приведенный ниже код правильным способом использовать кучу памяти (в shared_string)? Если бы ребенок использовал аналогичный код (т. Е. Начиная с // start), могли бы другие дети читать / писать в него, пока ребенок работает и после его смерти?

key_t key;
int shmid;

key = ftok("/tmp",'R');
shmid = shmget(key, 1024, 0644 | IPC_CREAT);

//start
char * string;
string = malloc(sizeof(char) * 10);

strcpy(string, "a string");

char * shared_string;

shared_string = shmat(shmid, string, 0);

strcpy(shared_string, string);

Ответы [ 5 ]

3 голосов
/ 03 мая 2012

Извините за ответ через месяц, но я не думаю, что существующие ответы дали то, о чем просил ОП.

Я думаю, что вы в основном хотите сделать то, что делает Redis (и, вероятно, другие),Они описывают это в http://redis.io/topics/persistence (поиск "копирование при записи").

  • потоки побеждают цель
  • классическая общая память (shm, отображенная память)также побеждает цель

Основным преимуществом использования этого метода является предотвращение блокировки, что может быть затруднительно для правильного решения.

Насколько я понимаю, идея использованияCOW:

  • разветвление, когда вы хотите записать, а не заранее
  • дочерний (пере) записывает данные на диск, а затем немедленно выходит из
  • родитель продолжает выполнять свою работу и определяет (SIGCHLD), когда ребенок вышел.Если, выполняя свою работу, родитель заканчивает тем, что вносит изменения в хеш, ядро ​​выполнит копию для затронутых блоков (правильная терминология?).
    «Грязный флаг» используется для отслеживания, нужен ли новый форк длявыполнить новую запись.

На что следует обратить внимание:

  • Убедитесь, что только один выдающийся дочерний элемент
  • Безопасность транзакций: сначала запишите во временный файл, затем переместите его так, чтобы у вас всегда была полная копия, возможно, сохраняя предыдущее, если перемещение не атомарное.
  • проверьте, не возникнут ли проблемы с другими дублирующимися ресурсами (файловые дескрипторы, глобальные деструкторы)в c ++)

Возможно, вы также захотите получить gander по redis-коду

3 голосов
/ 01 апреля 2012

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

С учетом вышесказанного, единственный способ иметь память, которая распределяется между родителем и потомком после fork и где одинаковые указатели действительны в обоих случаях, - это mmap (или shmat, но это много fuglier) файл или анонимная карта с MAP_SHARED до fork. Вы не можете создать новую разделяемую память, подобную этой, после fork, потому что нет гарантии, что она будет отображена в одном и том же диапазоне адресов в обоих.

Только не используйте fork. Это не правильный инструмент для работы.

1 голос
/ 19 ноября 2014

Многие популярные HTTP-серверы используют fork () для использования преимуществ нескольких процессоров, Nginx является одним из них.

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

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

Мне нравится спать по ночам, не получая этих звонков в 2 часа ночи, зная, что я обращаюсь к Интернету, серверы с высокой пропускной способностью не будут зависать от меня, потому что я не смог увидеть один издесятки ловушек с многопоточностью в тот день.

Во многих случаях совместная память не вызывает проблем, например, если данные в общей памяти доступны только для чтения.Вам не нужно беспокоиться о замках и т. Д.

1 голос
/ 01 апреля 2012

Достаточно ли поделиться корневым указателем базы данных или мне нужно сделать всю выделенную память общей?

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

Как уже говорили другие, файлы SHM или mmap'd являются единственным способом разделения памяти между отдельными процессами.

0 голосов
/ 01 апреля 2012

Если вам необходимо fork, общая память кажется «единственным» выбором.

На самом деле, я думаю, что в вашей сцене нить более подходит.

Если вы не хотите быть многопоточным. Вот еще один вариант, вы можете использовать только однопроцессный и однопоточный режим, например redis

В этом режиме вам не нужно беспокоиться о чем-то вроде lock, а если вы хотите масштабировать, просто разработайте политику маршрутизации, как маршрут со значением хеш-функции key

...