Какие ресурсы распределяются между потоками?

264

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

Потоки разделяют одну и ту же память, процессы - нет. После ответа на это интервьюер злобно улыбнулся мне и задал мне следующие вопросы:

В. Знаете ли вы сегменты, в которых программа делится?

Мой ответ: да (думал, что это было легко) Стек, Данные, Код, Куча

Q. Итак, скажите мне: какими сегментами делятся потоки?

Я не мог ответить на это и в итоге сказал все из них.

Пожалуйста, кто-нибудь может представить правильные и впечатляющие ответы на разницу между процессом и потоком?

Xinus
источник
10
Потоки разделяют одно и то же виртуальное адресное пространство , а процесс - нет.
Бенуа
2
Возможный дубликат В чем разница между процессом и потоком
sashoalm

Ответы:

177

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

Грег Хьюгилл
источник
28
Интересно то, что хотя потоки имеют независимые стеки вызовов, память в других стеках по-прежнему доступна.
Картик Балагуру
1
да - мне интересно, допустимо ли доступ к памяти в других стеках между потоками? Пока вы уверены, что не пытаетесь сослаться на стек, который был освобожден, я не уверен, что вижу проблему с ним?
Bph
2
@bph: Возможно получить доступ к памяти стека другого потока, но в интересах хорошей практики разработки программного обеспечения, я бы не сказал, что это приемлемо .
Грег Хьюгилл
1
Доступ к стекам других потоков, особенно запись в них, путается с несколькими реализациями сборщика мусора. Это может быть оправдано как ошибка реализации GC, однако.
yyny
56

Из Википедии (я думаю, это было бы действительно хорошим ответом для интервьюера: P)

Потоки отличаются от традиционных процессов многозадачной операционной системы тем, что:

  • процессы обычно независимы, в то время как потоки существуют как подмножества процесса
  • процессы несут значительную информацию о состоянии, тогда как несколько потоков в состоянии процесса совместно используют, а также память и другие ресурсы
  • процессы имеют отдельные адресные пространства, тогда как потоки разделяют их адресное пространство
  • процессы взаимодействуют только через предоставляемые системой механизмы межпроцессного взаимодействия.
  • Переключение контекста между потоками в одном и том же процессе обычно происходит быстрее, чем переключение контекста между процессами.
Хорхе Кордова
источник
2
о пункте № 2 выше: для потоков также CPU поддерживает контекст.
Джек,
49

Что-то, на что действительно нужно обратить внимание, состоит в том, что в этом вопросе действительно есть два аспекта - теоретический аспект и аспект реализации.

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

У нас есть следующее из раздела 2.2.2 Модель классической нити в современных операционных системах 3e от Tanenbaum:

Модель процесса основана на двух независимых концепциях: группировка ресурсов и выполнение. Иногда полезно разделить их; это где темы приходят ....

Он продолжает:

Один из способов взглянуть на процесс состоит в том, что это способ объединить связанные ресурсы. Процесс имеет адресное пространство, содержащее текст и данные программы, а также другие ресурсы. Эти ресурсы могут включать открытые файлы, дочерние процессы, ожидающие тревоги, обработчики сигналов, учетную информацию и многое другое. Объединяя их в форме процесса, ими можно легче управлять. Другая концепция, которую имеет процесс, - это поток выполнения, обычно сокращаемый до простого потока. Поток имеет программный счетчик, который отслеживает, какую инструкцию выполнять дальше. Он имеет регистры, которые содержат текущие рабочие переменные. Он имеет стек, который содержит историю выполнения, с одним кадром для каждой вызванной процедуры, но еще не возвращенной из. Хотя поток должен выполняться в каком-то процессе, нить и ее процесс являются разными понятиями и могут рассматриваться отдельно. Процессы используются для группировки ресурсов; потоки - это объекты, запланированные для выполнения на процессоре.

Далее он предоставляет следующую таблицу:

Per process items             | Per thread items
------------------------------|-----------------
Address space                 | Program counter
Global variables              | Registers
Open files                    | Stack
Child processes               | State
Pending alarms                |
Signals and signal handlers   |
Accounting information        |

Это то, что вам нужно для работы потоков. Как уже отмечали другие, такие вещи, как сегменты, являются деталями реализации, зависящими от ОС.

Роберт С. Барнс
источник
2
Это отличное объяснение. Но, вероятно, его следует как-то привязать к вопросу, чтобы считать его «Ответом»
катализатор294
Что касается таблицы, разве счетчик программ не является регистром? а "состояние" потока, зафиксированное в значении регистров? Мне не хватает также указателя на код, который они запускают (указатель на текст процесса)
onlycparra
29

Скажите интервьюеру, что это полностью зависит от реализации ОС.

