Что такое процесс в программировании
Перейти к содержимому

Что такое процесс в программировании

  • автор:

Параллельное программирование

Процесс (process) — это объект, который создается операционной системой, при запуске приложения. Процессу выделяется отдельное адресное пространство, причем это пространство физически недоступно для других процессов. При запуске приложения порождается минимум 1 поток, он называется главным. Главный поток может порождать другие потоки. При завершении главного потока — дочерние потоки так же завершаются. У каждого процесса есть приоритет, чтобы могла ОС регулировать выделение процессорного времени. При необходимости организации взаимодействия между процессами (или потоками, принадлежащими разным процессам), используется IPC. Механимы IPC (inter-process communication) включается в себя:

  • Файлы
  • Сигналы
  • Сокет
  • Канал (pipe)
  • Семафор
  • Разделяемая память
  • MMAP (файлы замавленные в память)
  • Очереди сообщений

Поток.

Все потоки, созданные процессом, выполняются в адресном пространстве этого процесса и имеют доступ к ресурсам процесса. Поток разных процессов не имеет никакого доступа к ресурсам друг друга, так как они работают в разных адресных пространствах.

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

CAS работает с переменными размером с машинное слово. В современных архитектурах запись в переменную с размером машинное слово всегда атомарно.

Поток и процессы в операционной системе, процессоре и программировании

Lorem ipsum dolor

Поток, процесс, задача, операционная систем а , программа, программировани е — это все звенья одной системы. Что такое потоки в процессоре? Что такое процесс в программировании? Что такое потоки и процессы в операционной системе? Эти и другие вопросы часто ставят врасплох начинающих программистов. Во всех этих определениях можно не запутаться, если понимать , о чем и дет речь. Сегодня в стать е м ы разложим все «по полкам» простым и понятным языком.

Процессы и потоки в операционной систем е ( ОС)

  • оперативная память;

  • время процессоров.

Что такое потоки в операционной системе и процессоре

Мы выяснили, что процесс в операционной системе является неким объектом, которому выделяются системные ресурсы, но самостоятельно он не выполняет код программы. У одного процесса мо жет быть несколько потоков, которые будут выполняться одновременно и параллельно. Важно отметить, что несколько потоков одного процесса будут выполнять отдельные части кода одной программы.

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

Представим на секунду, сколько процессов может быть одновременно запущено на компьютере? Несколько десятков. А сколько потоков могут быть одновременно запущены на компьютере? Несколько сотен. Установленный процессор чисто физически не справился бы со всеми потоками одновременно. Поэтому в операционной системе существует специальный планировщик процессорных потоков. Суть его работы сводится к тому, чтобы выдавать приоритет каждому отдельному потоку и отправлять на выполнение тот, у которого максимальный приоритет.

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

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

Что такое потоки в процессоре

  • один с 4 ядрами по 2 потока, то есть всего 8 потоков;

  • второй с 8 яд рами по одному потоку, то есть тоже 8 потоков.

Заключение

Что такое потоки в процессоре? Это «каналы» для одновременного исполнения команд. Чем больше потоков, тем больше команд может обработать процессор, а значит , многопоточная программа будет работать быстрее.

Что такое процесс в ОС? Простыми словами, процессом в операционной системе является каждое отдельно запущенное приложение. Процессы в ОС не взаимосвязаны, поэтому могут работать по отдельности.

Что такое поток в программировании? Это возможность разрабатываемой программы работать параллельно в несколько «веток » ( потоков). Потоки в программировании взаимосвязаны. Потоки одной программы не могут работать отдельно друг от друга.

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

Мы будем очень благодарны

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

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

Программа (program) – это последовательность команд, реализующая алгоритм решения задачи. Программа может быть записана на языке программирования (например, на Pascal , С++, BASIC); в этом случае она хранится на диске в виде текстового файла с расширением, соответствующим языку программирования (например, .PAS, . CPP , .VB). Также программа может быть представлена при помощи машинных команд; тогда она хранится на диске в виде двоичного исполняемого файла ( executable file ), чаще всего с расширением .EXE. Исполняемый файл генерируется из текста программы при компиляции.

Процесс (process) – это программа (пользовательская или системная) в ходе выполнения.

В современных операционных системах процесс представляет собой объект – структуру данных, содержащую информацию, необходимую для выполнения программы. Объект «Процесс» создается в момент запуска программы (например, пользователь дважды щелкает мышью на исполняемом файле) и уничтожается при завершении программы.

