Які ресурси поділяються між потоками?


264

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

Нитки поділяють однакову пам’ять, процеси - ні. Відповівши на це, інтерв'юер посміхнувся злісно і посміхнувся на мене:

З. Чи знаєте ви сегменти, на які поділяється програма?

Моя відповідь: так (думав, що це легко) Стек, дані, код, купа

В .: Отже, скажіть: на які сегменти діляться нитки?

Я не зміг відповісти на це, і закінчив, сказавши їх усі.

Будь ласка, чи може хто-небудь представити правильні та вражаючі відповіді на різницю між процесом та потоком?


9
Нитки поділяють один і той же віртуальний адресний простір , процес - ні.
Бенуа

Відповіді:


177

Ви майже правильні, але нитки поділяють усі сегменти, крім стека. Нитки мають незалежні стеки викликів, однак пам'ять в інших стеках потоків все ще доступна, і теоретично ви можете утримувати вказівник на пам'ять у локальному кадру іншого потоку потоку (хоча ви, мабуть, повинні знайти краще місце, щоб помістити цю пам'ять!).


27
Цікава частина полягає в тому, що хоча потоки мають незалежні стеки викликів, пам'ять в інших стеках все ще доступна.
Karthik Balaguru

1
так - мені цікаво, чи прийнятний доступ до пам'яті в інших стеках між потоками? Поки ви впевнені, що не намагаєтесь посилатись на стек, який був розміщений, я не впевнений, що бачу проблему?
бут

2
@bph: Можна отримати доступ до пам'яті стека іншого потоку, але в інтересах доброї інженерної практики програмного забезпечення, я б не сказав, що це прийнятно .
Грег Хьюгілл

1
Доступ, особливо для запису, до стеків інших потоків обмінюється кількома реалізаціями сміттєзбірника. Однак це може бути виправданим як помилка впровадження ГК.
yyny

56

З Вікіпедії (я думаю, що це зробить дійсно гарну відповідь для інтерв'юера: P)

Нитки відрізняються від традиційних багатозадачних операційних систем тим, що:

  • процеси, як правило, незалежні, тоді як потоки існують як підмножини процесу
  • процеси несуть значну інформацію про стан, тоді як декілька потоків у загальному стані поділяють процес, а також пам'ять та інші ресурси
  • процеси мають окремі простори адрес, тоді як потоки поділяють їх адресний простір
  • процеси взаємодіють лише за допомогою механізмів міжпроцесорних комунікацій, що забезпечуються системою.
  • Переключення контексту між потоками в одному і тому ж процесі, як правило, швидше, ніж контекстне переключення між процесами.

2
про пункт № 2 вище: Для потоків також процесор підтримує контекст.
Джек

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]. Але обидва вказують на однакові речі!

Людина, яка опитувала вас, висловила приховане припущення, що він / вона не заявляв, і це дурна хитрість.

Тож щодо

В. Тож скажіть мені, на який сегмент ділять нитки?

Сегменти не мають відношення до питання, принаймні в 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) DATA (32-бітове зміщення, необхідне для доступу). Можливо також, що існує додатковий сегмент TLS DATA (Thread Local Store), який генерується на основі потоку
Adisak

5
А, ні! Ви плутаєте сегменти з розділами! Розділи - це те, як лінкер ділить модуль на частини (дані, rdata, текст, bss тощо), як ви описали. Але я говорю про сегменти, як зазначено в апаратному забезпеченні intel / amd x86. Не пов’язані взагалі з компіляторами / посиланнями. Сподіваюся, що це має сенс.
Олексій Будовський

Однак Adisak має рацію щодо магазину Thread Local. Вона є приватною для потоку і не поділяється. Я знаю ОС Windows і не впевнений в інших ОС.
Джек

20

Як правило, нитки називають легким ваговим процесом. Якщо ми розділимо пам’ять на три розділи, то це буде: Код, дані та стек. У кожного процесу є свій розділ коду, даних та стека, і через цей контекст час комутації є трохи високим. Щоб скоротити час переключення контексту, люди придумали концепцію потоку, яка ділиться даними та кодовим сегментом з іншим потоком / процесом, і він має свій сегмент STACK.


Ви забули купу. Купу, якщо я не помиляюся, слід поділитись між
темами

20

Процес має сегменти коду, даних, купи та стеки. Тепер, покажчик Інструкції (IP) потоку АБО нитки вказує на кодовий сегмент процесу. Сегменти даних і купи діляться всіма потоками. А як щодо області стеку? Що таке насправді площа стека? Його область, створена процесом, лише для того, щоб використовувати її потік ... тому що стеки можна використовувати набагато швидше, ніж купи тощо. Площа стека процесу поділена між потоками, тобто якщо є 3 потоки, то Площа стіки процесу ділиться на 3 частини і кожна надається по 3 нитки. Іншими словами, коли ми кажемо, що кожен потік має свій стек, цей стек є фактично частиною області стека процесу, виділеної для кожного потоку. Коли нитка закінчує її виконання, стек потоку відновлюється процесом. Фактично, не тільки стек процесу поділяється між потоками, але весь набір регістрів, який використовує потік, такі як SP, PC та регістри стану, є регістрами процесу. Таким чином, коли мова йде про спільний доступ, ділянки коду, даних та купи діляться, тоді як область стека просто розділена між потоками.


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 вказують на та ж область, але це лише для спрощення речей.

Загалом, всі регістри сегментів можуть мати різні значення під час роботи. Отже, питання інтерв'ю було правильним: який з КОДУ, ДАНИХ і СТАК поділяється між потоками. Управління купою - це щось інше - це просто послідовність дзвінків в ОС. Але що робити, якщо у вас взагалі немає ОС, як у вбудованій системі - чи все одно ви можете мати новий / видалити у своєму коді?

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


2

Окрім глобальної пам’яті, потоки також мають ряд інших атрибутів (тобто ці атрибути є глобальними для процесу, а не специфічними для потоку). Ці атрибути включають наступне:

  • ідентифікатор процесу та ідентифікатор батьківського процесу;
  • ідентифікатор групи групи та ідентифікатор сеансу;
  • контрольний термінал;
  • облікові дані процесу (ідентифікатори користувачів та групи);
  • дескриптори відкритого файлу;
  • блокування записів, створені за допомогою fcntl();
  • диспозиції сигналів;
  • інформація, що стосується файлової системи: umask, поточний робочий каталог та кореневий каталог;
  • таймери інтервалу ( setitimer()) та таймери POSIX ( timer_create());
  • semadjЗначення V semaphore undo ( ) (розділ 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

Thread share the heap (є дослідження щодо певної нитки), але поточна реалізація поділяє купу. (і звичайно код)


0

У процесі роботи всі потоки діляться системними ресурсами, такими як пам'ять купи тощо, в той час як Thread має власний стек

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.