управление памятью — параллельное программирование, стеки и кучи в C / Stack Overflow

Что ж, извините, если это похоже на повторение старых вопросов, я прошел через несколько вопросов о переполнении стека, Книге современных операционных систем от tanenbaum, и мне еще предстоит прояснить мои сомнения по этому поводу.

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

Прежде чем задавать свои вопросы, я опишу свои выводы на основе чтений о стеках / кучах

отвал

  • Содержит только все переменные экземпляра, динамически распределенные (new / malloc) и глобальные переменные
  • Больше не использует кучу структур данных, использует более сложные структуры
  • Доступ через ячейки памяти, отдельный процесс, ответственный за выделенную память
  • Дефрагментация и выделение памяти осуществляется ОС (если да или нет, пожалуйста, ответьте на мой вопрос о том, кто управляет средой кучи, ОС или средой выполнения)
  • Совместно используется всеми потоками в процессе, которые имеют доступ к своей ссылке

стек

  • Содержит только все локальные переменные. (Нажата при вызове функции)
  • Использует фактическую структуру данных стека для работы
  • Более быстрый доступ из-за смежной природы

Теперь несколько вопросов о том же.

  1. Глобальные переменные, где они распределяются? (Я считаю, что они выделяются в куче. Если так, когда они выделяются, во время выполнения или во время компиляции, и еще один вопрос, можно ли очистить эту память (как при использовании delete)?)
  2. Какова структура кучи? Как организована куча (управляется ли она ОС или средой выполнения (как установлено компилятором C / C ++)).
  3. Стек вмещает ТОЛЬКО метод, а их локальные переменные?
  4. Каждому приложению (процессу) назначается отдельная куча, но если вы превышаете выделение кучи, значит ли это, что ОС не смогла выделить больше памяти? (Я предполагаю, что нехватка памяти приводит к перераспределению ОС, чтобы избежать фрагментации)
  5. Куча доступна из всех потоков в процессе (я верю, что это правда). Если да, все потоки могут получить доступ к переменным экземпляра, динамически распределенным переменным, глобальным переменным (если они имеют ссылку на него)
  6. Разные процессы, не могут получить доступ к куче друг друга (даже если им передан адрес)
  7. Сбой переполнения стека
    • только текущая тема
    • текущий процесс
    • все процессы
  8. В C / C ++ выделяется ли память во время выполнения в стеке для блочных переменных внутри функции (например, если подблок (например, цикл For) кода создает новую переменную, которая выделяется во время выполнения на стек (или куча) или он предварительно выделен?) когда они удаляются (область действия уровня блока, как это поддерживается). Я считаю, что все добавления в стек производятся во время выполнения до начала блока, при достижении конца этого блока все элементы, добавленные до этой точки, перемещаются.
  9. Поддержка процессором стекового регистра ограничена указателем стека, который можно увеличивать (выдавать) и уменьшать (выдвигать) посредством обычного доступа к памяти. (Это правда?)
  10. Наконец, существуют ли структуры стека и кучи, генерируемые средой ОС / среды выполнения, которые существуют в основной памяти (как абстракция?)

Я знаю, что это много, и я, кажется, очень запутался, я был бы признателен, если бы вы указали мне правильное направление, чтобы разобраться в этих вещах!

4

Решение

  1. Глобальные переменные размещаются в статическом разделе памяти, который располагается во время компиляции. Значения инициализируются во время запуска до main введен Инициализация может, конечно, распределять в куче (то есть статически распределенной std::string будет иметь саму структуру в статически расположенной памяти, но строковые данные, которые она содержит, будут выделены в куче во время запуска). Эти вещи удаляются при нормальном завершении работы программы. Вы не можете освободить их раньше, если хотите, вы можете заключить значение в указатель и инициализировать указатель при запуске программы.

  2. Куча управляется библиотекой-распределителем. Есть одна, которая поставляется со средой выполнения C, но есть и такие, как tcmalloc или же jemalloc что вы можете использовать вместо стандартного распределителя. Эти распределители становятся большими страницы памяти из операционной системы с помощью системных вызовов, а затем дать вам части этих страниц при вызове malloc. Организация кучи несколько сложна и варьируется между распределителями, вы можете посмотреть, как они работают на своих сайтах.

  3. Да-иш. Хотя вы можете использовать библиотечные функции, такие как alloca чтобы освободить место в стеке и использовать его для чего угодно.

  4. Каждый процесс имеет отдельное пространство памяти, то есть он думает, что он один, и никакого другого процесса не существует. Как правило, операционная система даст вам больше памяти, если вы об этом попросите, но она также может применять ограничения (например, ulimit на Linux), когда он может отказаться дать вам больше памяти. Фрагментация не проблема для ОС, потому что она дает память в страницы. Однако фрагментация в вашем процессе может привести к тому, что ваш распределитель запросит больше страниц, даже если есть свободное место.

  5. Да.

  6. Да, однако, как правило, существуют специфические способы ОС для создания областей совместно используемой памяти, к которым могут обращаться несколько процессов.

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

  8. В C ++ объекты, размещенные в стеке, создаются при входе в блок и уничтожаются при выходе из блока. Хотя фактическое пространство в стеке может быть распределено менее точно, но строительство и разрушение будут происходить именно в этих точках.

  9. Указатель стека в процессах x86 может быть произвольно изменен. Обычно компиляторы генерируют код, который просто добавляет объем пространства к указателю стека, а затем устанавливает память для значений в стеке, вместо того чтобы выполнять кучу операций push.

  10. Стеки и куча процесса все живут в одном и том же пространстве памяти.

Обзор того, как организована память, может быть полезным:

  • У вас есть физическая память, которую видит ядро.
  • Ядро отображает страницы физической памяти на страницы виртуальной памяти, когда процесс запрашивает ее.
  • Процесс работает в своем собственном пространстве виртуальной памяти, не зная о других процессах в системе.
  • Когда процесс запускается, он помещает части исполняемого файла (код, глобальные переменные и т. Д.) В некоторые из этих страниц виртуальной памяти.
  • Распределитель запрашивает страницы у процесса, чтобы удовлетворить вызовы malloc, эта память составляет кучу.
  • когда поток запускается (или начальный поток для процесса), он запрашивает у ОС несколько страниц, образующих стек. (Вы также можете спросить своего распределителя кучи и использовать пространство, которое оно дает вам, как стек).
  • Когда программа запущена, она может свободно обращаться ко всей памяти в своем адресном пространстве, куче, стеке, что угодно.
  • Когда вы пытаетесь получить доступ к области вашей памяти, которая не отображается, ваша программа падает. (более конкретно, вы получаете сигнал от ОС, который вы можете выбрать для обработки).
  • Переполнение стека, как правило, приводит к тому, что ваши программы получают доступ к таким не отображенным областям, поэтому переполнение стека приводит к сбою вашей программы.
6

Другие решения

  1. Где распределены глобальные переменные, зависит от системы. Некоторые системы размещают их статически в двоичном коде, некоторые размещают их в куче, некоторые размещают их в стеке. Если глобальная переменная является указателем, вы можете delete значение, на которое он указывает, но иначе невозможно очистить эту память. Деструкторы для глобальных переменных будут вызываться автоматически при выходе из приложения (ну, может быть, не с SIGTERM)
  2. Я не уверен, но я предполагаю, что это управляется операционной системой, особенно ядром.
  3. Да и только до определенного момента. Например, вы не можете выполнять бесконечную рекурсию, потому что значения будут (без каламбура) складываться. Вы получите с собой, подождите, переполнение стека (А-а-а, вот и он!)
  4. Некоторые операционные системы могут устанавливать ограничение размера кучи через отдельный процесс, но, как правило, если вам не удается выделить память, это происходит потому, что памяти не осталось.
  5. Все потоки имеют общую кучу, и поэтому да, они могут обращаться к глобальным переменным, динамически размещаться и т. Д.
  6. В целом правильно, хотя на некоторых действительно голых архитектурах это может быть не так. По большей части ОС выполняет процесс в контексте виртуальной таблицы, поэтому значения указателей, которые вы используете, на самом деле указывают на адрес памяти, отличный от того, на который они бы показались.
  7. Текущий процесс, если под процессом вы подразумеваете процесс на уровне ОС.
  8. Я предполагаю, что это правильно, но я не знаю себя.
  9. Это из моей рубки.
  10. Да вроде. Как я упоминал ранее, большинство операционных систем используют vtables для отображения указателей процесса на основную память. Также рассмотрите возможность подкачки на диск (подкачки)
2

По вопросам рекламы [email protected]