Если операционная система умеет запускать в одно и то же время несколько процессов, она называется многозадачной ( multitasking ) (пример – Windows ), иначе – однозадачной (пример – MS DOS ).

Процесс может содержать один или несколько потоков ( thread ) – объектов, которым операционная система предоставляет процессорное время . Сам по себе процесс не выполняется – выполняются его потоки. Таким образом, машинные команды, записанные в исполняемом файле, выполняются на процессоре в составе потока. Если потоков несколько, они могут выполняться одновременно.

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

Операционные системы, поддерживающие несколько процессоров, называются многопроцессорными.

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

Многопоточность – это средство распараллеливания действий внутри процесса.

  1. В современных браузерах каждой вкладке соответствует свой поток.
  2. В текстовом редакторе один поток может управлять вводом текста, второй – проверять орфографию, третий – выводить документ на принтер.
  3. В компьютерной игре-стратегии за обработку действий каждого игрока отвечает отдельный поток.

Каждый процесс имеет свое собственное виртуальное адресное пространство (см. лекцию 11 » Управление памятью «), в котором независимо друг от друга работают все потоки процесса. Например, поток 1 может записывать данные в ячейку с адресом 100, а поток 2 читать данные из ячейки с адресом 101.

Замечание. Конечно, если два (или более) потоков захотят записать что-то свое в одну и ту же ячейку, возникнет неопределенность – кто раньше? Это одна из подзадач обширной проблемы синхронизации.

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

Структуры данных для процессов и потоков

В WRK за управление процессами отвечает диспетчер процессов (base\ntos\ps), а многие важные структуры данных описаны в заголовочных файлах base\ntos\inc\ps.h и base\ntos\inc\ke.h.

Процесс в Windows описывается структурой данных EPROCESS [5]. Эта структура в WRK содержится в файле base\ntos\inc\ps.h (строка 238). Рассмотрим некоторые её поля.

  • Pcb (Process Control Block – блок управления процессом) – представляет собой структуру KPROCESS , хранящую данные, необходимые для планирования потоков, в том числе указатель на список потоков процесса (файл base\ntos\inc\ke.h, строка 944).
  • CreateTime и ExitTime – время создания и завершения процесса.
  • UniqueProcessId – уникальный идентификатор процесса.
  • ActiveProcessLinks – элемент двунаправленного списка (тип LIST_ENTRY ), содержащего активные процессы.
  • QuotaUsage , QuotaPeak , CommitCharge – квоты (ограничения) на используемую память.
  • ObjectTable – таблица дескрипторов процесса.
  • Token – маркер доступа.
  • ImageFileName – имя исполняемого файла.
  • ThreadListHead – двунаправленный список потоков процесса.
  • Peb (Process Environment Block – блок переменных окружения процесса) – информация об образе исполняемого файла (файл public\sdk\inc\pebteb.h, строка 75).
  • PriorityClass – класс приоритета процесса (см. лекцию 9 «Планирование потоков»).

Структура для потока в Windows называется ETHREAD и описывается в файле base\ntos\inc\ps.h (строка 545). Её основные поля следующие:

  • Tcb (Thread Control Block – блок управления потоком) – поле, которое является структурой типа KTHREAD (файл base\ntos\inc\ke.h, строка 1069) и необходимо для планирования потоков.
  • CreateTime и ExitTime – время создания и завершения потока.
  • Cid – структура типа CLIENT_ID , включающая два поля – идентификатор процесса-владельца данного потока и идентификатор самого потока.
  • ThreadsProcess – указатель на структуру EPROCESS процесса-владельца.
  • StartAddress – адрес системной стартовой функции потока. При создании потока сначала вызывается системная стартовая функция, которая запускает пользовательскую стартовую функцию.
  • Win32StartAddress – адрес пользовательской стартовой функции.

Создание процесса

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

Процесс, создавший другой процесс, называется родителем, а созданный процесс – потомком. Таким образом, формируется иерархия процессов.

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

В Windows для создания процессов применяется одна из следующих WinAPI-функций: CreateProcess, CreateProcessAsUser, CreateProcessWithTokenW, CreateProcessWithLogonW [10]. Далее при описании будем использовать функцию CreateProcess.

https://amdy.su/wp-admin/options-general.php?page=ad-inserter.php#tab-8

Создание процессов в Windows включает 7 этапов [5; 2].

1. Проверка и преобразование параметров.

Параметры функции CreateProcess проверяются на корректность и преобразуются к внутреннему формату системы.

2. Открытие исполняемого файла.

Происходит поиск файла, который содержит запускаемую программу. Обычно это файл с расширением .EXE, но могут быть также расширения . COM , . PIF , .BAT, . CMD . Определяется тип исполняемого файла:

  • Windows приложение (.EXE) – продолжается нормальное создание процесса;
  • приложение MS-DOS или Win16 (.EXE, .COM, .PIF) – запускается образ поддержки Ntvdm.exe;
  • командный файл (.BAT, .CMD) – запускается образ поддержки Cmd.exe;
  • приложение POSIX – запускается образ поддержки Posix.exe.

3. Создание объекта «Процесс».

Формируются структуры данных EPROCESS, KPROCESS, PEB , инициализируется адресное пространство процесса. Для этого вызывается системная функция NtCreateProcess ( файл base\ntos\ps\create.c, строка 826), которая затем вызывает функцию NtCreateProcessEx (тот же файл , строка 884), а та, в свою очередь , функцию PspCreateProcess (тот же файл , строка 1016).

Замечание. Начиная с Windows Vista при создании процесса вызов нескольких функций ( NtCreateProcess, NtWriteVirtualMemory, NtCreateThread ) заменен вызовом одной функции NtCreateUserProcess.

Рассмотрим некоторые важные действия, выполняемые функцией PspCreateProcess .

  • Если в параметрах функции PspCreateProcess указан процесс-родитель:
    • по его дескриптору определяется указатель на объект EPROCESS (функция ObReferenceObjectByHandle , строка 1076);
    • наследуется от процесса родителя маска привязки к процессорам (Affinity, строка 1092).

    4. Создание основного потока.

    Формируется структура данных ETHREAD , стек и контекст потока, генерируется идентификатор потока. Поток создается при помощи функции NtCreateThread, определенной в файле base\ntos\ps\create.c, (строка 117), которая вызывает функцию PspCreateThread (тот же файл , строка 295). При этом выполняются следующие действия:

    • создается объект ETHREAD (строка 370).
    • Заполняются поля структуры ETHREAD , связанные с процессом-владельцем, – указатель на структуру EPROCESS (ThreadsProcess) и идентификатор процесса ( Cid.UniqueProcess ) (строки 396 и 398).
    • Генерируется уникальный идентификатор потока (функция ExCreateHandle ) и сохраняется в поле Cid.UniqueThread структуры EPROCESS (строки 400–402).
    • Заполняются стартовые адреса потока, системный ( StartAddress ) и пользовательский ( Win32StartAddress ) (строки 468-476).
    • Инициализируются поля структуры KTHREAD при помощи вызова функции KeInitThread (строки 490–498 для потока пользовательского режима и 514–522 для потока режима ядра).
    • Функция KeStartThread заполняет остальные поля структуры ETHREAD и вставляет поток в список потоков процесса (строка 564).
    • Если при вызове функции PspCreateThread установлен флаг CreateSuspended («Приостановлен») поток переводится в состояние ожидания (функция KeSuspendThread , строка 660); иначе вызывается функция KeReadyThread (строка 809), которая ставит поток в очередь готовых к выполнению потоков (см. лекцию 9 «Планирование потоков»).

    5. Уведомление подсистемы Windows.

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

    6. Запуск основного потока.

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

    7. Инициализация процесса.

    • Проверяется, не запущен ли процесс в отладочном режиме;
    • проверяется, следует ли производить предвыборку блоков памяти (тех участков памяти, которые при прошлом запуске использовались в течение первых 10 секунд работы процесса);
    • инициализируются необходимые компоненты и структуры данных процесса, например, диспетчер кучи;
    • загружаются динамически подключаемые библиотеки (DLL – Dynamic Link Library);
    • начинается выполнение стартовой функции потока.

    Резюме

    В этой лекции введены понятия «процесса» и «потока». Рассмотрены структуры данных, представляющие в операционной системе процесс ( EPROCESS ) и поток ( ETHREAD ). Описан ход создания процесса с использованием структур данных и функций Windows Research Kernel .

    Следующая лекция посвящена алгоритмам планирования потоков и реализации этих алгоритмов в Windows .

    Процессы и потоки in-depth. Обзор различных потоковых моделей

    Здравствуйте дорогие читатели. В данной статье мы рассмотрим различные потоковые модели, которые реализованы в современных ОС (preemptive, cooperative threads). Также кратко рассмотрим как потоки и средства синхронизации реализованы в Win32 API и Posix Threads. Хотя на Хабре больше популярны скриптовые языки, однако основы — должны знать все 😉

    Потоки, процессы, контексты.

    Системный вызов (syscall). Данное понятие, вы будете встречать достаточно часто в данной статье, однако несмотря на всю мощь звучания, его определение достаточно простое 🙂 Системный вызов — это процесс вызова функции ядра, из приложение пользователя. Режим ядра — код, который выполняется в нулевом кольце защиты процессора (ring0) с максимальными привилегиями. Режим пользователя — код, исполняемый в третьем кольце защиты процессора (ring3), обладает пониженными привилегиями. Если код в ring3 будет использовать одну из запрещенных инструкций (к примеру rdmsr/wrmsr, in/out, попытку чтения регистра cr3, cr4 и т.д.), сработает аппаратное исключение и пользовательский процесс, чей код исполнял процессор в большинстве случаях будет прерван. Системный вызов осуществляет переход из режима ядра в режим пользователя с помощью вызова инструкции syscall/sysenter, int2eh в Win2k, int80h в Linux и т.д.

    И так, что же такое поток? Поток (thread) — это, сущность операционной системы, процесс выполнения на процессоре набора инструкций, точнее говоря программного кода. Общее назначение потоков — параллельное выполнение на процессоре двух или более различных задач. Как можно догадаться, потоки были первым шагом на пути к многозадачным ОС. Планировщик ОС, руководствуясь приоритетом потока, распределяет кванты времени между разными потоками и ставит потоки на выполнение.

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

    • Регистры процессора.
    • Указатель на стек потока/процесса.
    • Если ваша задача требует интенсивного распараллеливания, используйте потоки одного процесса, вместо нескольких процессов. Все потому, что переключение контекста процесса происходит гораздо медленнее, чем контекста потока.
    • При использовании потока, старайтесь не злоупотреблять средствами синхронизации, которые требуют системных вызовов ядра (например мьютексы). Переключение в редим ядра — дорогостоящая операция!
    • Если вы пишете код, исполняемый в ring0 (к примеру драйвер), старайтесь обойтись без использования дополнительных потоков, так как смена контекста потока — дорогостоящая операция.

    Классификация потоков

    • По отображению в ядро: 1:1, N:M, N:1
    • По многозадачной модели: вытесняющая многозадачность (preemptive multitasking), кооперативная многозадачность (cooperative multitasking).
    • По уровню реализации: режим ядра, режим польователя, гибридная реализация.

    Классификация потоков по отображению в режим ядра

    • Центральный планировщик ОС режима ядра, который распределяет время между любым потоком в системе.
    • Планировщик библиотеки потоков. У библиотеки потоков режима пользователя может быть свой планировщик, который распределяет время между потоками различных процессов режима пользователя.
    • Планировщик потоков процесса. Уже рассмотренные нами волокна, ставятся на выполнение именно таким способом. К примеру свой Thread Manager есть у каждого процесса Mac OS X, написанного с использованием библиотеки Carbon.

    Модель N:M отображает некоторое число потоков пользовательских процессов N на M потоков режима ядра. Проще говоря имеем некую гибридную систему, когда часть потоков ставится на выполнение в планировщике ОС, а большая их часть в планировщике потоков процесса или библиотеки потоков. Как пример можно привести GNU Portable Threads. Данная модель достаточно трудно реализуема, но обладает большей производительностью, так как можно избежать значительного количества системных вызовов.

    Модель N:1. Как вы наверное догадались — множество потоков пользовательского процесса отображаются на один поток ядра ОС. Например волокна.

    Классификация потоков по многозадачной модели

    Во времена DOS, когда однозадачные ОС перестали удовлетворять потребителя, программисты и архитекторы задумали реализовать многозадачную ОС. Самое простое решение было следующим: взять общее количество потоков, определить какой-нибудь минимальный интервал выполнения одного потока, да взять и разделить между всеми -братьями- потоками время выполнения поровну. Так и появилось понятие кооперативной многозадачности (cooperative multitasking), т.е. все потоки выполняются поочередно, с равным временем выполнения. Никакой другой поток, не может вытеснить текущий выполняющийся поток. Такой очень простой и очевидный подход нашел свое применение во всех версиях Mac OS вплоть до Mac OS X, также в Windows до Windows 95, и Windows NT. До сих пор кооперативная многозадачность используется в Win32 для запуска 16 битных приложений. Также для обеспечения совместимости, cooperative multitasking используется менеджером потоков в Carbon приложениях для Mac OS X.

    Однако, кооперативная многозадачность со временем показала свою несостоятельность. Росли объемы данных хранимых на винчестерах, росла также скорость передачи данных в сетях. Стало понятно, что некоторые потоки должны иметь больший приоритет, как-то потоки обслуживания прерываний устройств, обработки синхронных IO операций и т.д. В это время каждый поток и процесс в системе обзавелся таким свойством, как приоритет. Подробнее о приоритетах потоков и процессов в Win32 API вы можете прочесть в книге Джефри Рихтера, мы на этом останавливатся не будем 😉 Таким образом поток с большим приоритетом, может вытеснить поток с меньшим. Такой прицип лег в основу вытесняющей многозадачности (preemptive multitasking). Сейчас все современные ОС используют данный подход, за исключением реализации волокон в пользовательском режиме.

    Классификация потоков по уровню реализации

    1. Реализация потоков на уровне ядра. Проще говоря, это классическая 1:1 модель. Под эту категорию подпадают:
      • Потоки Win32.
      • Реализация Posix Threads в Linux — Native Posix Threads Library (NPTL). Дело в том, что до версии ядра 2.6 pthreads в Linux был целиком и полностью реализован в режиме пользователя (LinuxThreads). LinuxThreads реализовывалf модель 1:1 следующим образом: при создании нового потока, библиотека осуществляла системный вызов clone, и создавало новый процесс, который тем не менее разделял единое адресное пространство с родительским. Это породило множество проблем, к примеру потоки имели разные идентификаторы процесса, что противоречило некоторым аспектам стандарта Posix, которые касаются планировщика, сигналов, примитивов синхронизации. Также модель вытеснения потоков, работала во многих случаях с ошибками, по этому поддержку pthread решено было положить на плечи ядра. Сразу две разработки велись в данном направлении компаниями IBM и Red Hat. Однако, реализация IBM не снискала должной популярности, и не была включена ни в один из дистрибутивов, потому IBM приостановила дальнейшую разработку и поддержку библиотеки (NGPT). Позднее NPTL вошли в библиотеку glibc.
      • Легковесные ядерны потоки (Leight Weight Kernel Threads — LWKT), например в DragonFlyBSD. Отличие этих потоков, от других потоков режима ядра в том, что легковесные ядерные потоки могут вытеснять другие ядерные потоки. В DragonFlyBSD существует множество ядерных потоков, например поток обслуживания аппаратных прерываний, поток обслуживания программных прерываний и т.д. Все они работают с фиксированным приоритетом, так вот LWKT могут вытеснять эти потоки (preempt). Конечно это уже более специфические вещи, про которые можно говорить бесконечно, но приведу еще два примера. В Windows все потоки ядра выполняются либо в контексте потока инициировавшего системный вызов/IO операцию, либо в контексте потока системного процесса system. В Mac OS X существует еще более интересная система. В ядре есть лишь понятие task, т.е. задачи. Все операции ядра выполняются в контексте kernel_task. Обработка аппаратного прерывания, к примеру, происходит в контексте потока драйвера, который обслуживает данное прерывание.
    2. Реализация потоков в пользовательском режиме. Так как, системный вызов и смена контекста — достаточно тяжелые операции, идея реализовать поддержку потоков в режиме пользователя витает в воздухе давно. Множество попыток было сделано, однако данная методика популярности не обрела:
      • GNU Portable Threads — реализация Posix Threads в пользовательском режиме. Основное преимущество — высокая портабельность данной библиотеки, проще говоря она может быть легко перенесена на другие ОС. Проблему вытиснения потоков в данной библиотеке решили очень просто — потоки в ней не вытесняются 🙂 Ну и конечно ни о какой мультмпроцессорности речь идти не может. Данная библиотека реализует модель N:1.
      • Carbon Threads, которые я упоминал уже не раз, и RealBasic Threads.
    3. Гибридная реализация. Попытка использовать все преимущества первого и второго подхода, но как правило подобные мутанты обладают гораздо бОльшими недостатками, нежели достоинствами. Один из примеров: реализация Posix Threads в NetBSD по модели N:M, которая была посже заменена на систему 1:1. Более подробно вы можете прочесть в публикации Scheduler Activations: Effective Kernel Support for the User-Level Management of Parallelism.

    Win32 API Threads

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

    Потоки в Win32 создаются с помощью функции CreateThread, куда передается указатель на функцию (назовем ее функцией потока), которая будет выполнятся в созданом потоке. Поток считается завершенным, когда выполнится функция потока. Если же вы хотите гарантировать, что поток завершен, то можно воспользоватся функцией TerminateThread, однако не злоупотребляйте ею! Данная функция «убивает» поток, и отнюдь не всегда делает это корректно. Функция ExitThread будет вызвана неявно, когда завершится функция потока, или же вы можете вызвать данную функцию самостоятельно. Главная ее задача — освободить стек потока и его хендл, т.е. структуры ядра, которые обслуживают данный поток.

    Поток в Win32 может пребывать в состоянии сна (suspend). Можно «усыпить поток» с помощью вызова функции SuspendThread, и «разбудить» его с помощью вызова ResumeThread, также поток можно перевести в состояние сна при создании, установив значение параметра СreateSuspended функции CreateThread. Не стоит удивлятся, если вы не увидите подобной функциональности в кроссплатформенных библиотеках, типа boost::threads и QT. Все очень просто, pthreads просто не поддерживают подобную функциональность.

    Средства синхронихации в Win32 есть двух типов: реализованные на уровне пользователя, и на уровне ядра. Первые — это критические секции (critical section), к второму набору относят мьютексы (mutex), события (event) и семафоры (semaphore).

    Критические секции — легковесный механизм синхронизации, который работает на уровне пользовательского процесса и не использует тяжелых системных вызовов. Он основан на механизме взаимных блокировок или спин локов (spin lock). Поток, который желает обезопасить определенные данные от race conditions вызывает функцию EnterCliticalSection/TryEnterCriticalSection. Если критическая секция свободна — поток занимает ее, если же нет — поток блокируется (т.е. не выполняется и не отъедает процессорное время) до тех пор, пока секция не будет освобождена другим потоком с помощью вызова функции LeaveCriticalSection. Данные функции — атомарные, т.е. вы можете не переживать за целостность ваших данных 😉

    • Они использует примитивы ядра при выполнении, т.е. системные вызовы, что сказывается не производительности.
    • Могут быть именованными и не именованными, т.е. каждому такому объекту синхронизации можно присвоить имя.
    • Работают на уровне системы, а не на уровне процесса, т.е. могут служить механизмом межпроцессного взаимодействия (IPC).
    • Используют для ожидания и захвата примитива единую функцию: WaitForSingleObject/WaitForMultipleObjects.

    Posix Threads или pthreads

    Сложно представить, какая из *nix подобных операционных систем, не реализует этот стандарт. Стоит отметить, что pthreads также используется в различных операционных системах реального времени (RTOS), потому требование к этой библиотеке (вернее стандарту) — жестче. К примеру, поток pthread не может пребывать в состоянии сна. Также в pthread нет событий, но есть гораздо более мощный механизм — условных переменных (conditional variables), который с лихвой покрывает все необходимые нужды.

    Поговорим об отличиях. К примеру, поток в pthreads может быть отменен (cancel), т.е. просто снят с выполнения посредством системного вызова pthread_cancel в момент ожидания освобождения какого-нибудь мьютекса или условной переменной, в момент выполнения вызова pthread_join (вызывающий поток блокируется до тех пор, пока не закончит свое выполнение поток, приминительно к которому была вызвана функция) и т.д. Для работы с мьютексами и семафорами существует отдельные вызовы, как-то pthread_mutex_lock/pthread_mutex_unlock и т.д.

    Conditional variables (cv) обычно используется в паре с мьютексами в более сложных случаях. Если мьютекс просто блокирует поток, до тех пор, пока другой поток не освободит его, то cv создают условия, когда поток может заблокировать сам себя до тех пор, пока не произойдет какое-либо условия разблокировки. Например, механизм cv помогает эмулировать события в среде pthreads. Итак, системный вызов pthread_cond_wait ждет, пока поток не будет уведомлен о том, что случилось определенное событие. pthread_cond_signal уведомляет один поток из очереди, что cv сработала. pthread_cond_broadcast уведомляет все потоки, которые вызывали pthread_cond_wait, что сработала cv.

    Прощальное слово

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

    UPD: дополнил статью небольшой информацией о режиме ядра и режиме пользователя.
    UPD2: исправил досадные промахи и ошибки. Спасибо комментаторам 😉

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *