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

Просматриваем список процессов в Linux

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

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

Способ 1: Терминал

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

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

  1. Для начала запустите консоль, нажав на соответствующий значок в меню или используя комбинацию клавиш Ctrl + Alt + T.

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

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

Чтобы отобразились сразу все процессы, стоит добавить -A. В таком случае команда выглядит как (A обязательно должна быть в верхнем регистре). После нажатия на клавишу Enter вы сразу увидите сводку строк.

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

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

Тогда полный список процессов с расширенной информацией будет вызываться через . В таблице вы увидите UID — имя пользователя, запустившего процесс, PID — уникальный номер, PPID — номер родительского процесса, C — количество времени нагрузки на ЦП в процентах, когда активен процесс, STIME — время активации, TTY — номер консоли, откуда был совершен запуск, TIME — время работы, CMD — команда, запустившая процесс.

Каждый процесс имеет свой PID (Proccess Identificator). Если вы хотите увидеть сводку о конкретном объекте, пропишите , где PID — номер процесса.

Отдельно хотелось бы затронуть и сортировку. Например, команда позволяет поставить все строки в порядке нагрузки на CPU, а — по затрачиваемому объему оперативной памяти.

Выше мы рассказали об основных аргументах команды , однако присутствуют еще и другие параметры, например:

  • — отображение дерева процессов;
  • — вывод версий объектов;
  • — выборка всех процессов кроме заданных;
  • — отображение только по имени команды.

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

Способ 2: Системный монитор

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

Подробнее: Способы запуска Системного монитора в Linux

  1. Запустите «Системный монитор» любым удобным методом, например, через меню.

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

Щелкните правой кнопкой мыши на интересующей строке, чтобы перейти в ее свойства.

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

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

Обратите внимание и на панель сверху — она позволяет сортировать таблицу по необходимым значениям.

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

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

TCP ретрансмиты

Отдельно мы показываем количество TCP ретрансмитов (повторных отправок TCP сегментов).

Само по себе наличие ретрансмитов не означает, что в вашей сети есть потери пакетов.
Повторная передача сегмента осуществляется, если передающий узел не получил от принимающего подтверждение (ACK) в течении определенного времени (RTO).

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

На практике количество ретрансмитов обычно коррелирует с нагрузкой на серверы и важно смотреть не на абсолютное значение, а на различные аномалии:

На данном графике мы видим 2 выброса ретрансмитов, в это же время процессы postgres утилизировали CPU данного сервера:

Cчетчики протоколов мы получаем из /proc/net/snmp.

С точки зрения ядра Linux

… есть «runnable entities» — что-то, что можно запустить и выполнение чего можно планировать. В ядре это называется процессом.

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

Для ядра каждый процесс идентифицируется по PID. Для так называемых потоков можно использовать термин TID, но для ядра это одно и то же.

Вывод списка потоков

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

$ ps -L

Утилита htop выводит список потоков. Чтобы выводить только процессы, есть опция «Hide userland threads».

Потоки и nice

Хоть это и противоречит стандарту, но потоки не разделяют nice-значение.

Если вы применяете утилиту renice, то нужно применить её к каждому потоку.

Потоки и сигналы

Сигналы были придуманы задолго до появления Pthreads.

Комбинирование потоков и сигналов — сложное дело, стоит его избегать почти всегда.

  • Действие сигнала распространяется на весь процесс. Например, если один поток неаккуратно обращается к памяти и вызывает SIGSEGV, все потоки будут остановлены.
  • Настройка обработчиков через sigaction() также общая на весь процесс.
  • Сигнал может быть направлен или процессу, или потоку.
  • Сигнальная маска у каждого потока своя.

Потоки и fork — exec

Если поток вызывает exec(), остальные потоки тут же уничтожаются, никакие деструкторы и функции очистки не вызываются.

Если поток вызывает fork(), то только этот поток будет продолжать работать в новом дочернем процессе.

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

В Linux существует три основных типа процессов:

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

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

   Демоны (англ. daemons») — это особый тип фоновых процессов, которые запускаются при старте системы и продолжают работать в виде службы; они не умирают. Такие процессы запускаются как системные задачи (службы). Однако при этом они могут управляться пользователем через init-процесс (о котором мы поговорим чуть позже). Например, к демонам относится служба электронных сообщений sendmail и sshd — служба, принимающая от клиентов запросы на соединения по протоколу ssh. За исключением процесса init и некоторых других, процессы демонов обычно имеют окончание в своем имени.

Обнаружение перенаправления в скрипте

Мы обсудили, как команда может обнаружить, перенаправлен ли какой-либо из потоков, и может соответствующим образом изменить свое поведение. Можем ли мы сделать это в наших собственных сценариях? Да мы можем. И это очень простая техника для понимания и применения.

