Одна из причин, по которой практически невозможно описать потоки и процессы неабстрактным образом, заключается в том, что они являются абстракциями.
Их конкретные реализации отличаются чрезвычайно .
Сравните, например, процесс Эрланга и процесс Windows: процесс Эрланга очень легкий, часто менее 400 байт. Вы можете без проблем запустить 10 миллионов процессов на не очень новом ноутбуке. Они запускаются очень быстро, они умирают очень быстро, и ожидается, что вы сможете использовать их для очень коротких задач. Каждый процесс Erlang имеет свой собственный сборщик мусора, связанный с ним. Процессы Эрланга никогда не могут совместно использовать память.
Процессы Windows очень тяжелые, иногда сотни мегабайт. Вы можете запустить пару тысяч из них на мощном сервере, если вам повезет. Они начинают и умирают довольно медленно. Процессы Windows являются единицами приложений, таких как IDE, текстовые редакторы или текстовые процессоры, поэтому обычно ожидается, что они будут работать довольно долго (по крайней мере, несколько минут). У них есть собственное адресное пространство, но нет сборщика мусора. Процессы Windows могут совместно использовать память, хотя по умолчанию они этого не делают.
Потоки похожи: нить Linux NPTL на x86 может быть размером до 4 Кбайт, и с некоторыми приемами вы можете запустить 800000+ на 32-битной машине x86. Машина, безусловно, будет использоваться с тысячами, может быть, десятками тысяч нитей. Поток .NET CLR имеет минимальный размер около 1 МБ, что означает, что только 4000 из них будут использовать все ваше адресное пространство на 32-разрядной машине. Таким образом, хотя 4000 NPTL Linux Threads обычно не являются проблемой, вы даже не можете запустить 4000 .NET CLR Threads, потому что до этого у вас не хватит памяти.
Процессы ОС и потоки ОС также очень по-разному реализованы в разных операционных системах. Основные два подхода: ядро знает только о процессах. Потоки реализуются библиотекой Userspace без какого-либо знания ядра вообще. В этом случае снова есть два подхода: 1: 1 (каждый поток отображается на один процесс ядра) или m: n (m потоков отображается на n процессов, где обычно m> n и часто n == #CPU). Это был ранний подход, принятый во многих операционных системах после изобретения потоков. Однако обычно он считается неэффективным и заменяется почти во всех системах вторым подходом: потоки реализованы (по крайней мере, частично) в ядре, так что ядро теперь знает о двух различных сущностях, потоках и процессах.
Одна операционная система, которая идет третьим путем, - это Linux. В Linux потоки не реализованы ни в пользовательском пространстве, ни в ядре. Вместо этого Ядро предоставляет абстракцию и Поток и Процесс (и даже пару других вещей), называемых Задачей. Задача - это запланированный объект ядра, который несет с собой набор флагов, определяющих, какие ресурсы он разделяет со своими братьями и сестрами, а какие являются частными.
В зависимости от того, как вы устанавливаете эти флаги, вы получаете либо поток (общий доступ ко всему), либо процесс (общий доступ ко всем системным ресурсам, таким как системные часы, пространство имен файловой системы, пространство имен сети, пространство имен идентификатора пользователя, процесс ID пространства имен, но не делят адресное пространство). Но вы также можете получить и другие довольно интересные вещи. Вы можете тривиально получить тюрьмы в стиле BSD (в основном те же флаги, что и у процесса, но не разделяйте файловую систему или сетевое пространство имен). Или вы можете получить то, что другие ОС называют Контейнером или Зоной виртуализации (например, тюрьмой, но не разделяйте пространства имен UID и PID и системные часы). Еще пару лет назад с помощью технологии KVM (виртуальная машина ядра) вы даже можете получить полноценную виртуальную машину (не делиться ничем, даже таблицами страниц процессора). [Крутая вещь в этом заключается в том, что вы можете повторно использовать тщательно настроенный зрелый планировщик задач в ядре для всех этих вещей. Виртуальная машина Xen часто критиковалась за низкую производительность планировщика. Разработчики KVM имеют гораздо лучший планировщик, чем Xen, и самое лучшее, что им даже не нужно было писать для него ни одной строчки кода!]
Итак, в Linux производительность потоков и процессов намного ниже, чем в Windows и многих других системах, потому что в Linux они на самом деле одно и то же. Это означает, что шаблоны использования очень разные: в Windows вы обычно выбираете между использованием потока и процесса в зависимости от их веса: могу ли я позволить себе процесс или использовать поток, даже если я на самом деле не хочу делиться государство? В Linux (и обычно в Unix в целом) вы решаете, основываясь на их семантике: я действительно хочу поделиться состоянием или нет?
Одной из причин , почему Процессы, как правило, легче в Unix, чем в Windows, является другое использование: в Unix, процессы являются основной единицей как параллелизма, так и функциональности. Если вы хотите использовать параллелизм, вы используете несколько процессов. Если ваше приложение можно разбить на несколько независимых частей, вы используете несколько процессов. Каждый процесс делает только одну вещь и только одну вещь. Даже простой однострочный сценарий оболочки часто включает в себя десятки или сотни процессов. Приложения обычно состоят из многих, часто недолговечных процессов.
В Windows потоки являются основными единицами параллелизма, а компоненты COM или объекты .NET являются основными единицами функциональности. Приложения обычно состоят из одного длительного процесса.
Опять же, они используются для самых разных целей и имеют очень разные цели дизайна. Дело не в том, что одно или другое лучше или хуже, просто они , поэтому отличаются тем, что общие характеристики можно описать только очень абстрактно.
Практически единственное, что вы можете сказать о потоках и процессах, это:
- Потоки принадлежат процессам
- Нити легче, чем процессы
- Потоки делят большинство состояний друг с другом
- Процессы имеют значительно меньшее состояние, чем потоки (в частности, они обычно не разделяют память, если специально не запрошено)