Почему создание нового процесса в Windows обходится дороже, чем в Linux?


101

Я слышал, что создание нового процесса в системе Windows дороже, чем в Linux. Это правда? Может ли кто-нибудь объяснить технические причины, по которым это дороже, и представить какие-либо исторические причины дизайнерских решений, лежащих в основе этих причин?

Ответы:


68

mweerden: NT с первого дня разрабатывался для многопользовательского режима, так что это не совсем причина. Однако вы правы в том, что создание процесса играет менее важную роль в NT, чем в Unix, поскольку NT, в отличие от Unix, отдает предпочтение многопоточности над многопроцессорностью.

Роб, это правда, что при использовании COW форк относительно дешев, но на самом деле за форком в основном следует exec. И исполнительный директор также должен загрузить все изображения. Поэтому обсуждение производительности вилки - это только часть правды.

При обсуждении скорости создания процесса, вероятно, будет хорошей идеей различать NT и Windows / Win32. Что касается NT (т.е. самого ядра), я не думаю, что создание процесса (NtCreateProcess) и создание потока (NtCreateThread) происходит значительно медленнее, чем в среднем Unix. Может быть, происходит немного больше, но я не вижу здесь основной причины разницы в производительности.

Однако, если вы посмотрите на Win32, вы заметите, что он добавляет довольно много накладных расходов на создание процесса. Во-первых, он требует, чтобы CSRSS был уведомлен о создании процесса, который включает LPC. Для этого требуется как минимум загрузка ядра 32, и он должен выполнить ряд дополнительных операций по учету, которые необходимо выполнить, прежде чем процесс будет считаться полноценным процессом Win32. И давайте не будем забывать обо всех дополнительных накладных расходах, связанных с синтаксическим анализом манифестов, проверкой того, требуется ли для изображения прокладка совместимости, проверкой применения политик ограниченного использования программ, yada yada.

Тем не менее, я вижу общее замедление в сумме всех тех мелочей, которые необходимо сделать в дополнение к необработанному созданию процесса, виртуального пространства и начального потока. Но, как было сказано в начале, из-за предпочтения многопоточности над многозадачностью единственное программное обеспечение, на которое серьезно влияют эти дополнительные расходы, - это плохо портированное программное обеспечение Unix. Хотя эта ситуация меняется, когда такие программы, как Chrome и IE8, внезапно заново открывают для себя преимущества многопроцессорности и начинают часто запускать и отключать процессы ...


8
За Fork не всегда следует exec (), и люди заботятся только о fork (). Apache 1.3 использует fork () (без exec) в Linux и потоки в Windows, даже если во многих случаях процессы разветвляются до того, как они понадобятся, и хранятся в пуле.
Blaisorblade

5
Не забывая, конечно, о команде vfork, которая разработана для описанного вами сценария «просто вызовите exec».
Крис Хуанг-Ливер,

4
Другой вид программного обеспечения, на который это серьезно влияет, - это любой вид сценариев оболочки, который включает координацию нескольких процессов. Например, сценарий Bash внутри Cygwin сильно страдает от этого. Рассмотрим цикл оболочки, который порождает в конвейерах множество команд sed, awk и grep. Каждая команда порождает процесс, а каждый конвейер порождает подоболочку и новый процесс в этой подоболочке. Unix был разработан с учетом именно такого использования, поэтому быстрое создание процессов остается там нормой.
Дэн Молдинг,

5
-1. Утверждение о том, что программное обеспечение «плохо портируется», потому что оно не работает должным образом в плохо спроектированной операционной системе, полной проблем с совместимостью, которые замедляют создание процесса, - нелепо.
Miles Rout

6
@MilesRout цель переноса - модифицировать программное обеспечение для работы в новой целевой системе с учетом сильных и слабых сторон этой системы. Плохо переносимое программное обеспечение - это плохо переносимое программное обеспечение, независимо от препятствий, которые предоставляет операционная система.
Dizzyspiral

