Главная » 2017 » Ноябрь » 18 » man 7 vdso
21:51
man 7 vdso

SEO sprint - Всё для максимальной раскрутки!





ИМЯ


vdso - обзор виртуального динамически компонуемого общего объекта ELF



ОБЗОР


#include <sys/auxv.h>

void *vdso = (uintptr_t) getauxval(AT_SYSINFO_EHDR);



ОПИСАНИЕ


«vDSO» (virtual dynamic shared object, виртуальный динамический общий объект) —
это маленькая общая библиотека, которую ядро автоматически отображает в адресное
пространство всех приложений пользовательского пространства. Обычно, приложениям
она не нужна, так как vDSO, чаще всего, вызывается из библиотеки C. Вы можете
использовать стандартные функции как обычно, а библиотека C самостоятельно
позаботится об использовании возможностей vDSO.

И всё же, зачем нужна vDSO? Есть несколько системных вызовов ядра, которые
используются в пользовательском коде настолько часто, что это сильно влияет на
общую производительность. Это происходит из-за частого повторения вызовов, а также
затрат на переключение контекста, которые возникают при выходе из
пользовательского пространства и входа в ядро.

Оставшаяся часть этой документации предназначена для любопытных и/или авторов
библиотеки C, а не для обычных разработчиков. Если вы попытаетесь вызвать vDSO в
своём приложении не через библиотеку C, то, наиболее вероятно, сделаете это
неправильно.

В качестве примера
Выполнение системных вызовов может быть медленным. В 32-битных системах x86 вы
можете использовать программное прерывание (int $0x80), чтобы заставить ядро
выполнить системный вызов. Однако, эта инструкция очень затратна: она проходит по
полному маршруту обработки прерываний в микрокоде процессора, а также в ядре.
Новые процессоры содержат более быстрые инструкции (но обратно совместимые) для
запуска системных вызовов. Вместо того, чтобы требовать от библиотеки C выяснения
во время выполнения есть ли такая возможность, библиотека C может использовать
функции, предоставляемые ядром в vDSO.

Заметим, что можно запутаться в терминологии. В системах x86 функция vDSO,
используемая для определения предпочтительного метода выполнения системного
вызова, называется «__kernel_vsyscall», но в x86-64 термин «vsyscall» также
ссылается на устаревший метод запроса ядра о времени или на каком ЦП выполняется
вызывающий.

Одним из наиболее часто используемых системных вызовов является gettimeofday(2).
Данный вызов используется прямо в приложениях, а также неявно из библиотеки C.
Метка времени, временной цикл или опрос — для всего этого часто нужно знать
сколько времени в данный момент. Также, это информация не секретна — многие
приложения с любыми правами (root или любого не привилегированного пользователя)
получат одинаковый ответ. Поэтому ядро размещает информацию, требуемую для ответа
на этот вопрос, в памяти процесса. Теперь системный вызов gettimeofday(2)
заменяется на вызов обычной функции и доступ к памяти.

Нахождение vDSO
Базовый адрес vDSO (если есть) передаётся ядром каждой программе во
вспомогательном векторе инициализации (смотрите getauxval(3)) через тег
AT_SYSINFO_EHDR.

должен использоваться.

Формат файла
Так как vDSO — полноценный образ ELF, вы можете искать в нём символы. Это
позволяет добавлять новые символы в новых версиях ядра и библиотеке C находить
доступные свойства во время выполнения с различными версиями ядер. Зачастую,
библиотека C выполняет обнаружение при первом вызове и затем кэширует результат
для последующих вызовов.

Все символы имеют версии (в формате версий GNU). Это позволяет ядру обновлять
сигнатуру функции без нарушения обратной совместимости. Смена версии означает
изменение аргументов функции или возвращаемого значения. Таким образом, при поиске
символа в vDSO, вы всегда должны включать версию для нахождения ожидаемого ABI.

Обычно, vDSO удовлетворяет соглашению об именовании, начиная все символы с
«__vdso_» или «__kernel_», для их выделения среди других стандартных символов.
Например, функция «gettimeofday» называется «__vdso_gettimeofday».

Для вызова этих функций используйте стандартные соглашения о вызове языка Си.
Учитывать поведение регистров и стека не требуется.



ЗАМЕЧАНИЯ


Исходный код
Код vDSO автоматически компилируется и компонуется при сборке ядра. Обычно, его
можно найти в каталоге соответствующей архитектуры:

find arch/$ARCH/ -name '*vdso*.so*' -o -name '*gate*.so*'

Имена vDSO
Имя vDSO отличается на разных архитектурах. Часто его можно увидеть в выводе
утилит, подобных ldd(1) из glibc. Точное имя не должно влиять на код, поэтому
жёстко оно нигде не задаётся.

ABI пользователя имя vDSO
_
aarch64 linux-vdso.so.1
arm linux-vdso.so.1
ia64 linux-gate.so.1
mips linux-vdso.so.1
ppc/32 linux-vdso32.so.1
ppc/64 linux-vdso64.so.1
s390 linux-vdso32.so.1
s390x linux-vdso64.so.1
sh linux-gate.so.1
i386 linux-gate.so.1
x86-64 linux-vdso.so.1
x86/x32 linux-vdso.so.1

strace(1) и vDSO
При трассировке системных вызовов с помощью strace(1), символы (системные вызовы),
экспортируемые vDSO, не показываются в результате трассировки.



ЗАМЕЧАНИЯ ДЛЯ РАЗЛИЧНЫХ АРХИТЕКТУР


Далее приведены замечания по vDSO для различных архитектур.

Заметим, что используемая vDSO основывается на ABI вашего кода пользовательского
пространства, а не на ABI ядра. Например, когда вы запускаете 32-битный
_
__vdso_gettimeofday LINUX_2.6 (экспортируется начиная с Linux 4.1)
__vdso_clock_gettime LINUX_2.6 (экспортируется начиная с Linux 4.1)

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

Эта страница с кодом хорошо описана в документации к ядру и в ней есть всё, что
вам нужно знать: Documentation/arm/kernel_user_helpers.txt.

Функции на aarch64
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_rt_sigreturn LINUX_2.6.39
__kernel_gettimeofday LINUX_2.6.39
__kernel_clock_gettime LINUX_2.6.39
__kernel_clock_getres LINUX_2.6.39

Функции на bfin (Blackfin)
Так как в этом ЦП отсутствует устройство управления памятью (MMU), на данной
архитектуре не создаётся vDSO в обычном понимании. Вместо этого, ядро отображает
несколько функций в постоянное расположение в памяти. Приложения пользовательского
пространства могут вызывать их из этой области напрямую. Здесь отсутствует
обратная совместимость кроме как слежение за кодами операций (sniffing raw
opcodes), но для таких встраиваемых ЦП это нормально — некоторые форматы объектов,
запускаемых на нём, даже не основаны на ELF (например, bFLT/FLAT).

Эта страница с кодом хорошо описана в открытой документации:
http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:fixed-code

Функции на mips
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_gettimeofday LINUX_2.6 (экспортируется начиная с Linux 4.4)
__kernel_clock_gettime LINUX_2.6 (экспортируется начиная с Linux 4.4)

Функции на ia64 (Itanium)
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_sigtramp LINUX_2.5
__kernel_syscall_via_break LINUX_2.5
__kernel_syscall_via_epc LINUX_2.5

Перенос на Itanium, в некоторой степени, неоднозначный. Кроме vDSO, показанной
выше, также есть «легковесные системные вызовы» (также называемые как «быстрые
syscall» или «fsys»). Вы можете вызвать их через вспомогательную функцию vDSO
__kernel_syscall_via_epc. Перечисленные здесь системные вызовы имеют ту же
семантику, как если бы вызывались напрямую через syscall(2), поэтому обратитесь к
соответствующей документации по каждому из них. В таблице ниже перечислены
функции, доступные через этот механизм.

Функции на parisc (hppa)
Перенос parisc содержит страницу кода со вспомогательными функциями, называемую
шлюзовой (gateway) страницей. Вместо того, чтобы использовать обычный
вспомогательный вектор ELF, передаётся адрес страницы для обработки через регистр
SR2. Права на страницу таковы, что простое выполнение этих адресов автоматически
выполняется с правами ядра и не в пространстве пользователя. Подобный способ
применяется в HP-UX.

Так как это просто страница с кодом, информация ELF отсутствует и поиск функций
невозможен и неизвестны их версии. Просто вызывайте функцию по соответствующему
смещению через инструкцию ветвления, например:

ble <смещение>(%sr2, %r0)

