В C++ мы привыкли к удобным абстракциям вроде std::thread и std::jthread, но как именно они маппятся на операционную систему?
Для планировщика Linux существует только одна базовая абстракция - task (структура task_struct).
Когда вы создаете новый поток, под капотом pthreads дергает системный вызов clone(). В отличие от привычного fork(), который полностью копирует состояние процесса, clone() позволяет битовыми флагами (CLONE_VM, CLONE_FILES и др.) жестко задать, какие ресурсы будут расшарены между родителем и потомком: адресное пространство, таблица файловых дескрипторов или обработчики сигналов.
Как ядро не путается в потоках и процессах:
• Каждый созданный поток получает свой уникальный PID (на уровне ядра это называется TID - Thread ID). Это позволяет планировщику (CFS) независимо распределять их по ядрам процессора.
• Все потоки одного приложения объединяются в Thread Group и делят общий TGID (Thread Group ID), который равен TID самого первого потока (main).
• Когда мы вызываем функцию getpid(), она на самом деле возвращает именно TGID. Поэтому, когда вы делаете kill -9 <PID>, ядро видит TGID и прибивает всю группу потоков разом, не оставляя зомби.
По ссылке ниже - отличный хардкорный разбор архитектуры потоков в Linux: от системных вызовов до разделяемых состояний. Полезно для понимания того, как наши высокоуровневые многопоточные программы реально исполняются железом и ОС.
🔗 Читать статью: https://telegra.ph/How-Linux-Threads-Actually-Work-clone-Thread-Groups-and-Shared-Memory-07-15
#cpp #programming
👉 @cpp_lib