Реентерабельность (вычисления)
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
Реентерабельность — это концепция программирования, при которой функция или подпрограмма может быть прервана, а затем возобновлена до завершения ее выполнения. Это означает, что функцию можно вызвать еще раз, прежде чем она завершит предыдущее выполнение. Реентерабельный код спроектирован так, чтобы быть безопасным и предсказуемым, когда несколько экземпляров одной и той же функции вызываются одновременно или в быстрой последовательности. или Компьютерная программа подпрограмма называется реентерабельной, если несколько вызовов могут безопасно выполняться одновременно на нескольких процессорах или если в однопроцессорной системе ее выполнение может быть прервано и безопасно запущено новое выполнение (ее можно «ввести повторно» "). Прерывание может быть вызвано внутренним действием, таким как переход или вызов, или внешним действием, таким как прерывание или сигнал , в отличие от рекурсии , где новые вызовы могут быть вызваны только внутренним вызовом.
Это определение происходит из мультипрограммных сред, где несколько процессов могут быть активны одновременно и где поток управления может быть прерван прерыванием и передан подпрограмме обслуживания прерываний (ISR) или подпрограмме «обработчика». Любая подпрограмма, используемая обработчиком и которая потенциально могла выполняться в момент срабатывания прерывания, должна быть реентерабельной. Аналогично, код, общий для двух процессоров, осуществляющих доступ к общим данным, должен быть реентерабельным. операционной системы, Часто подпрограммы, доступные через ядро не являются реентерабельными. Следовательно, подпрограммы обслуживания прерываний ограничены в действиях, которые они могут выполнять; например, им обычно ограничен доступ к файловой системе , а иногда даже к выделению памяти .
Повторный вход не является ни необходимым, ни достаточным для обеспечения потокобезопасности в многопоточных средах. Другими словами, реентерабельная подпрограмма может быть потокобезопасной. [1] но это не гарантированно [ нужна ссылка ] . И наоборот, потокобезопасный код не обязательно должен быть реентерабельным (примеры см. ниже).
Другие термины, используемые для реентерабельных программ, включают «совместный код». [2] Реентерабельные подпрограммы иногда помечаются в справочных материалах как «безопасные по сигналу». [3] Программы повторного поступления часто [а] «чистые процедуры».
Фон
[ редактировать ]Реентерабельность — это не то же самое, что идемпотентность , при которой функция может вызываться более одного раза, но при этом генерировать точно такой же результат, как если бы она была вызвана только один раз. Вообще говоря, функция создает выходные данные на основе некоторых входных данных (хотя, как правило, и то, и другое не является обязательным). К общим данным может получить доступ любая функция в любое время. Если данные могут быть изменены любой функцией (и ни одна из них не отслеживает эти изменения), для тех, кто использует общие данные, нет никакой гарантии, что эти данные такие же, как и в любой момент времени.
Данные имеют характеристику, называемую областью действия , которая описывает, где в программе могут использоваться данные. Область данных может быть либо глобальной (вне области действия любой функции и с неопределенным объемом), либо локальной (создается каждый раз при вызове функции и уничтожается при выходе).
Локальные данные не используются никакими подпрограммами, независимо от того, вводятся они повторно или нет; следовательно, это не влияет на повторный вход. Глобальные данные определяются вне функций и могут быть доступны более чем одной функции либо в форме глобальных переменных (данных, общих для всех функций), либо в виде статических переменных (данных, общих для всех вызовов одной и той же функции). В объектно-ориентированном программировании глобальные данные определяются в области класса и могут быть частными, что делает их доступными только для функций этого класса. Существует также концепция переменных экземпляра , когда переменная класса привязана к экземпляру класса. По этим причинам в объектно-ориентированном программировании это различие обычно зарезервировано для данных, доступных вне класса (общедоступных), и для данных, независимых от экземпляров класса (статических).
Реентерабельность отличается от потокобезопасности , но тесно связана с ней . Функция может быть потокобезопасной и при этом не реентерабельной. Например, функция может быть полностью обернута мьютексом ( что позволяет избежать проблем в многопоточных средах), но если эта функция используется в подпрограмме обслуживания прерываний, она может простоять в ожидании первого выполнения, которое освободит мьютекс. Ключом к избежанию путаницы является то, что реентерабельность означает выполнение только одного потока. Это концепция тех времен, когда еще не существовало многозадачных операционных систем.
Правила повторного входа
[ редактировать ]- Реентерабельный код не может содержать статические или глобальные непостоянные данные без синхронизации .
- Реентерабельные функции могут работать с глобальными данными. Например, процедура обслуживания реентерабельного прерывания может получить часть состояния оборудования для работы (например, буфер чтения последовательного порта), которая не только глобальна, но и нестабильна. разделов кода Тем не менее, типичное использование статических переменных и глобальных данных не рекомендуется в том смысле, что, за исключением синхронизированных только атомарные инструкции чтения-изменения-записи , в этих переменных следует использовать (это не должно быть возможно для прерывание или сигнал, который должен прийти во время выполнения такой инструкции). Обратите внимание, что в C даже чтение или запись не гарантированно являются атомарными; его можно разделить на несколько операций чтения или записи. [4] Стандарт C и SUSv3 обеспечивают
sig_atomic_t
для этой цели, хотя и с гарантиями только для простого чтения и записи, а не для увеличения или уменьшения. [5] Более сложные атомарные операции доступны в C11 , который обеспечиваетstdatomic.h
. - Реентерабельный код не может изменяться без синхронизации.
- Операционная система может позволить процессу изменять свой код. Для этого существуют различные причины (например, быстрое копирование графики), но обычно для этого требуется синхронизация, чтобы избежать проблем с повторным входом.
Однако он может модифицировать себя, если находится в его собственной уникальной памяти. То есть, если каждый новый вызов использует другое физическое место машинного кода, где создается копия исходного кода, это не повлияет на другие вызовы, даже если оно изменится во время выполнения этого конкретного вызова (потока).
- Реентерабельный код не может вызывать нереентерабельные компьютерные программы или процедуры без синхронизации.
- пользователя, объекта или процесса Множественные уровни приоритета или многопроцессорность обычно усложняют управление реентерабельным кодом. Важно отслеживать любой доступ или побочные эффекты, которые выполняются внутри подпрограммы, предназначенной для повторного входа.
Повторный вход подпрограммы, которая работает с ресурсами операционной системы или нелокальными данными, зависит от атомарности соответствующих операций. Например, если подпрограмма изменяет 64-битную глобальную переменную на 32-битной машине, операция может быть разделена на две 32-битные операции, и, таким образом, если подпрограмма прерывается во время выполнения и вызывается снова из обработчика прерывания , глобальная переменная может находиться в состоянии, в котором были обновлены только 32 бита. Язык программирования может обеспечивать гарантии атомарности для прерываний, вызванных внутренним действием, например переходом или вызовом. Тогда функция f
в выражении вроде (global:=1) + (f())
, где порядок вычисления подвыражений может быть произвольным на языке программирования, глобальная переменная будет либо установлена в 1, либо в ее предыдущее значение, но не в промежуточном состоянии, где была обновлена только часть. (Последнее может произойти в C , поскольку выражение не имеет точки последовательности .) Операционная система может предоставлять гарантии атомарности для сигналов , таких как системный вызов, прерванный сигналом, не имеющим частичного эффекта. Аппаратное обеспечение процессора может обеспечивать гарантии атомарности прерываний , например прерываемые инструкции процессора, не имеющие частичного эффекта.
Примеры
[ редактировать ]Чтобы проиллюстрировать реентерабельность, в этой статье в качестве примера используется служебная функция C : swap()
, который принимает два указателя и транспонирует их значения, а также процедуру обработки прерываний, которая также вызывает функцию подкачки.
Ни реентерабельный, ни потокобезопасный
[ редактировать ]Это пример функции подкачки, которая не может быть реентерабельной или потокобезопасной. Поскольку tmp
переменная является глобально общей, без синхронизации, среди любых одновременных экземпляров функции, один экземпляр может мешать данным, на которые опирается другой. По существу, его не следовало использовать в процедуре обслуживания прерываний. isr()
:
int tmp;
void swap(int* x, int* y)
{
tmp = *x;
*x = *y;
/* Hardware interrupt might invoke isr() here. */
*y = tmp;
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
Потокобезопасный, но не реентерабельный
[ редактировать ]Функция swap()
в предыдущем примере можно сделать потокобезопасным, сделав tmp
локальный поток . Он по-прежнему не может быть реентерабельным, и это будет продолжать вызывать проблемы, если isr()
вызывается в том же контексте, что и уже выполняющийся поток swap()
:
_Thread_local int tmp;
void swap(int* x, int* y)
{
tmp = *x;
*x = *y;
/* Hardware interrupt might invoke isr() here. */
*y = tmp;
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
Реентерабельный и потокобезопасный
[ редактировать ]Реализация swap()
который выделяет tmp
в стеке, а не глобально, и он вызывается только с неразделяемыми переменными в качестве параметров. [б] является одновременно потокобезопасным и реентерабельным. Потокобезопасен, поскольку стек является локальным по отношению к потоку, и функция, действующая только на локальные данные, всегда будет давать ожидаемый результат. Нет доступа к общим данным, поэтому нет гонки за данными.
void swap(int* x, int* y)
{
int tmp;
tmp = *x;
*x = *y;
*y = tmp; /* Hardware interrupt might invoke isr() here. */
}
void isr()
{
int x = 1, y = 2;
swap(&x, &y);
}
Реентерабельный обработчик прерываний
[ редактировать ]Обработчик повторного прерывания — это обработчик прерываний , который повторно разрешает прерывания на ранних этапах обработчика прерываний. Это может уменьшить задержку прерывания . [6] В общем, при программировании процедур обработки прерываний рекомендуется как можно скорее повторно разрешить прерывания в обработчике прерываний. Эта практика помогает избежать потери прерываний. [7]
Дальнейшие примеры
[ редактировать ]В следующем коде ни f
ни g
функции реентерабельны.
int v = 1;
int f()
{
v += 2;
return v;
}
int g()
{
return f() + 2;
}
В приведенном выше f()
зависит от непостоянной глобальной переменной v
; таким образом, если f()
прерывается во время выполнения ISR, который изменяет v
, затем снова войдите в f()
вернет неправильное значение v
. Стоимость v
и, следовательно, возвращаемое значение f
, невозможно предсказать с уверенностью: они будут различаться в зависимости от того, изменилось ли прерывание. v
в течение f
казнь. Следовательно, f
не является реентерабельным. Ни то, ни другое g
, потому что он вызывает f
, который не является реентерабельным.
Эти слегка измененные версии являются реентерабельными:
int f(int i)
{
return i + 2;
}
int g(int i)
{
return f(i) + 2;
}
Ниже функция является потокобезопасной, но не (обязательно) реентерабельной:
int function()
{
mutex_lock();
// ...
// function body
// ...
mutex_unlock();
}
В приведенном выше function()
может быть вызван разными потоками без каких-либо проблем. Но если функция используется в обработчике реентерабельного прерывания и внутри функции возникает второе прерывание, вторая подпрограмма зависнет навсегда. Поскольку обслуживание прерываний может отключить другие прерывания, может пострадать вся система.
Примечания
[ редактировать ]- ^ Программа, которая сериализует самомодификацию, может быть реентерабельной, а чистая процедура, обновляющая глобальные данные без надлежащей сериализации, может не быть реентерабельной.
- ^ Если isr() вызывает swap() с одной или двумя глобальными переменными в качестве параметров, то swap() не будет реентерабельным.
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Керриск 2010 , с. 657 .
- ^ Ралстон 2000 , с. 1514–1515.
- ^ «pthread_cond_init() — инициализировать переменную условия» . Центр знаний IBM . Проверено 5 октября 2019 г.
- ^ Прешинг, Джефф (18 июня 2013 г.). «Атомарные и неатомарные операции» . Прешинг по программированию . Архивировано из оригинала 3 декабря 2014 г. Проверено 24 апреля 2018 г.
- ^ Керриск 2010 , с. 428 .
- ^ Слосс и др. 2004 , с. 342 .
- ^ Регер, Джон (2006). «Безопасное и структурированное использование прерываний в реальном времени и встроенном программном обеспечении» (PDF) . Справочник по системам реального времени и встроенным системам . ЦРК Пресс . Архивировано (PDF) из оригинала 24 августа 2007 г. - на веб-сайте автора в Школе вычислительной техники Университета Юты.
Цитируемые работы
[ редактировать ]- Керриск, Майкл (2010). Программный интерфейс Linux . Пресс без крахмала .
- Ралстон, Энтони , изд. (2000). «Программа реентеринга». Энциклопедия информатики (4-е изд.). Издательская группа «Природа» .
- Слосс, Эндрю Н.; Саймс, Доминик; Райт, Крис ; Рэйфилд, Джон (2004). Руководство разработчика системы ARM . Издательство Морган Кауфманн. ISBN 9780080490496 .
Дальнейшее чтение
[ редактировать ]- Чен, Раймонд (29 июня 2004 г.). «Разница между потокобезопасностью и повторным входом» . Старая новая вещь . Сеть разработчиков Microsoft . Архивировано из оригинала 24 апреля 2018 г. Проверено 24 апреля 2018 г.
- Ганссл, Джек (15 марта 2001 г.). «Введение в реентерабельность» . Встроенный.com . Архивировано из оригинала 21 января 2013 г. Проверено 24 апреля 2018 г.
- ИБМ (2018). «Общие концепции программирования» (PDF) . Руководство по AIX версии 7.2 . стр. 636–641 . Проверено 24 апреля 2018 г.
- Джа, Дипак (20 января 2005 г.). «Используйте реентерабельные функции для более безопасной обработки сигналов» . IBM DeveloperWorks . Архивировано из оригинала 7 июля 2014 г. Проверено 24 апреля 2018 г.