Введите следующий текст в редактор и сохраните его как input.sh.

  #! / Bin / Баш

 если ;  тогда

   эхо ввода с клавиатуры
 
 еще

   эхо стандартного ввода из трубы или файла
 
 фи 

Используйте следующую команду, чтобы сделать его исполняемым:

  chmod + x input.sh 

Умная часть — это проверка в квадратных скобках . Опция (терминал) возвращает истину (0), если файл, связанный с дескриптором файла, заканчивается в окне терминала . Мы использовали дескриптор файла 0 в качестве аргумента теста, который представляет стандартный .

Если подключен к окну терминала, тест подтвердится. Если подключен к файлу или каналу, тест не пройден.

Мы можем использовать любой удобный текстовый файл для генерации ввода в скрипт. Здесь мы используем файл с именем dummy.txt.

  ./input.sh <dummy.txt 

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

Это было с перенаправлением файла, давайте попробуем это с каналом.

  кот dummy.txt |  ./input.sh 

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

Давайте запустим скрипт без каналов и перенаправлений.

  ./input.sh 

Поток подключен к окну терминала, и скрипт сообщает об этом соответственно.

Чтобы проверить то же самое с выходным потоком, нам нужен новый скрипт. Введите следующее в редактор и сохраните его как output.sh.

  #! / Bin / Баш

 если ;  тогда

 echo stdout собирается в окно терминала
 
 еще

 echo stdout перенаправляется или передается по каналу
 
 фи 

Используйте следующую команду, чтобы сделать его исполняемым:

  chmod + x input.sh 

Единственное существенное изменение в этом скрипте — в тесте в квадратных скобках. Мы используем цифру 1 для представления дескриптора файла для .

Давайте попробуем это. Мы передадим вывод через .

  ./output |  Кот 

Скрипт распознает, что его вывод не идет прямо в окно терминала.

Мы также можем протестировать скрипт, перенаправив вывод в файл.

  ./output.sh> capture.txt 

Нет вывода в окно терминала, мы молча возвращаемся в командную строку. Как и следовало ожидать.

Мы можем заглянуть внутрь файла capture.txt, чтобы увидеть, что было захвачено. Используйте следующую команду, чтобы сделать это.

  кошка захват.ш 

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

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

  ./output.sh 

И это именно то, что мы видим.

Отслеживание активных процессов

Существует несколько различных инструментов для просмотра/перечисления запущенных в системе процессов. Двумя традиционными и хорошо известными из них являются команды ps и top:

Команда ps

Отображает информацию об активных процессах в системе, как показано на следующем скриншоте:

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

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

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

   PID — идентификатор процесса.

   PPID — идентификатор родительского процесса.

   C — загрузка CPU процессом.

   STIME — время начала выполнения процесса.

   TTY — тип терминала, связанного с процессом.

   TIME — количество процессорного времени, потраченного на выполнение процесса.

   CMD — команда, запустившая этот процесс.

Также можно отобразить информацию по конкретному процессу, используя команду , например:

Есть и другие опции, которые можно использовать вместе с командой :

    — показывает информацию о процессах по всем пользователям;

    — показывает информацию о процессах без терминалов;

    — показывает дополнительную информацию о процессе по заданному UID или имени пользователя;

    — отображение расширенной информации.

Если вы хотите вывести вообще всю информацию по всем процессам системы, то используйте команду :

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

Например, чтобы отсортировать список процессов по потреблению ресурсов процессора (в порядке возрастания), введите команду:

Результат:

Если вы ходите выполнить сортировку по потреблению памяти (в порядке убывания), то добавьте к имени интересующего столбца знак минуса:

Результат:

Еще один очень популярный пример использования команды — это объединение её и для поиска заданного процесса по его имени:

Результат:

Команда top

Команда top отображает информацию о запущенных процессах в режиме реального времени:

Рассмотрим детально:

   PID — идентификатор процесса.

   USER — пользователь, которому принадлежит процесс.

   PR — приоритет процесса на уровне ядра.

   NI — приоритет выполнения процесса от до .

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

   RES — текущий объем (в килобайтах) физической памяти процесса.

   SHR — объем совместно используемой с другими процессами памяти.

   S (сокр. от «STATUS») — состояние процесса:

   S (сокр. от «Sleeping») — прерываемое ожидание. Процесс ждет наступления события.

   I (сокр. от «Idle») — процесс бездействует.

   R (сокр. от «Running») — процесс выполняется (или поставлен в очередь на выполнение).

   Z (сокр. от «Zombie») — зомби-процесс.

   %CPU — процент используемых ресурсов процессора.

   %MEM — процент используемой памяти.

   TIME+ — количество процессорного времени, потраченного на выполнение процесса.

   COMMAND — имя процесса (команды).

Также в сочетании с основными символами состояния процесса (S от «STATUS») вы можете встретить и дополнительные:

    — процесс с высоким приоритетом;

    — процесс с низким приоритетом;

    — многопоточный процесс;

    — фоновый процесс;

    — лидер сессии.