28

В Unix есть системный вызов fork, который «разбивает» текущий процесс на два и дает вам второй процесс, идентичный первому (по модулю возврата из вызова fork). Поскольку адресное пространство нового процесса уже запущено и работает, это должно быть дешевле, чем вызов CreateProcess в Windows и загрузка исполняемого образа, связанных библиотек DLL и т. Д.

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


22
Этот аргумент справедлив только тогда, когда вы действительно разветвляетесь. Если вы начинаете новый процесс, в Unix вам все равно придется выполнять fork и exec. И Windows, и Unix имеют копию при записи. Windows обязательно будет повторно использовать загруженный EXE, если вы запустите вторую копию приложения. Я не думаю, что ваше объяснение правильное, извините.
Джоэл Спольски

1
Подробнее о exec () и fork () vipinkrsahu.blogspot.com/search/label/system%20programming
webkul

Я добавил в свой ответ некоторые данные о производительности. stackoverflow.com/a/51396188/537980 Как видите, это быстрее.
ctrl-alt-delor

25

Добавим к тому, что сказал JP: большая часть накладных расходов относится к запуску процесса Win32.

Ядро Windows NT действительно поддерживает вилку COW. SFU (среда Microsoft UNIX для Windows) использует их. Однако Win32 не поддерживает вилку. Процессы SFU не являются процессами Win32. SFU ортогонален Win32: обе подсистемы среды построены на одном ядре.

В дополнение к внепроцессным вызовам LPC CSRSSв XP и более поздних версиях существует внепроцессный вызов подсистемы совместимости приложений для поиска программы в базе данных совместимости приложений. Этот шаг вызывает достаточно накладных расходов, поэтому Microsoft предоставляет параметр групповой политики для отключения механизма совместимости на WS2003 по соображениям производительности.

Библиотеки времени выполнения Win32 (kernel32.dll и т. Д.) Также выполняют множество операций чтения реестра и инициализации при запуске, что не относится к UNIX, SFU или собственным процессам.

Нативные процессы (без подсистемы окружения) создаются очень быстро. SFU делает намного меньше, чем Win32 для создания процессов, поэтому его процессы также создаются быстро.

ОБНОВЛЕНИЕ НА 2019 ГОД: добавлен LXSS: подсистема Windows для Linux

Заменой SFU для Windows 10 является подсистема среды LXSS. Это 100% режим ядра и не требует каких-либо IPC, которые по-прежнему используются в Win32. Системный вызов для этих процессов направлен непосредственно на lxss.sys / lxcore.sys, поэтому вызов fork () или другого процесса, создающий, стоит всего 1 системный вызов для создателя, всего. [Область данных, называемая экземпляром] отслеживает все процессы LX, потоки и состояние выполнения.

Процессы LXSS основаны на собственных процессах, а не на процессах Win32. Все специфические для Win32 вещи, такие как механизм совместимости, вообще не задействованы.


16

В дополнение к ответу Роба Уокера: в настоящее время у вас есть такие вещи, как собственная библиотека потоков POSIX - если хотите. Но долгое время единственным способом «делегировать» работу в мире unix было использование fork () (и это все еще предпочитается во многих, многих случаях). например, какой-то сервер сокетов

socket_accept ()
вилка ()
если (ребенок)
    handleRequest ()
еще
    goOnBeingParent ()
Поэтому реализация форка должна быть быстрой, и со временем было реализовано множество оптимизаций. Microsoft одобрила CreateThread или даже волокна вместо создания новых процессов и использования межпроцессного взаимодействия. Я считаю нечестным сравнивать CreateProcess с fork, поскольку они не взаимозаменяемы. Вероятно, более уместно сравнить fork / exec с CreateProcess.


2
По поводу вашего последнего замечания: fork () нельзя заменить на CreateProcess (), но можно также сказать, что Windows должна реализовать fork () тогда, потому что это дает больше гибкости.
Blaisorblade

Ах, глагол To Bee.
acib708

Но fork + exec в Linux быстрее, чем CreateThread в MS-Windows. А Linux может делать форк самостоятельно, чтобы быть еще быстрее. Как ни крути, MS медленнее.
ctrl-alt-delor

13

Я думаю, что ключом к этому вопросу является историческое использование обеих систем. Windows (и до этого DOS) изначально были однопользовательскими системами для персональных компьютеров. Таким образом, этим системам обычно не нужно постоянно создавать множество процессов; (очень) проще говоря, процесс создается только тогда, когда его запрашивает одинокий пользователь (а мы, люди, не работаем очень быстро, условно говоря).

Системы на основе Unix изначально были многопользовательскими системами и серверами. Особенно для последних нередки случаи, когда процессы (например, почтовые или http-демоны) отделяются от процессов для обработки определенных заданий (например, для обслуживания одного входящего соединения). Важным фактором в этом является дешевый forkметод (который, как упоминал Роб Уокер ( 47865 ), изначально использует ту же память для вновь созданного процесса), который очень полезен, поскольку новый процесс сразу получает всю необходимую информацию.

Ясно, что, по крайней мере, исторически потребность для систем на базе Unix в быстром создании процессов намного больше, чем для систем Windows. Я думаю, что это все еще так, потому что системы на основе Unix все еще очень ориентированы на процессы, в то время как Windows, из-за своей истории, вероятно, была более ориентирована на потоки (потоки полезны для создания отзывчивых приложений).

Отказ от ответственности: я ни в коем случае не эксперт в этом вопросе, так что простите меня, если я ошибся.


9

Кажется, есть много оправданий типа "так лучше".

Я думаю, людям было бы полезно прочитать "Showstopper"; книга о разработке Windows NT.

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

Если вы опуститесь и испачкаетесь, вы обнаружите, что проблема заключается в стратегии загрузки библиотеки.

В Unices (в общем) сегменты кода общих библиотек (DLL) фактически являются общими.

Windows NT загружает копию DLL для каждого процесса, так как она манипулирует сегментом кода библиотеки (и сегментом исполняемого кода) после загрузки. (Сообщает, где ваши данные?)

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

Итак, создание процесса NT на самом деле довольно дорого. И с другой стороны, это делает DLL не заметной экономией в памяти, а шансом для проблем с зависимостями между приложениями.

Иногда инженеру выгодно сделать шаг назад и сказать: «А теперь, если бы мы собирались спроектировать это по-настоящему отстойным, как бы это выглядело?»

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


3
Сегменты кода можно использовать повторно, пока DLL загружается по предпочтительному базовому адресу. Традиционно вы должны убедиться, что вы установили неконфликтующие базовые адреса для всех DLL, которые будут загружаться в ваши процессы, но это не работает с ASLR.
Майк Диммик,

Есть какой-нибудь инструмент для переустановки всех DLL, не так ли? Не уверен, что он делает с ASLR.
Zan Lynx

3
Совместное использование разделов кода также работает в системах с поддержкой ASLR.
Пасс Йоханнеса

@MikeDimmick, поэтому все, создающие DLL, должны сотрудничать, чтобы гарантировать отсутствие конфликтов, или вы исправляете их все на системном уровне перед загрузкой?
ctrl-alt-delor

9

Краткий ответ - «программные уровни и компоненты».

Архитектура Windows SW имеет несколько дополнительных уровней и компонентов, которых нет в Unix или которые упрощены и обрабатываются внутри ядра в Unix.

В Unix fork и exec - это прямые вызовы ядра.

В Windows API ядра не используется напрямую, есть win32 и некоторые другие компоненты поверх него, поэтому создание процесса должно проходить через дополнительные уровни, а затем новый процесс должен запускаться или подключаться к этим уровням и компонентам.

В течение довольно долгого времени исследователи и корпорации пытались разбить Unix примерно таким же способом, обычно основывая свои эксперименты на ядре Mach ; хорошо известный пример является OS X. . Однако каждый раз, когда они пытаются, это становится настолько медленным, что они, по крайней мере, частично объединяют части обратно в ядро ​​либо навсегда, либо для производственных поставок.


Слои не обязательно замедляют работу: я написал драйвер устройства с большим количеством слоев на C. Чистый код, грамотное программирование, легко читаемый. Это было быстрее (незначительно), чем версия, написанная на высокооптимизированном ассемблере, без слоев.
ctrl-alt-delor

Ирония в том, что NT - это огромное ядро ​​(а не микро-ядро)
ctrl-alt-delor

2

Поскольку в некоторых ответах, похоже, есть какое-то оправдание MS-Windows, например

  • «Ядро NT и Win32 - это не одно и то же. Если вы программируете под ядро ​​NT, тогда это не так уж плохо »- Верно, но если вы не пишете подсистему Posix, то кого это волнует. Вы будете писать в win32.
  • «Несправедливо сравнивать форк с ProcessCreate, поскольку они делают разные вещи, а в Windows нет форка» - Верно, поэтому я буду сравнивать подобное с подобным. Однако я также буду сравнивать fork, потому что у него много разных вариантов использования, таких как изоляция процесса (например, каждая вкладка веб-браузера запускается в другом процессе).

Теперь посмотрим на факты, в чем разница в производительности?

Данные взяты из http://www.bitsnbites.eu/benchmarking-os-primitives/ .
Поскольку предвзятость неизбежна, резюмируя, я сделал это в пользу MS-Windows
Hardware для большинства тестов i7 8 core 3.2GHz. Кроме Raspberry-Pi под управлением Gnu / Linux

Сравнение различных базовых операций в Gnu / Linux, Apple-Mac и Microsoft Windows (чем меньше, тем лучше)

Сравнение процесса создания MS-Windows и Linux

Примечания: В Linux forkэто быстрее, чем предпочтительный метод MS-Window CreateThread.

Числа для операций типа создания процесса (потому что значение для Linux трудно увидеть на диаграмме).

В порядке скорости, от самой быстрой к самой медленной (числа - время, маленькие - лучше).

  • Linux CreateThread 12
  • Mac CreateThread 15
  • Linux форк 19
  • Windows CreateThread 25
  • Linux CreateProcess (вилка + выполнение) 45
  • Вилка Mac 105
  • Mac CreateProcess (fork + exec) 453.
  • Raspberry-Pi CreateProcess (вилка + выполнение) 501
  • Windows CreateProcess 787
  • Windows CreateProcess с антивирусным сканером 2850
  • Windows Fork (моделирование с помощью CreateProcess + fixup) больше 2850

Цифры для других измерений

  • Создание файла.
    • Linux 13
    • Mac 113
    • Окна 225
    • Raspberry-Pi (с медленной SD-картой) 241
    • Windows с защитником и антивирусным сканером и т. Д. 12950
  • Выделение памяти
    • Linux 79
    • Окна 93
    • Mac 152

1

Все это плюс тот факт, что на машине Win, скорее всего, антивирусное программное обеспечение сработает во время CreateProcess ... Обычно это самое большое замедление.


1
Да, это самое большое, но не единственное существенное замедление.
ctrl-alt-delor 01

1

Также стоит отметить, что модель безопасности в Windows намного сложнее, чем в ОС на базе UNIX, что добавляет много накладных расходов при создании процесса. Еще одна причина, по которой многопоточность предпочтительнее многопроцессорной обработки в Windows.


1
Я ожидал, что более сложная модель безопасности будет более безопасной; но факты говорят об обратном.
Ли Райан

4
SELinux также является очень сложной моделью безопасности, и она не требует значительных накладных расходовfork()
Spudd86

6
@LieRyan, В дизайне ПО (по моему опыту) сложнее очень редко значит безопаснее.
Вудро Дуглас,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.