смещение функция
_
00b0 lws_entry
00e0 set_thread_pointer
0100 linux_gateway_entry (syscall)
0268 syscall_nosys
0274 tracesys
0324 tracesys_next
0368 tracesys_exit
03a0 tracesys_sigexit
03b8 lws_start
03dc lws_exit_nosys
03e0 lws_exit
03e4 lws_compare_and_swap64
03e8 lws_compare_and_swap
0404 cas_wouldblock
0410 cas_action

Функции на ppc/32
В таблице ниже перечислены символы, экспортируемые vDSO. Функции, помеченные *,
доступны только в ядре PowerPC64 (64-бита).

символ версия
_
__kernel_clock_getres LINUX_2.6.15
__kernel_clock_gettime LINUX_2.6.15
__kernel_datapage_offset LINUX_2.6.15
__kernel_get_syscall_map LINUX_2.6.15
__kernel_get_tbfreq LINUX_2.6.15
__kernel_getcpu * LINUX_2.6.15
__kernel_gettimeofday LINUX_2.6.15
__kernel_sigtramp_rt32 LINUX_2.6.15
__kernel_sigtramp32 LINUX_2.6.15
__kernel_sync_dicache LINUX_2.6.15
__kernel_sync_dicache_p5 LINUX_2.6.15

Часы CLOCK_REALTIME_COARSE и CLOCK_MONOTONIC_COARSE не поддерживаются в
интерфейсах __kernel_clock_getres и __kernel_clock_gettime; ядро вернется к
использованию реального системного вызова.

Функции на ppc/64
В таблице ниже перечислены символы, экспортируемые vDSO.
__kernel_gettimeofday LINUX_2.6.15
__kernel_sigtramp_rt64 LINUX_2.6.15
__kernel_sync_dicache LINUX_2.6.15
__kernel_sync_dicache_p5 LINUX_2.6.15

Часы CLOCK_REALTIME_COARSE и CLOCK_MONOTONIC_COARSE не поддерживаются в
интерфейсах __kernel_clock_getres и __kernel_clock_gettime; ядро вернется к
использованию реального системного вызова.

Функции на s390
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_clock_getres LINUX_2.6.29
__kernel_clock_gettime LINUX_2.6.29
__kernel_gettimeofday LINUX_2.6.29

Функции на s390x
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_clock_getres LINUX_2.6.29
__kernel_clock_gettime LINUX_2.6.29
__kernel_gettimeofday LINUX_2.6.29

Функции на sh (SuperH)
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_rt_sigreturn LINUX_2.6
__kernel_sigreturn LINUX_2.6
__kernel_vsyscall LINUX_2.6

Функции на i386
В таблице ниже перечислены символы, экспортируемые vDSO.

символ версия
_
__kernel_sigreturn LINUX_2.5
__kernel_rt_sigreturn LINUX_2.5
__kernel_vsyscall LINUX_2.5
__vdso_clock_gettime LINUX_2.6 (экспортируется начиная с Linux 3.15)
__vdso_gettimeofday LINUX_2.6 (экспортируется начиная с Linux 3.15)
__vdso_time LINUX_2.6 (экспортируется начиная с Linux 3.15)

Функции x86-64
В таблице ниже перечислены символы, экспортируемые vDSO. Все эти символы также
доступны без префикса «__vdso_», но вы не должны пользоваться этим, применяйте
имена, перечисленные ниже.

символ версия
_
__vdso_clock_gettime LINUX_2.6
__vdso_getcpu LINUX_2.6
__vdso_getcpu LINUX_2.6
__vdso_gettimeofday LINUX_2.6
__vdso_time LINUX_2.6

История
Первоначально vDSO представляла собой одну функцию — vsyscall. В старых ядрах вы
можете видеть это имя в карте памяти процесса вместо «vdso». Со временем, люди
поняли, что этот механизм является отличным способом перенести больше возможностей
в пользовательское пространство, результатом чего стала vDSO в текущем виде.



СМОТРИТЕ ТАКЖЕ


syscalls(2), getauxval(3), proc(5)

Документация, примеры и исходный код в дереве исходного кода Linux:

Documentation/ABI/stable/vdso
Documentation/ia64/fsys.txt
Documentation/vDSO/* (содержит примеры использования vDSO)

find arch/ -iname '*vdso*' -o -iname '*gate*'



Категория: (7) Различные описания, соглашения и прочее | Просмотров: 453 | Добавил: Администратор | Рейтинг: 0.0/0
Всего комментариев: 0
avatar