Примечание: Все процессы объединены в сессии. Процессы, принадлежащие к одной сессии, определяются общим идентификатором сессии — идентификатором процесса, который создал эту сессию. Лидер сессии — это процесс, идентификатор сессии которого совпадает с его идентификаторами процесса и группы процессов.

Команда glances

Команда glances — это относительно новый инструмент мониторинга системы с расширенными функциями:

Примечание: Если в вашей системе отсутствует данная утилита, то установить её можно с помощью следующих команд:

RHEL/CentOS/Fedora

Debian/Ubuntu/Linux Mint

Команда lsof

lsof означает ‘LiSt Open Files’ (список открытых файлов). Эта программа используется чтобы узнать, какие файлы открыты и каким процессом.

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

Многие процессы или устройства, о которых lsof может сообщать, принадлежат root или были запущены пользователем root, поэтому вам нужно будет использовать команду sudo с lsof.

И поскольку этот список будет очень длинным, то можно передать его команде less:

sudo lsof | less

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

lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
      Output information may be incomplete.

lsof пытается обработать все смонтированные файловые системы. Это предупреждающее сообщение появляется из-за того, что lsof обнаружил виртуальную файловую систему GNOME (GVFS). Это особый случай файловой системы в пространстве пользователя (FUSE). Он действует как мост между GNOME, его API и ядром. Никто, даже root, не может получить доступ к одной из этих файловых систем, кроме владельца, который её смонтировал (в данном случае, текущий пользователь). Вы можете игнорировать это предупреждение. Если же вы хотите получить доступ к данной файловой системе, то запустите lsof без sudo.

Шапка вывода lsof:

«Убиваем» процесс командой kill

Когда известен PID процесса, мы можем убить его командой kill. Команда kill принимает в качестве параметра PID процесса. Например, убьем процесс с номером 25609:

Вообще команда kill предназначена для посылки сигнала процессу. По умолчанию, если мы не указываем какой сигнал посылать, посылается сигнал SIGTERM (от слова termination — завершение). SIGTERM указывает процессу на то, что необходимо завершиться. Каждый сигнал имеет свой номер. SIGTERM имеет номер 15. Список всех сигналов (и их номеров), которые может послать команда kill, можно вывести, выполнив kill -l. Чтобы послать сигнал SIGKILL (он имеет номер 9) процессу 25609, выполните в командой строке:

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

18. Nagios – Мониторинг сети/сервера

Nagios – мощная система мониторинга, которая даёт возможность сетевому/системному администраторы выявить и разрешить проблемы с сервером до того, как они доставят серьёзные проблемы. С системой Nagios, администраторы могут следить за удалёнными Linux, Windows, свичерами, роутерами и принтерами в одном окне. Она показывает критические предупреждения и даёт знать, если что-то пошло не так в вашей сети/сервере, что является предпосылкой для начала процессов исправления до того, как проблема по-настоящему проявила себя.

19. Nmon: Производительность системы Linux

Nmon – инструмент контроля производительности, который используется для наблюдения за всеми ресурсами Linux, к которым относится центральный процессор, память, использование диска, сеть, топ процессов, NFS, ядро и многое другое. Этот инструмент поставляется с двумя режимами: Online Mode и Capture Mode.

Online Mode используется для мониторинга в реальном времени, а Capture Mode сохраняет вывод в формате CSV для последующей обработки.

nmon

30. Linux Dash – Мониторинг производительности сервера Linux

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

31. Cacti – Мониторинг сети и системы

Cacti – это веб-интерфейс для RRDtool, он часто используется для контроля использования сети, используя SNMP (Simple Network Management Protocol), также может использоваться для контроля использования центрального процессора.

Особенности Cacti

  • Бесплатен, открыт, лицензия GPL.
  • Написан на PHP в PL/SQL.
  • Инструмент является кроссплатформенным, работает на Windows и Linux.
  • Управление пользователями; вы можете создать различные пользовательские аккаунты для Cacti.

