Темы и процессы в Linux

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

Итак, мой вопрос заключается в том, что, столкнувшись с ситуацией, когда потоки и процессы могут обрабатываться довольно хорошо, следует ли использовать процессы или потоки? Например, если я писал веб-сервер, следует ли использовать процессы или потоки (или комбинацию)?

Ответ 1

Linux использует модель потоковой передачи 1-1, с (ядром) различие между процессами и потоками - все это просто выполняемая задача. *

В Linux системный вызов clone клонирует задачу с настраиваемым уровнем совместного доступа, среди которых:

  • CLONE_FILES: используйте одну и ту же таблицу дескриптора файла (вместо создания копии)
  • CLONE_PARENT: не устанавливайте отношения родитель-потомок между новой задачей и старым (иначе, дочерний getppid()= parent getpid())
  • CLONE_VM: используйте одно и то же пространство памяти (вместо создания COW)

fork() вызывает clone( наименьший общий доступ ) и pthread_create() вызывает clone( наиболее общий доступ ). **

fork затраты стоят чуть меньше, чем pthread_create из-за копирования таблиц и создания сопоставлений COW для памяти, но разработчики ядра Linux пытались (и добились успеха) минимизировать эти затраты.

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

Фактически, если вы используете многопроцессорную систему, общий доступ не может быть полезен для производительности: если каждая задача выполняется на другом процессоре, синхронизация общей памяти стоит дорого.


* Упрощенный. CLONE_THREAD приводит к совместному распределению сигналов (для которого требуется CLONE_SIGHAND, который разделяет таблицу обработчика сигналов).

** Упрощенный. Существуют как SYS_fork, так и SYS_clone syscalls, но в ядре SYS_fork и SYS_clone являются очень тонкими обертками вокруг одной и той же функции do_fork, которая сама по себе является тонкой оболочкой вокруг copy_process. Да, термины process, thread и task используются относительно взаимозаменяемо в ядре Linux...

Ответ 2

Linux (и действительно Unix) предоставляет вам третий вариант.

Вариант 1 - процессы

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

Вариант 2 - потоки

Создайте автономный исполняемый файл, который запускается с одним потоком и создает дополнительные потоки для выполнения некоторых задач.

Вариант 3 - вилка

Доступно только в Linux/Unix, это немного отличается. Разветвленный процесс - это собственный процесс с собственным адресным пространством - нет ничего, что ребенок может (обычно) воздействовать на адресное пространство своего родителя или родного брата (в отличие от потока), поэтому вы получаете дополнительную надежность.

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

Рассмотрим программу веб-сервера, состоящую из двух шагов:

  • Чтение данных конфигурации и времени выполнения
  • Подавать запросы на страницу

Если вы использовали потоки, шаг 1 будет выполнен один раз, а шаг 2 будет выполнен в нескольких потоках. Если вы использовали "традиционные" процессы, шаги 1 и 2 нужно будет повторять для каждого процесса, а память для хранения дубликатов данных конфигурации и времени выполнения. Если вы использовали fork(), вы можете сделать шаг 1 один раз, а затем fork(), оставив данные и конфигурацию в памяти, нетронутыми, не скопированными.

Итак, есть три варианта.

Ответ 3

Это зависит от множества факторов. Процессы более тяжелые, чем потоки, и имеют более высокую стоимость запуска и выключения. Межпроцессная связь (IPC) также сложнее и медленнее, чем межпротокольная связь.

И наоборот, процессы безопаснее и безопаснее, чем потоки, потому что каждый процесс работает в своем собственном виртуальном адресном пространстве. Если один из процессов выходит из строя или имеет переполнение буфера, он вообще не влияет на какой-либо другой процесс, тогда как при сбое потока он удаляет все остальные потоки процесса, и если поток имеет переполнение буфера, он открывается защитное отверстие во всех потоках.

Итак, если ваши прикладные модули могут работать в основном независимо друг от друга с небольшим количеством сообщений, вам, вероятно, следует использовать процессы, если вы можете позволить себе затраты на запуск и завершение работы. Снижение производительности IPC будет минимальным, и вы будете немного безопаснее от ошибок и дыр в безопасности. Если вам нужна каждая бит производительности, вы можете получить или иметь много общих данных (например, сложные структуры данных), переходите к потокам.

Ответ 4

Для некоторых программ, которые извлекают выгоду из concurrency, решение о том, следует ли использовать процессы или потоки, может быть затруднено. Ниже приведены некоторые рекомендации, которые помогут вам решить, какая модель concurrency лучше всего подходит вашей программе:

1 > Все потоки в программе должны запускать один и тот же исполняемый файл. С другой стороны, дочерний процесс может запускать другой исполняемый файл, вызывая функцию exec.

2 > Исправляемый поток может нанести вред другим потокам в том же процессе, потому что потоки используют одно и то же пространство виртуальной памяти и другие ресурсы. Например, запись дикой памяти через неинициализированный указатель в одном потоке может привести к повреждению памяти, видимой для другого потока.

3 > С другой стороны, ошибочный процесс не может этого сделать, потому что каждый процесс имеет копию пространства памяти программы.

4 > Копирование памяти для нового процесса добавляет дополнительные служебные накладные расходы по сравнению с созданием нового потока. Однако копия выполняется только при изменении памяти, поэтому штраф минимален, если дочерний процесс считывает только память.

5 > Нити должны использоваться для программ, требующих мелкозернистого parallelism. Например, если проблему можно разбить на несколько, почти идентичных задач, потоки могут быть хорошим выбором. Процессы должны использоваться для программ, которые нуждаются в более грубом parallelism.

6 > Обмен данными между потоками тривиален, потому что потоки разделяют одну и ту же память. (Тем не менее, следует проявлять большую осторожность, чтобы избежать условий гонки, как описано ранее.) Для обмена данными между процессами требуется использование механизмов МПК. Это может быть более громоздким, но делает несколько процессов менее подверженными ошибкам concurrency.

Ответ 5

Другие обсудили эти соображения.

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

Ответ 6

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

В современном Linux (2.6.x) нет никакой разницы в производительности между контекстным переключателем процесса по сравнению с потоком (только материал MMU является дополнительным для потока). Существует проблема с общим адресным пространством, что означает, что ошибочный указатель в потоке может испортить память родительского процесса или другого потока в одном и том же адресном пространстве.

Процесс защищен MMU, поэтому ошибочный указатель просто вызовет сигнал 11 и не будет поврежден.

Я бы вообще использовал процессы (не слишком много перераспределения контекста в Linux, но защита памяти из-за MMU), но pthreads, если мне понадобится класс планировщика в реальном времени, который представляет собой чашку чая все вместе.

Почему вы думаете, что потоки имеют такую ​​большую производительность в Linux? У вас есть данные для этого, или это просто миф?

Ответ 7

Насколько тесно связаны ваши задачи?

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

Ответ 8

Чтобы усложнить ситуацию, существует потоковое локальное хранилище и общая память Unix.

Потоковое локальное хранилище позволяет каждому потоку иметь отдельный экземпляр глобальных объектов. Единственный раз, когда я использовал его, - это создание среды эмуляции на linux/windows, для кода приложения, работающего в RTOS. В RTOS каждая задача была процессом с собственным адресным пространством, в среде эмуляции каждая задача была потоком (с общим адресным пространством). Используя TLS для таких вещей, как одиночные игры, мы могли иметь отдельный экземпляр для каждого потока, как в реальной среде RTOS.

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

Ответ 9

Я должен согласиться с тем, что вы слышали. Когда мы сравниваем наш кластер (xhpl и т.д.), Мы всегда получаем значительно лучшую производительность с процессами по потокам. </anecdote>

Ответ 10

Решение между потоком/процессом немного зависит от того, к чему вы его будете использовать. Одно из преимуществ процесса состоит в том, что он имеет PID и может быть убит без прерывания родительского.

Для примера реального веб-сервера apache 1.3 используется только для поддержки нескольких процессов, но в 2.0 они добавили абстракцию так что вы можете переключаться между ними. Комментарии кажется согласны с тем, что процессы более надежны, но потоки могут дать немного лучшую производительность (за исключением окон, в которых производительность процессов отстойна и вы хотите использовать потоки).

Ответ 11

В моей недавней работе с LINUX стоит только знать библиотеки. Если вы используете потоки, убедитесь, что любые библиотеки, которые вы можете использовать в потоках, являются потокобезопасными. Это сожгло меня пару раз. В частности, libxml2 не является потокобезопасным. Он может быть скомпилирован с помощью потоковой безопасности, но это не то, что вы получаете с установкой aptitude.

Ответ 12

В большинстве случаев я бы предпочел процессы над потоками. потоки могут быть полезны, когда у вас относительно небольшая задача (время процесса) → время, затраченное каждым разделенным блоком задач), и есть необходимость в совместном использовании памяти между ними. Подумайте о большом массиве. Также (offtopic), обратите внимание, что если использование вашего процессора на 100 процентов или близко к нему, не будет никакой пользы от многопоточности или обработки. (на самом деле он будет ухудшаться)

Ответ 13

Потоки → Потоки разделяют пространство памяти, это абстракция ЦП, она легкая. Процессы → Процессы имеют собственное пространство памяти, это абстракция компьютера. Чтобы распараллелить задачу, вам нужно абстрагировать процессор. Однако преимуществом использования процесса над потоком является безопасность, стабильность, в то время как поток использует меньше памяти, чем процесс, и обеспечивает меньшую задержку. Примером с точки зрения сети будет Chrome и Firefox. В случае Chrome каждая вкладка - это новый процесс, поэтому использование памяти в chrome выше, чем в Firefox, а обеспеченная безопасность и стабильность лучше, чем в Firefox. Безопасность, обеспечиваемая Chrome, лучше, так как каждая вкладка - это новый процесс, который не может быть отслежен в пространстве памяти данного процесса.

Ответ 14

Я думаю, что все проделали отличную работу, отвечая на ваш вопрос. Я просто добавляю больше информации о потоке против процесса в Linux, чтобы прояснить и обобщить некоторые предыдущие ответы в контексте ядра. Итак, мой ответ касается специфичного для ядра кода в Linux. Согласно документации ядра Linux, нет четкого различия между потоком и процессом, за исключением того, что поток использует общее виртуальное адресное пространство в отличие от процесса. Также обратите внимание, что ядро Linux использует термин "задача" для обозначения процесса и потока в целом.

"Нет внутренних структур, реализующих процессы или потоки, вместо этого есть структура task_struct, которая описывает абстрактный блок планирования, называемый задачей"

Также, согласно Линусу Торвальдсу, вы НЕ должны вообще думать о процессе по сравнению с потоком, и потому что он слишком ограничивает, и единственное отличие - это COE или контекст выполнения с точки зрения "ветки адресного пространства от родительского" или общего адресного пространства. На самом деле он использует пример веб - сервера, чтобы сделать его пункт здесь (что очень рекомендую чтение).

Полный кредит документации ядра Linux

Ответ 15

Если вам нужно обмениваться ресурсами, вам действительно нужно использовать потоки.

Также рассмотрим тот факт, что контекст переключается между потоками намного дешевле, чем контекстные переключатели между процессами.

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