7.3.2 Блокировка памяти

Предыдущая  Содержание  Следующая V*D*V

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

 

Функции блокировки памяти

 

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

 

Таблица 7.3 Функции блокировки памяти POSIX.1b

 

Метод

Описание

mlock

Блокировка указанной области адресного пространства процесса

mlockall

Блокировка всего адресного пространства процесса

munlock

Разблокировка области, заблокированной с помощью mlock

munlockall

Разблокировка всего адресного пространства процесса

 

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

 

Эффективная блокировка с помощью сценария компоновщика

 

Идея заключается в размещении объектных файлов, содержащих код и данные реального времени, в отдельном разделе компоновщика с помощью сценария компоновщика. Блокирование с помощью mlock такого раздела во время запуска программы привело бы к блокировка только кода и данных, использующихся в режиме реального времени. Для иллюстрации этого возьмём пример приложения. В Распечатке 7.4 мы предполагаем, что hello_rt_world - это функция реального времени, которая работает с rt_data в rt_bss как неинициализированными данными.

Для достижения селективного блокирования должны быть выполнены следующие шаги:

 

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

a.hello_world.c: содержит функции, не работающие в режиме реального времени

b.hello_rt_world.c: содержит функции, работающие в режиме реального времени

c.hello_rt_data.c: содержит данные реального времени

d.hello_rt_bss.c: содержит bss реального времени

e.hello_main.c: целевое приложение

2.Сгенерировать объектный код, но не делать связывание.
 
# gcc -c hello_world.c hello_rt_world.c hello_rt_data.c \
     hello_rt_bss.c hello_main.c
 

3.Получить сценарий компоновщика по умолчанию и сделать копию.
 
# ld –verbose > default
# cp default rt_script

 

4.Отредактировать rt_script и удалить команды компоновщика. (Удаляем всё перед командой OUTPUT_FORMAT, а также ==== .. в конце файла.)

5.Найти в rt_script разделы .text, .data и .bss и добавить перед ними записи rt_text, rt_data и rt_bss, соответственно, как показано в Распечатке 7.5. Таким образом, все функции, определённые в hello_rt_world.c, войдут в раздел rt_text.  Данные, определённые в hello_rt_data.c, войдут в раздел rt_data, а все неинициализированные данные в hello_rt_bss.c войдут в раздел rt_bss. Переменные __start_rt_text, __start_rt_data и __start_rt_bss отмечают начало разделов rt_text, rt_data и rt_bss, соответственно. Аналогично, __end_rt_text, __end_rt_data и __end_rt_bss отмечают адрес  конца соответствующих разделов.

6.Наконец, компонуем приложение.
 
# gcc -o hello hello_main.o hello_rt_bss.o \
    hello_rt_data.o hello_rt_world.o hello_world.o \
    -T rt_script

 

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

 

# objdump -t hello

  .....

08049720   g     .rt_bss   00000000   __start_rt_bss

08049760   g  O  .rt_bss   00000064   rt_bss

080497c4   g     .rt_bss   00000000   __end_rt_bss.....

080482f4   g     .rt_text  00000000   __start_rt_text

080482f4   g  F  .rt_text  0000001d   hello_rt_world

0804834a   g     .rt_text  00000000   __end_rt_text

                      ......

080496c0   g     .rt_data  00000000   __start_rt_data

080496c0   g  O  .rt_data  00000011   rt_data

08049707   g     .rt_data  00000000   __end_rt_data

 

Эффективная блокировка с помощью раздела атрибутов GCC

 

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

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

 

# gcc -o hello hello.c

# objdump -t hello

   .....

   .....

08049724 g     *ABS*      00000000   __stop_real_bss

08048560 g     *ABS*      00000000   __stop_real_text

080496a0 g     *ABS*      00000000   __start_real_data

080496b1 g     *ABS*      00000000   __stop_real_data

080496c0 g     *ABS*      00000000   __start_real_bss

0804852c g     *ABS*      00000000   __start_real_text

080496c0 g  O  real_bss   00000064   rt_bss

080496a0 g  O  real_data  00000011   rt_data

0804852c g  F  real_text  00000034   hello_rt_world

   ......

   ......

 

Обратите внимание на определённые компоновщиком символы __start_real_text, __stop_real_text, и так далее.

 

Что следует помнить

 

Один вызов munlock или munlockall разблокирует область памяти, даже если она заблокирована процессом несколько раз.

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

Дочерние процессы не наследуют блокировки страниц через ветвление.

Страницы, заблокированные mlock или mlockall, гарантированно остаются в памяти, пока страницы не разблокированы munlock или munlockall, не отключено их отображение через munmap, или пока процесс не завершится или не запустит другую программу с помощью exec.

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

В случае, если вы хотите быть уверенными, что выделения памяти для стека остаются детерминированными, вам также необходимо заблокировать некоторые страницы стека. Чтобы избежать замещения для сегмента стека, вы можете написать небольшую функцию lock_stack и вызвать её во время инициализации.
 
void lock_stack(void){
 char dummy[MAX_APPROX_STACK_SIZE];
 /* Это делает блокировку страниц стека */
 memset(dummy, 0, MAX_APPROX_STACK_SIZE);
 mlock(dummy, MAX_APPROX_STACK_SIZE);
 return;
}
 
MAX_APPROX_STACK_SIZE является оценкой размера стека, используемого вашим потоком реального времени. Как только это сделано, ядро гарантирует, что это пространство для стека всегда находится в памяти.

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

 

Предыдущая  Содержание  Следующая