Значение столбцов lsof

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

  • COMMAND: Имя команды, связанной с процессом, который открыл файл.
  • PID: Идентификационный номер процесса, который открыл файл.
  • TID: Идентификационный номер задачи (потока) . Пустой столбец означает, что это не задача; это процесс.
  • TASKCMD: это имя команды задачи. Обычно это будет то же самое, что и процесс, названный в столбце COMMAND, но некоторые реализации задач (например, Linux) позволяют задаче изменить имя своей команды.
  • USER: это идентификационный номер пользователя или логин пользователя, которому принадлежит процесс, обычно то же самое, что сообщает ps. Однако в Linux USER — это идентификационный номер пользователя или логин, который владеет каталогом в /proc, где lsof находит информацию о процессе. Обычно это то же самое значение, которое сообщает ps, но может отличаться, если процесс изменил свой эффективный идентификатор пользователя (также смотрите Что такое файловая система /proc в Linux)
  • FD: показывает файловый дескриптор файла. Файловые дескрипторы описаны ниже.
  • TYPE: тип узла, связанного с файлом. Виды данных типов описаны ниже.
  • DEVICE: содержит номера устройств, разделённые запятыми, для специальных символьных, специальных блочных, обычных файлов, каталогов или NFS. Также может отображаться базовый адрес или имя устройства с сокетом Linux AX.25.
  • SIZE/OFF: Показывает размер файла или смещение файла в байтах.
  • NODE: Показывает номер узла локального файла или номер узла NFS-файла на хосте сервера или тип интернет-протокола. Может отображаться STR для потока, IRQ или номер инода устройства с сокетом Linux AX.25.
  • NAME: для обычных файлов показывает имя точки монтирования и файловой системы, в которой находится файл. Для других типов файлов здесь указываются специфичные для них данные, подробности будут даны ниже.

25. Sysstat – Система контроля производительности всё-в-одном

Ещё один инструмент мониторинга для вашей системы Linux. На самом деле Sysstat – это не команда, это название проекта. В действительности Sysstat – это пакет, который включает много инструментов слежения за производительностью, к ним относятся iostat, sadf, pidstat и много других инструментов, которые показывают вам множество статистической информации о вашей ОС Linux.

Особенности Sysstat

  • Доступна во многих стандартных репозиториях дистрибутивов Linux.
  • Может собирать статистику об использовании RAM, CPU, SWAP. Помимо этого, способность мониторить активность ядра Linux, NFS сервера, сокетов, TTY и файловых систем.
  • Способность мониторить статистику ввода и вывода для устройств, задач и т.п.
  • Способность выводить отчёты о сетевых интерфейсах и устройствах, в том числе с поддержкой IPv6.
  • Sysstat может показать вам также статистику энергопотребления (использование, устройства, скорость вентиляторов и т.д.).
  • Многие другие функции.

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

  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.

Внутреннее устройство примитивов синхронизации

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

Атомарные операции

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

  • atomic_dec(var) — уменьшить значение переменной на единицу, вернуть её старое значение.
  • atomic_inc(var) — увеличить значение переменной на единицу, вернуть её старое значение.
  • cmpxchg(var, old, new) — если текущее значение var равно old, то заменить его на new. В любом случае веруть значение var до операции.

Как правило, эти операции сравнительно быстрые, но гораздо медленнее неатомарных аналогов. На x86 могут занимать порядка 100 тактов.

Futex

Futex (сокращение от fast userspace mutex) — способ реализации базовых блокировок, «кирпичик» для построения более сложных примитивов синхронизации (семафоров, мьютексов).

Фьютекс состоит из двух частей:

  • выровненной переменной 32-битного целого типа, которая размещается в userspace,
  • очередь ожидания в kernelspace, ассоциированная с этим целым числом.

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

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

Словом futex называется системный вызов:

#include <linux/futex.h>
#include <sys/time.h>
 
int futex(int *uaddr, int futex_op, int val,
          const struct timespec *timeout,   /* or: uint32_t val2 */
          int *uaddr2, int val3);

Впервые введены в ядро Linux с версии 2.5.7 (development); выработана стабильная семантика с 2.5.40; включаются в стабильные версии серии 2.6.x.

Операции

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

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

  1. futex_wait(addr, val) — приостановить выполнение текущего потока в случае, если переменная в памяти имеет указанное значение. Перед помещением потока в очередь ожидания ядро проверяет значение переменной по адресу addr: если там записано не val, то засыпать не нужно, сразу же вернуть управление.
  2. futex_wake(addr, count) — разбудить один или несколько потоков в очереди. Макс. количество потоков передаётся в переменной count.

Пример: event

class event {
public:
    event() : val(0) { }

    void ev_signal() {
        ++val;
        futex_wake(&val, INT_MAX);
    }

    void ev_wait() {
        futex_wait(&val, val);
    }

private:
    int val;
};

Пример: mutex

Введём состояния по значениям переменной-фьютекса:

  1. unlocked,
  2. locked, no waiters,
  3. locked, one or more waiters.
class mutex {
public:
    mutex() : val(0) { }

    void lock() {
        int c;
        if ((c = cmpxchg(val, 0, 1)) != 0) {
            do {
                if (c == 2 || cmpcxhg(val, 1, 2) != 0) {
                    futex_wait(&val, 2);
                }
            } while ((c = cmpxchg(val, 0, 2)) != 0);
        }
    }

    void unlock() {
        if (atomic_dec(val) != 1) {
            val = 0;
            futex_wake(&val, 1);
        }
    }

private:
    int val;
}
Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Ваша ОС
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: