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

Советую проверить http://wurfl.io/

В двух словах, если вы импортируете крошечный файл JavaScript:


Вы останется объект JSON, который выглядит следующим образом:

{
 "complete_device_name":"Google Nexus 7",
 "is_mobile":true,
 "form_factor":"Tablet"
}

(конечно, при условии, что вы используете Nexus 7), и вы сможете делать такие вещи, как:

if(WURFL.is_mobile) {
    //dostuff();
}

Это то, что вы ищете.

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

44
задан Peter Mortensen 29 January 2014 в 18:58
поделиться

3 ответа

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

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

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

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

40
ответ дан 26 November 2019 в 22:08
поделиться

Пользовательские потоки управляются в пользовательском пространстве - это означает, что планирование, переключение и т. Д. Осуществляется не из ядра.

Поскольку, в конечном счете, ядро ​​ОС отвечает за переключение контекста между «исполнительными модулями» - ваши пользовательские потоки должны быть связаны (т. Е. «Отображать») с планируемым объектом ядра - потоком ядра 1 .

Итак, учитывая N пользовательских потоков - вы можете использовать N потоков ядра (карта 1: 1). Это позволяет вам использовать преимущества аппаратной многопроцессорной обработки ядра (работающей на нескольких процессорах) и быть довольно упрощенной библиотекой - в основном просто перекладывая большую часть работы на ядро. Однако это делает ваше приложение переносимым между ОС, поскольку вы не вызываете напрямую функции потока ядра. Я считаю, что потоки POSIX ( PThreads ) являются предпочтительной реализацией * nix, и что он следует схеме 1: 1 (что делает его практически эквивалентным потоку ядра). Это, однако, не гарантируется, поскольку это будет зависеть от реализации (основной причиной использования PThreads будет переносимость между ядрами).

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

Или вы можете сопоставить произвольное количество потоков ядра - карту N: M. В Windows есть Fibers , которые позволяют отображать N волокон в M потоков ядра и совместно планировать их. Пул потоков также может быть примером этого - N рабочих элементов для M потоков.

1 : процесс имеет по крайней мере 1 поток ядра, который является фактической исполнительной единицей. Кроме того, в процессе должен содержаться поток ядра. Операционная система должна планировать выполнение потока , а не процесса.

18
ответ дан 26 November 2019 в 22:08
поделиться

http://www.informit.com/articles/printerfriendly.aspx?p=25075

Реализация потоков в пространстве пользователя

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

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

Все эти реализации имеют одну и ту же общую структуру, которая показана на рис. 2-8(a). Потоки выполняются поверх системы времени выполнения, которая представляет собой набор процедур, управляющих потоками. Мы уже видели четыре из них: thread_create, thread_exit, thread_wait и thread_yield, но обычно их больше.

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

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

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

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

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

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

Другая альтернатива возможна в том случае, если можно заранее сказать, будет ли вызов блокироваться. В некоторых версиях UNIX существует системный вызов select, который позволяет вызывающей стороне определить, будет ли блокироваться перспективное чтение. Когда этот вызов присутствует, библиотечная процедура read может быть заменена новой, которая сначала выполняет вызов select, а затем выполняет вызов read, только если он безопасен (т.е. не заблокируется). Если вызов read заблокируется, вызов не выполняется. Вместо этого запускается другой поток. В следующий раз, когда система времени выполнения получит управление, она сможет снова проверить, безопасно ли теперь чтение. Этот подход требует переписывания части библиотеки системных вызовов, неэффективен и неэлегантен, но выбор невелик. Код, помещаемый вокруг системного вызова для выполнения проверки, называется оболочкой или оберткой.

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

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

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

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

18
ответ дан 26 November 2019 в 22:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: