Какие участки памяти создаются при создании потока в Linux/Unix-подобной системе?

Ответ

При создании нового потока (например, с помощью pthread_create() или std::thread) в рамках существующего процесса операционная система выделяет для него новый регион памяти под стек. Размер стека по умолчанию обычно составляет от 1 до 8 МБ (зависит от ОС и может настраиваться через атрибуты потока).

Ключевое разделение памяти в многопоточном процессе:

  • Разделяемые всеми потоками:
    • Сегмент кода (.text) — исполняемые инструкции программы.
    • Сегмент данных (глобальные и статические переменные).
    • Куча (heap) — память, выделяемая через malloc()/new.
  • Уникальные для каждого потока:
    • Стек (stack) — содержит локальные переменные, аргументы функций и информацию о вызовах (call stack).
    • Thread-Local Storage (TLS) — область для переменных, объявленных с thread_local в C++11.

Пример, демонстрирующий разные стеки:

#include <iostream>
#include <thread>

void thread_func() {
    int local_to_thread = 42; // Живёт в стеке нового потока
    std::cout << "Thread stack address: " << &local_to_thread << 'n';
}

int main() {
    int local_to_main = 10; // Живёт в стеке главного потока
    std::cout << "Main stack address:  " << &local_to_main << 'n';

    std::thread t(thread_func);
    t.join();
}
// Адреса &local_to_main и &local_to_thread будут значительно различаться.

Ответ 18+ 🔞

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

А теперь самое интересное — стек. Это типа личная комната у каждого потока. Когда ты создаёшь новый поток через pthread_create() или std::thread, операционка ему выделяет новую, отдельную комнату под его личные вещи. Размер этой комнаты по умолчанию — ну, от 1 до 8 мегабайт, смотря какая ОС и как ты её настроишь. В этой комнате он хранит свои локальные переменные, аргументы функций и всю свою внутреннюю кухню вызовов.

И главное — эти комнаты изолированы. Один поток не может просто так залезть в стек другого и посмотреть, какие у того там int'ы лежат в носках. Это, блядь, святое. А то представь, если бы все лезли друг к другу в стек — был бы пиздец, а не многопоточность.

Короче, расклад по памяти такой:

  • Что общее (всё как в коммуналке):
    • Код программы (сегмент .text) — инструкция, как жить, одна на всех.
    • Глобальные и статические переменные — общий холодильник.
    • Куча (heap) — общая кладовка, куда через malloc/new можно складывать что угодно. Вот тут уже можно и подраться, если синхронизации нет.
  • Что личное (у каждого своя берлога):
    • Стек (stack) — личная комната. Твои локальные переменные живут тут.
    • Thread-Local Storage (TLS) — это типа запертый ящик в твоей комнате, для переменных с thread_local. Совсем своё, приватное.

А вот живой пример, чтобы вообще мозг встал на место:

#include <iostream>
#include <thread>

void thread_func() {
    int local_to_thread = 42; // Эта переменная живёт в стеке НОВОГО потока
    std::cout << "Адрес стека нового потока: " << &local_to_thread << 'n';
}

int main() {
    int local_to_main = 10; // А эта — в стеке ГЛАВНОГО потока (main)
    std::cout << "Адрес стека main:         " << &local_to_main << 'n';

    std::thread t(thread_func);
    t.join();
}
// Адреса &local_to_main и &local_to_thread будут овердохуища разные.

Запустишь этот код — и увидишь, что адреса этих local переменных отличаются как небо и земля. Потому что они в совершенно разных регионах памяти, в своих собственных стеках. Один поток не дотронется до локальной переменной другого, даже если захочет. Вот и вся магия, ёпта. Никакой чертовщины, просто у каждого своя комната, и слава богу.