Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Реентерабельность программы и функции

Лекция



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

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

реентерабельность тесно связана с безопасностью функции в многопоточной среде (thread-safety), тем не менее, это разные понятия. Обеспечение реентерабельности является ключевым моментом при программировании многозадачных систем, в частности, операционных систем.

Для обеспечения реентерабельности необходимо выполнение нескольких условий:

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

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

Пример

В следующем фрагменте кода функции f() и g() не являются реентерабельными.

int g_var = 1;

int f ()
{
  g_var = g_var + 2;
  return g_var;
}

int g ()
{
  return f () + 2;
}

Здесь f() зависит от глобальной переменной g_var, поэтому, если два процесса вызывают f() в одно и то же время, результат непредсказуем. Поэтому, f() не реентерабельна. Но и g() не реентерабельна, поскольку она использует нереентерабельную функцию f().

В следующем фрагменте кода функция accum() также не является реентерабельной.

int accum(int b)
{
  static int a = 0; 
  ++a;
  return (a+b);
}

Здесь accum — функция накапливающая значение а, за которое отвечает статическая переменная. Если accum будет вызвана разными процессами, то результат также будет непредсказуем. Как и в предыдущем примере a является общей для всех вызывающих ее процессов.

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

#define SQR(x) ((x)*(x))
void func (void)
{
  int x, y;
  x = SQR (y);
}

В этом случае макрос SQR(x) будет работать некорректно, если при каждом обращении к аргументу он изменяется.

Обзор

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

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

Локальные данные не используются какими-либо процедурами, независимо от того, вводятся они повторно или нет; следовательно, это не влияет на повторный вход. Глобальные данные определяются вне функций и могут быть доступны более чем одной функции либо в форме глобальных переменных (данные, общие для всех функций), либо в виде статических переменных (данные, общие для всех функций с одним и тем же именем). В объектно-ориентированном программировании глобальные данные определяются в области видимости класса и могут быть частными, что делает их доступными только для функций этого класса. Также существует понятие переменных экземпляра, где переменная класса привязана к экземпляру класса. По этим причинам в объектно-ориентированном программировании это различие обычно зарезервировано для данных, доступных вне класса (общедоступные), и для данных, не зависящих от экземпляров класса (статические).

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

Правила повторного входа

Реентерабельный код не может содержать статические или глобальные непостоянные данные.

Реентерабельные функции могут работать с глобальными данными. Например, подпрограмма обслуживания повторного прерывания может захватывать часть состояния оборудования для работы (например, буфер чтения последовательного порта), которая является не только глобальной, но и изменчивой. Тем не менее, типичное использование статических переменных и глобальных данных не рекомендуется, в том смысле, что в этих переменных должны использоваться только атомарные инструкции чтения-изменения-записи (прерывание или сигнал не должны поступать во время выполнения таких переменных). инструкция). Обратите внимание, что в C даже чтение или запись не гарантируется атомарностью; он может быть разделен на несколько операций чтения или записи. Стандарт C и SUSv3 обеспечивают sig_atomic_tэту цель, хотя с гарантиями только для простых операций чтения и записи, а не для увеличения или уменьшения. Более сложные атомарные операции доступны в C11 , который предоставляетstdatomic.h.

Реентерабельный код не может изменять себя .

Операционная система может позволить процессу изменять свой код. Об этом говорит сайт https://intellect.icu . Для этого есть различные причины (например, быстрое копирование графики), но это может вызвать проблемы с повторным входом , поскольку в следующий раз код может быть другим .

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

Реентерабельный код не может вызывать не реентерабельные компьютерные программы или подпрограммы .

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

Повторный вход подпрограммы, которая работает с ресурсами операционной системы или нелокальными данными, зависит от атомарности соответствующих операций. Например, если подпрограмма изменяет 64-битную глобальную переменную на 32-битной машине, операция может быть разделена на две 32-битные операции, и, таким образом, если подпрограмма прерывается во время выполнения и вызывается снова из обработчика прерывания , глобальная переменная может находиться в состоянии, в котором были обновлены только 32 бита. Язык программирования может предоставлять гарантии атомарности для прерывания, вызванного внутренним действием, таким как переход или вызов. Тогда функция fв выражении вроде(global:=1) + (f()), где порядок оценки подвыражений может быть произвольным в языке программирования, глобальная переменная будет либо установлена ​​в 1, либо в свое предыдущее значение, но не в промежуточном состоянии, когда была обновлена ​​только часть. (Последнее может произойти в C , потому что выражение не имеет точки последовательности .) Операционная система может предоставлять гарантии атомарности для сигналов , таких как системный вызов, прерванный сигналом, не имеющим частичного эффекта. Аппаратное обеспечение процессора может обеспечивать гарантии атомарности для прерываний , таких как прерванные инструкции процессора, не имеющие частичных эффектов.

Примеры

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

Ни реентерабельность, ни потокобезопасность

Это пример функции подкачки, которая не может быть реентерабельной или поточно-ориентированной. Поскольку tmpпеременная используется глобально, без сериализации, среди любых параллельных экземпляров функции, один экземпляр может мешать данным, на которые полагается другой. Таким образом, его не следовало использовать в подпрограмме обслуживания прерывания isr():

int  tmp ;

недействительный  swap( int *  x ,  int *  y ) 
{ 
    tmp  =  * x ; 
    * х  =  * у ; 
    / * Аппаратное прерывание может вызвать здесь isr (). * / 
    * 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 ; 
    * х  =  * у ; 
    / * Аппаратное прерывание может вызвать здесь isr (). * / 
    * y  =  tmp ;     
}

void  isr () 
{ 
    int  x  =  1 ,  y  =  2 ; 
    swap( & x ,  & y ); 
}

Реентерабельность, но не потокобезопасная

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

int  tmp ;

void  swap ( int *  x ,  int *  y ) 
{ 
    / * Сохранить глобальную переменную. * / 
    int  s ; 
    s  =  tmp ;

    tmp  =  * x ; 
    * х  =  * у ;       / * Если здесь происходит аппаратное прерывание, то оно не сможет сохранить значение tmp. Так что это тоже не повторный пример * / 
    * y  =  tmp ;      / * Аппаратное прерывание может вызвать здесь isr (). * /

    / * Восстанавливаем глобальную переменную. * / 
    tmp  =  s ; 
}

void  isr () 
{ 
    int  x  =  1 ,  y  =  2 ; 
    swap( & x ,  & y ); 
}

Реентерабельность и потокобезопасность

Реализация swap()этого выделяется tmpв стеке, а не глобально и вызывается только с неразделенными переменными, поскольку параметры [a] одновременно являются потокобезопасными и реентерабельными. Поточно-ориентированный, потому что стек является локальным для потока, а функция, работающая только с локальными данными, всегда будет давать ожидаемый результат. Нет доступа к общим данным, поэтому нет гонки за данными.

void  подкачка ( int *  x ,  int *  y ) 
{ 
    int  tmp ; 
    tmp  =  * x ; 
    * х  =  * у ; 
    * y  =  tmp ;     / * Аппаратное прерывание может вызвать здесь isr (). * / 
}

void  isr () 
{ 
    int  x  =  1 ,  y  =  2 ; 
    swap( & x ,  & y ); 
}

Повторный обработчик прерывания

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

Дальнейшие примеры

В следующем коде 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 ) 
{ 
    вернуть  е ( i )  +  2 ; 
}

Далее функция является поточно-ориентированной, но не реентерабельной:

int  функция () 
{ 
    mutex_lock ();

    // ... 
    // тело функции 
    // ...

    mutex_unlock (); 
}

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

Вау!! 😲 Ты еще не читал? Это зря!

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

создано: 2022-02-06
обновлено: 2022-02-06
132265



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей



Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Разработка программного обеспечения и информационных систем

Термины: Разработка программного обеспечения и информационных систем