Возьмите Windows x86 для примера. Есть только 2 сегмента [1], Код и Данные. И они оба сопоставлены с целым 2 ГБ (линейным, пользовательским) адресным пространством. База = 0, предел = 2 ГБ. Они бы сделали один, но x86 не позволяет сегменту быть как для чтения / записи, так и для выполнения. Таким образом, они сделали два и установили CS для указания на дескриптор кода, а остальные (DS, ES, SS и т. Д.) Для указания на другой [2]. Но оба указывают на то же самое!

Человек, опрашивающий вас, сделал скрытое предположение, что он или она не заявили, и это глупая уловка, которую нужно вытащить.

Так что относительно

Q. Так скажите мне, какой сегмент разделяет поток?

Сегменты не имеют отношения к вопросу, по крайней мере, на Windows. Потоки разделяют все адресное пространство. Существует только 1 сегмент стека, SS, и он указывает на те же вещи, что и DS, ES и CS [2]. Т.е. все кровавое пространство пользователя . 0-2GB. Конечно, это не означает, что потоки имеют только 1 стек. Естественно, у каждого свой стек, но сегменты x86 для этой цели не используются.

Возможно * nix делает что-то другое. Кто знает. Предпосылка, на которой был основан вопрос, была нарушена.


  1. По крайней мере, для пользовательского пространства.
  2. От ntsd notepad:cs=001b ss=0023 ds=0023 es=0023
Алекс Будовски
источник
1
Да ... Сегменты зависят от ОС и компилятора / компоновщика. Иногда есть отдельный сегмент BSS от сегмента DATA. Иногда есть RODATA (данные, подобные константным строкам, которые могут быть на страницах, помеченных только для чтения) Некоторые системы даже разбивают DATA на МАЛЫЕ ДАННЫЕ (доступны из базового + 16-битного смещения) и (FAR) ДАННЫЕ (32-битное смещение требуется для доступа). Также возможно, что существует дополнительный сегмент TLS DATA (Thread Local Store), который генерируется для каждого потока
Adisak
5
Ах нет! Вы путаете сегменты с разделами! Разделы - это то, как компоновщик делит модуль на части (данные, rdata, текст, bss и т. Д.), Как вы описали. Но я говорю о сегментах, как указано в оборудовании intel / amd x86. Не имеет никакого отношения к компиляторам / компоновщикам. Надеюсь, что это имеет смысл.
Алекс Будовский
Тем не менее, Adisak прав насчет магазина Local Local. Это приватно для потока и не передается. Я в курсе ОС Windows и не уверен в других ОС.
Джек,
20

Обычно потоки называются легковесным процессом. Если мы разделим память на три части, то это будет: код, данные и стек. Каждый процесс имеет свой собственный раздел кода, данных и стека, и из-за этого время переключения контекста немного велико. Чтобы сократить время переключения контекста, люди придумали концепцию потока, который разделяет сегмент данных и кода с другим потоком / процессом и имеет собственный сегмент STACK.

Нимиш Тхаккар
источник
Ты забыл кучу. Куча, если я не ошибаюсь, должна быть разделена между потоками
Phate
20

Процесс имеет код, данные, кучу и сегменты стека. Теперь указатель инструкций (IP) потока ИЛИ потока указывает на сегмент кода процесса. Сегменты данных и кучи совместно используются всеми потоками. А как насчет области стека? Что на самом деле площадь стека? Это область, созданная процессом только для использования его потока ... потому что стеки могут использоваться намного быстрее, чем кучи и т. Д. Область стека процесса делится между потоками, то есть, если есть 3 потока, то Область стека процесса делится на 3 части, и каждая отводится 3 потокам. Другими словами, когда мы говорим, что каждый поток имеет свой собственный стек, этот стек фактически является частью области стека процесса, выделенной каждому потоку. Когда поток завершает свое выполнение, стек потока освобождается процессом. По факту, не только стек процесса разделен между потоками, но и все наборы регистров, которые использует поток, такие как SP, ПК и регистры состояния, являются регистрами процесса. Таким образом, когда дело доходит до совместного использования, код, данные и области кучи являются общими, а область стека просто делится между потоками.

Дирендра Викаш Шарма
источник
13

Потоки разделяют код и сегменты данных и кучу, но они не разделяют стек.

Кевин Петерсон
источник
11
Существует разница между «возможностью доступа к данным в стеке» и совместным использованием стека. Эти потоки имеют свои собственные стеки, которые выталкиваются и выталкиваются при вызове методов.
Кевин Петерсон
2
Они оба одинаково правильные взгляды. Да, каждый поток имеет свой собственный стек в том смысле, что существует взаимно-однозначное соответствие между потоками и стеками, и каждый поток имеет пространство, которое он использует для своего собственного обычного использования стека. Но они также являются полностью общими ресурсами процесса, и при желании любой поток может получить доступ к стеку любого другого потока так же легко, как и свой собственный.
Дэвид Шварц
@DavidSchwartz, могу ли я обобщить вашу точку зрения, как показано ниже: Каждый поток имеет свой собственный стек, и этот стек состоит из 2 частей: первая часть разделяется между потоками до многопоточного процесса, а вторая часть заполняется, когда собственный поток работает .. Согласитесь?
FaceBro
2
@nextTide Нет двух частей. Стеки являются общими, точка. Каждый поток имеет свой собственный стек, но они также являются общими. Возможно, хорошая аналогия - если у вас и у вашей жены есть машина, но вы можете использовать машины друг друга в любое время.
Дэвид Шварц
5

Потоки обмениваются данными и кодом, а процессы - нет. Стек не является общим для обоих.

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

           Процесс поток

   Стек частный частный
   Данные приватные
   Код приват 1   поделился 2

1 Код является логически закрытым, но может использоваться совместно для повышения производительности. 2 Я не уверен на 100%.

Даниэль Брюкнер
источник
Я бы сказал, что сегмент кода (текстовый сегмент), в отличие от данных, почти всегда доступен только для чтения на большинстве архитектур.
Хорхе Кордова
4

Темы делятся всем [1]. Есть одно адресное пространство для всего процесса.

Каждый поток имеет свой собственный стек и регистры, но стеки всех потоков видны в общем адресном пространстве.

Если один поток выделяет некоторый объект в своем стеке и отправляет адрес другому потоку, они оба будут иметь равный доступ к этому объекту.


На самом деле, я только что заметил более широкую проблему: я думаю, что вы путаете два использования слова « сегмент» .

Формат файла для исполняемого файла (например, ELF) содержит отдельные разделы, которые могут называться сегментами, содержащими скомпилированный код (текст), инициализированные данные, символы компоновщика, отладочную информацию и т. Д. Сегменты кучи или стека отсутствуют. здесь, так как это только конструкции времени выполнения.

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

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


[1] Хорошо, я знаю: маски сигналов, TSS / TSD и т. Д. Адресное пространство, включая все отображаемые сегменты программы, все еще используется совместно.

Бесполезный
источник
3

В платформе x86 можно разделить столько сегментов (до 2 ^ 16-1). Директивы ASM SEGMENT / ENDS позволяют это, а операторы SEG и OFFSET позволяют инициализировать регистры сегментов. CS: IP обычно инициализируется загрузчиком, но для DS, ES, SS приложение отвечает за инициализацию. Во многих средах допускаются так называемые «упрощенные определения сегментов», такие как .code, .data, .bss, .stack и т. Д., И, в зависимости от «модели памяти» (маленькая, большая, компактная и т. Д.), Загрузчик инициализирует регистры сегментов. соответственно. Обычно .data, .bss, .stack и другие обычные сегменты (я не делал этого уже 20 лет, поэтому не помню всех) сгруппированы в одну группу, поэтому обычно DS, ES и SS указывают на это. та же область, но это только для упрощения вещей.

Как правило, все регистры сегментов могут иметь разные значения во время выполнения. Итак, вопрос интервью был правильным: какой из CODE, DATA и STACK распределяется между потоками. Управление кучей - это нечто другое - это просто последовательность обращений к ОС. Но что, если у вас вообще нет операционной системы, как во встроенной системе - можете ли вы добавить / удалить в своем коде?

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

Джордж
источник
2

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

  • идентификатор процесса и идентификатор родительского процесса;
  • идентификатор группы процессов и идентификатор сеанса;
  • управляющий терминал;
  • учетные данные процесса (идентификаторы пользователей и групп);
  • дескрипторы открытых файлов;
  • блокировки записей, созданные с использованием fcntl();
  • расположение сигналов;
  • информация, связанная с файловой системой: umask, текущий рабочий каталог и корневой каталог;
  • интервальные таймеры ( setitimer()) и POSIX-таймеры ( timer_create());
  • semadjЗначения System V семафора отменить ( ) (раздел 47.8);
  • ограничения ресурсов;
  • Время процессора (как возвращено times());
  • потребленные ресурсы (как возвращено getrusage()); и
  • хорошее значение (устанавливается с помощью setpriority()и nice()).

Среди атрибутов, которые различны для каждого потока, следующие:

  • идентификатор потока (раздел 29.5);
  • сигнальная маска;
  • специфичные для потока данные (раздел 31.3);
  • стек альтернативных сигналов ( sigaltstack());
  • переменная errno;
  • среда с плавающей точкой (см. fenv(3));
  • политика и приоритет планирования в реальном времени (разделы 35.2 и 35.3);
  • Сродство процессора (специфично для Linux, описано в разделе 35.4);
  • возможности (специфичные для Linux, описаны в главе 39); и
  • стек (локальные переменные и информация о связи между вызовами функций).

Отрывок из: Интерфейс программирования Linux: Справочник по системному программированию Linux и UNIX, Майкл Керриск , стр. 619

ОСШ
источник
0

Поток разделяет кучу (есть исследование о куче, специфичной для потока), но текущая реализация разделяет кучу. (и конечно же код)

Дани
источник
0

В процессе все потоки совместно используют системный ресурс, такой как куча памяти и т. Д., А поток имеет свой собственный стек

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

Рошни
источник