- Runtime error with homebrew netlib-native_system-linux-x86_64-natives.jar on CentOS 7.0 #66
- Comments
- yangliuyu commented Jul 23, 2014
- fommil commented Jul 23, 2014
- yangliuyu commented Jul 24, 2014
- gabeos commented Jan 21, 2015
- fommil commented Jan 22, 2015
- kgrech commented Nov 16, 2015
- danielkza commented Jun 19, 2017
- fommil commented Jun 19, 2017
- danielkza commented Jun 19, 2017 •
- Быстрее, выше, сильнее: Clear Linux — самый быстрый дистрибутив для x86-64?
- Эволюция системных вызовов архитектуры x86
- System call
- Способы реализации системных вызовов
- Выполнение неверной инструкции.
- Call gates
- Системные вызовы, реализованные в Linux
- int 80h
- sysenter/sysexit
- syscall/sysret
- vsyscall
- vDSO (Virtual Dynamic Shared Object)
- Сравнение производительности системных вызовов
- Бонус: Производительность системных вызовов без KPTI
Runtime error with homebrew netlib-native_system-linux-x86_64-natives.jar on CentOS 7.0 #66
Comments
yangliuyu commented Jul 23, 2014
I got a runtime error when running spark application.
/usr/java/jdk1.7.0_65/bin/java: symbol lookup error: /tmp/jniloader9104995036364593011netlib-native_system-linux-x86_64.so: undefined symbol: cblas_daxpy
Same build steps work on CentOs 6.5, so maybe it caused by the library version.
gcc.x86_64 4.8.2-16.el7 @base
gcc-gfortran.x86_64 4.8.2-16.el7 @base
libgfortran.x86_64 4.8.2-16.el7 @base
blas.x86_64 3.4.2-4.el7 @base
lapack.x86_64 3.4.2-4.el7 @base
arpack.x86_64 3.1.3-2.el7 @epel
The text was updated successfully, but these errors were encountered:
fommil commented Jul 23, 2014
hmm, looks like you’re missing the cblas layer on top of BLAS (which is a fortran API). most distros compile the C API into the blas library.
Check if you have a libcblas you can install (e.g. ATLAS’ version)
Or compile your own CBLAS against your fortran BLAS. Sources included in this project.
Can you do an ldd against the system-linux lib?
Does the reference lib work ok?
yangliuyu commented Jul 24, 2014
No missing so files hint here.
The atlas-3.10.1-7.el7.x86_64 (CentOS 7) do not contain libcblas.so.3 which atlas-3.8.4-2.el6.x86_64 (CentOS 6.5) does. After install old version of atlas and arpack, the problem fixed.
And now ldd result is:
gabeos commented Jan 21, 2015
I ran into this same issue — in Fedora 21 atlas is packaged only with libsatlas.so (serial) and libtatlas.so (threaded)
Errant bug report describing change
There’s an apparently ongoing discussion of how to handle this here
Anyway, now with fedora 21, you can’t downgrade to atlas 3.8, so there’s no easy way to get things working. I force installed an old rpm of 3.8 with ‘rpm’ rather than yum/dnf, but it’s super hacky and might very well break things..
anyway, I’m just wondering if you have any other suggestions, is there a way to build netlib-java or otherwise directly specify with libraries to use? I tried making symlinks to the libsatlas.so library in the /lib64/atlas directory (same error).
fommil commented Jan 22, 2015
yeah, unfortunately the only thing I can recommend is to tell Fedora to learn how to package these projects correctly.
You could try installing the debian/ubuntu equivalents.
You don’t need to recompile netlib-java. You just need to link it to a library at runtime that implements libblas.so.3 and liblapack.so.3 through symlinks.
Try OpenBLAS or Intel’s Math Kernel Library. They are faster anyway.
kgrech commented Nov 16, 2015
Same issue for openSuse 13.2. Solved by removing repository version of atlas and installing the following rpm:
http://rpm.pbone.net/index.php3/stat/4/idpl/23733523/dir/opensuse/com/atlas-3.8.3-5.1.x86_64.rpm.html
danielkza commented Jun 19, 2017
Sorry for reviving this old issue, but it seems as of today Fedora still does not ship CBLAS with ATLAS or OpenBLAS. There seems to be an implementation available from GSL in libgslcblas.so.0 . My first try was to simply symlink it to libcblas.so.3 , but it was not sufficient, as it seems the netlib JNI objects are not compiled with -lcblas .
The only solution that seemed to work was to run Java with LD_PRELOAD=libgslcblas.so.0 in it’s environment, so that the CBLAS symbols are already loaded in the JVM when netlib-native is loaded. Unfortunately I don’t see an easy way to construct a library that would work in both situations, as explicitly linking to CBLAS would break Debian-based distributions, which do not have it.
fommil commented Jun 19, 2017
GSL is GPL. Make sure that your application is correctly licensed GPL if distributing them linked to GSL.
danielkza commented Jun 19, 2017 •
I’m not distributing any binaries with the LD_PRELOAD workaround applied, I only used it privately. I thought it might be useful to anyone ending up in this thread from Google.
Источник
Быстрее, выше, сильнее: Clear Linux — самый быстрый дистрибутив для x86-64?
На днях ресурс Phoronix опубликовал результаты тестов скорости работы разных дистрибутивов Linux на системе с Core i9 10980XE.
Тестирование проводилось со сборками Clear Linux 33540 (самая новая на момент теста), Clear Linux 31480 (конец 2019 года), Endeavour OS Rolling, openSUSE Tumbleweed 20200727, Debian Buster Testing, Ubuntu 20.04 LTS и тестовая сборка Ubuntu 20.10.
Подробности тестирования — под катом.
Технические характеристики тестируемой системы:
- Процессор: Intel Core i9-10980XE @ 4.60GHz (18 Cores / 36 Threads)
- Плата: Gigabyte X299X DESIGNARE 10G (F1 BIOS)
- Чипсет: Intel Sky Lake-E DMI3 Registers
- SSD: Samsung SSD 970 PRO 512GB
- Графический чип: AMD Radeon RX 64 8GB (1590/800MHz)
- Аудио: Realtek ALC1220
Что касается Clear Linux, то его характеристики следующие:
- Ядро Linux 5.7,
- Компилятор GCC 10.2.1,
- Python 3.8.4,
- I/O-планировщик для NVMe-накопителей — BFQ,
- Графическая оболочка GNOME 3.36.
Тестирование проводилось для того, чтобы выяснить прогресс в разработке Clear Linux. Весной компания Intel, создатель этого дистрибутива, заявила о том, что Clear Linux становится системой для серверных, облачных и встраиваемых систем, где скорость работы и отзывчивость очень критичны.
Пока что обещания постепенно повышать производительность дистрибутива выполняются. Так, время загрузки новой версии Clear Linux по сравнению с предыдущей снизилось сразу в 2,5 раза.
Что касается сравнения с другими системами, то Clear Linux в большей части тестов опередил конкурентов, хотя и не везде. Дистрибутив от Intel оказался первым в 73 из 136 тестов. Результаты авторы проекта визуализировали.
По словам разработчиков, высокая скорость работы дистрибутива Clear Linux объясняется жесткой оптимизацией для архитектуры x86-64.
Второе и третье место заняли Ubuntu 20.10 и Debian Buster. Ubuntu всего на 4% отстал от лидера.
Результаты всех тестов можно просмотреть в итоговом файле.
Источник
Эволюция системных вызовов архитектуры x86
Про системные вызовы уже много было сказано, например здесь или здесь. Наверняка вам уже известно, что системный вызов — это способ вызова функции ядра ОС. Мне же захотелось копнуть глубже и узнать, что особенного в этом системном вызове, какие существуют реализации и какова их производительность на примере архитектуры x86-64. Если вам также интересны ответы на данные вопросы, добро пожаловать под кат.
System call
Каждый раз, когда мы хотим что-то отобразить на мониторе, записать в устройство, считать с файла, нам приходится обращаться к ядру ОС. Именно ядро ОС отвечает за любое общение с железом, именно там происходит работа с прерываниями, режимами процессора, переключениями задач… Чтобы пользователь программой не смог завалить работу всей операционной системы, было решено разделить пространство памяти на пространство пользователя (область памяти, предназначенная для выполнения пользовательских программ) и пространство ядра, а также запретить пользователю доступ к памяти ядра ОС. Реализовано это разделение в x86-семействе аппаратно при помощи сегментной защиты памяти. Но пользовательской программе нужно каким-то образом общаться с ядром, для этого и была придумана концепция системных вызовов.
Системный вызов — способ обращения программы пользовательского пространства к пространству ядра. Со стороны это может выглядеть как вызов обычной функции со своим собственным calling convention, но на самом деле процессором выполняется чуть больше действий, чем при вызове функции инструкцией call. Например, в архитектуре x86 во время системного вызова как минимум происходит увеличение уровня привилегий, замена пользовательских сегментов на сегменты ядра и установка регистра IP на обработчик системного вызова.
Программист обычно не работает с системными вызовами напрямую, так как системные вызовы обернуты в функции и скрыты в различных библиотеках, например libc.so в Linux или же ntdll.dll в Windows, с которыми и взаимодействует прикладной разработчик.
Теоретически, реализовать системный вызов можно при помощи любого исключения, хоть при помощи деления на 0. Главное — это передача управления ядру. Рассмотрим реальные примеры реализаций исключений.
Способы реализации системных вызовов
Выполнение неверной инструкции.
Ранее, ещё на 80386 это был самый быстрый способ сделать системный вызов. Для этого обычно применялась бессмысленная и неверная инструкция LOCK NOP, после исполнения которой процессором вызывался обработчик неверной инструкции. Это было больше 20 лет назад и, говорят, этим приёмом обрабатывались системные вызовы в корпорации Microsoft. Обработчик неверной инструкции в наши дни используется по назначению.
Call gates
Для того, чтобы иметь доступ к сегментам кода с различным уровнем привилегий, в Intel был разработан специальный набор дескрипторов, называемый gate descriptors. Существует 4 вида таких дескрипторов:
- Call gates
- Trap gates (для исключений, вроде int 3, требующих выполнения участка кода)
- Interrupt gates (аналогичен trap gates, но с некоторыми отличиями)
- Task gates (полагалось, что будут использоваться для переключения задач)
Нам интересны только call gates, так как именно через них планировалось реализовывать системные вызовы в x86.
Call gate реализован при помощи инструкции call far или jmp far и принимает в качестве параметра call gate-дескриптор, который настраивается ядром ОС. Является достаточно гибким механизмом, так как возможен переход и на любой уровень защитного кольца, и на 16-битный код. Считается, что call gates производительней прерываний. Этот способ использовался в OS/2 и Windows 95. Из-за неудобства использования в Linux механизм так и не был реализован. Со временем совсем перестал использоваться, так как появились более производительные и простые в обращении реализации системных вызовов (sysenter/sysexit).
Системные вызовы, реализованные в Linux
В архитектуре x86-64 ОС Linux существует несколько различных способов системных вызовов:
В реализации каждого системного вызова есть свои особенности, но в общем, обработчик в Linux имеет примерно одинаковую структуру:
- Включается защита от чтения/записи/исполнения кода пользовательского пространства.
- Заменяется пользовательский стек на стек ядра, сохраняются callee-saved регистры.
- Выполняется обработка системного вызова
- Восстановление стека, регистров
- Отключение защиты
- Выход из системного вызова
Рассмотрим немного подробнее каждый системный вызов.
int 80h
Изначально, в архитектуре x86, Linux использовал программное прерывание 128 для совершения системного вызова. Для указания номера системного вызова, пользователь задаёт в eax номер системного вызова, а его параметры располагает по порядку в регистрах ebx, ecx, edx, esi, edi, ebp. Далее вызывается инструкция int 80h, которая программно вызывает прерывание. Процессором вызывается обработчик прерывания, установленный ядром Linux ещё во время инициализации ядра. В x86-64 вызов прерывания используется только во время эмуляции режима x32 для обратной совместимости.
В принципе, никто не запрещает пользоваться инструкцией в расширенном режиме. Но вы должны понимать, что используется 32-битная таблица вызовов и все используемые адреса должны помещаться в 32-битное адресное пространство. Согласно SYSTEM V ABI [4] §3.5.1, для программ, виртуальный адрес которых известен на этапе линковки и помещается в 2гб, по умолчанию используется малая модель памяти и все известные символы находятся в 32-битном адресном пространстве. Под это определение подходят статически скомпилированные программы, где и возможно использовать int 80h. Пошаговая работа прерывания подробно описана на stackoverflow.
В ядре обработчиком этого прерывания является функция entry_INT80_compat и находится в arch/x86/entry/entry_64_compat.S
Или в расширенном режиме (программа работает так как компилируется статически)
sysenter/sysexit
Спустя некоторое время, ещё когда не было x86-64, в Intel поняли, что можно ускорить системные вызовы, если создать специальную инструкцию системного вызова, тем самым минуя некоторые издержки прерывания. Так появилась пара инструкций sysenter/sysexit. Ускорение достигается за счёт того, что на аппаратном уровне при выполнении инструкции sysenter опускается множество проверок на валидность дескрипторов, а так же проверок, зависящих от уровня привилегий [3] §6.1. Также инструкция опирается на то, что вызывающая её программа использует плоскую модель памяти. В архитектуре Intel, инструкция валидна как для режима совместимости, так и для расширенного режима, но у AMD данная инструкция в расширенном режиме приводит к исключению неизвестного опкода [3]. Поэтому в настоящее время пара sysenter/sysexit используется только в режиме совместимости.
В ядре обработчиком этой инструкции является функция entry_SYSENTER_compat и находится в arch/x86/entry/entry_64_compat.S
Несмотря на то, что в реализации архитектуры от Intel инструкция валидна, в расширенном режиме скорее всего такой системный вызов никак не получится использовать. Это из-за того, что в регистре ebp сохраняется текущее значение стека, а адрес верхушки независимо от модели памяти находится вне 32-битного адресного пространства. Это всё потому, что Linux отображает стек на конец нижней половины каноничного адреса пространства.
Разработчики ядра Linux предостерегают пользователей от жесткого программирования sysenter из-за того, что ABI системного вызова может измениться. Из-за того, что Android не последовал этому совету, Linux пришлось откатить свой патч для сохранения обратной совместимости. Правильно реализовывать системный вызов нужно используя vDSO, речь о которой будет идти далее.
syscall/sysret
Так как именно AMD разработали x86-64 архитектуру, которая и называется AMD64, то они решили создать свой собственный системный вызов. Инструкция разрабатывалась AMD, как аналог sysenter/sysexit для архитектуры IA-32. В AMD позаботились о том, чтобы инструкция была реализована как в расширенном режиме, так и в режиме совместимости, но в Intel решили не поддерживать данную инструкцию в режиме совместимости. Несмотря на всё это, Linux имеет 2 обработчика для каждого из режимов: для x32 и x64. Обработчиками этой инструкции является функции entry_SYSCALL_64 для x64 и entry_SYSCALL_compat для x32 и находится в arch/x86/entry/entry_64.S и arch/x86/entry/entry_64_compat.S соответственно.
Кому интересно более подробно ознакомиться с инструкциями системных вызовов, в мануале Intel [0] (§4.3) приведён их псевдокод.
Для тестирования следующего примера потребуется ядро с конфигурацией CONFIG_IA32_EMULATION=y и компьютер AMD. Если же у вас компьютер фирмы Intel, то можно запустить пример на виртуалке. Linux может без предупреждения изменить ABI и этого системного вызова, поэтому в очередной раз напомню: системные вызовы в режиме совместимости правильнее исполнять через vDSO.
Непонятна причина, по которой AMD решили разработать свою инструкцию вместо того, чтобы расширить инструкцию Intel sysenter на архитектуру x86-64.
vsyscall
При переходе из пространства пользователя в пространство ядра происходит переключение контекста, что является не самой дешёвой операцией. Поэтому, для улучшения производительности системных вызовов, было решено их обрабатывать в пространстве пользователя. Для этого было зарезервировано 8 мб памяти для отображения пространства ядра в пространство пользователя. В эту память для архитектуры x86 поместили 3 реализации часто используемых read-only вызова: gettimeofday, time, getcpu.
Со временем стало понятно, что vsyscall имеет существенные недостатки. Фиксированное размещение в адресном пространстве является уязвимым местом с точки зрения безопасности, а отсутствие гибкости в размере выделяемой памяти может негативно сказаться на расширении отображаемой области ядра.
Для того, чтобы пример работал, необходимо, чтобы в ядре была включена поддержка vsyscall: CONFIG_X86_VSYSCALL_EMULATION=y
Linux не отображает vsyscall в режиме совместимости.
На данный момент, для сохранения обратной совместимости, ядро Linux предоставляет эмуляцию vsyscall. Эмуляция сделана для того, чтобы залатать дыры безопасности в ущерб производительности.
Эмуляция может быть реализована двумя способами.
Первый способ — при помощи замены адреса функции на системный вызов syscall. В таком случае виртуальный системный вызов функции gettimeofday на x86-64 выглядит следующим образом:
Где 0x60 — код системного вызова функции gettimeofday.
Второй же способ немного интереснее. При вызове функции vsyscall генерируется исключение Page fault, которое обрабатывается Linux. ОС видит, что ошибка произошла из-за исполнения инструкции по адресу vsyscall и передаёт управление обработчику виртуальных системных вызовов emulate_vsyscall (arch/x86/entry/vsyscall/vsyscall_64.c).
Реализацией vsyscall можно управлять при помощи параметра ядра vsyscall. Можно как отключить виртуальный системный вызов при помощи параметра vsyscall=none , задать реализацию как при помощи инструкции syscall syscall=native , так и через Page fault vsyscall=emulate .
vDSO (Virtual Dynamic Shared Object)
Чтобы исправить основной недостаток vsyscall, было предложено реализовать системные вызовы в виде отображения динамически подключаемой библиотеки, к которой применяется технология ASLR. В «длинном» режиме библиотека называется linux-vdso.so.1, а в режиме совместимости — linux-gate.so.1. Библиотека автоматически подгружается для каждого процесса, даже статически скомпилированного. Увидеть зависимости приложения от неё можно при помощи утилиты ldd в случае динамической компоновки библиотеки libc.
Также vDSO используется в качестве выбора наиболее производительного способа системного вызова, например в режиме совместимости.
Список разделяемых функций можно посмотреть в руководстве.
Для режима совместимости:
Правильнее всего искать функции vDSO при помощи извлечения адреса библиотеки из вспомогательного вектора AT_SYSINFO_EHDR и последующего парсинга разделяемого объекта. Пример парсинга vDSO из вспомогательного вектора можно найти в исходном коде ядра: tools/testing/selftests/vDSO/parse_vdso.c
Или если интересно, то можно покопаться и посмотреть, как парсится vDSO в glibc:
- Парсинг вспомогательных векторов: elf/dl-sysdep.c
- Парсинг разделяемой библиотеки: elf/setup-vdso.h
- Установка значений функций: sysdeps/unix/sysv/linux/x86_64/init-first.c, sysdeps/unix/sysv/linux/x86/gettimeofday.c, sysdeps/unix/sysv/linux/x86/time.c
Согласно System V ABI AMD64 [4] вызовы должны происходить при помощи инструкции syscall. На практике же к этой инструкции добавляются вызовы через vDSO. Поддержка системных вызовов в виде int 80h и vsyscall остались для обратной совместимости.
Сравнение производительности системных вызовов
С тестированием скорости системных вызовов всё неоднозначно. В архитектуре x86 на выполнение одной инструкции влияет множество факторов таких как наличие инструкции в кэше, загруженность конвейера, даже существует таблица задержек для данной архитектуры [2]. Поэтому достаточно сложно определить скорость выполнения участка кода. У Intel есть даже специальный гайд по замеру времени для участка кода [1]. Но проблема в том, что мы не можем замерить время согласно документу из-за того, что нам нужно вызывать объекты ядра из пользовательского пространства.
Поэтому было решено замерить время при помощи clock_gettime и тестировать производительность вызова gettimeofday, так как он есть во всех реализациях системных вызовов. На разных процессорах время может отличаться, но в целом, относительные результаты должны быть схожи.
Программа запускалась несколько раз и в итоге бралось минимальное время исполнения.
Тестирование int 80h, sysenter и vDSO-32 производилось в режиме совместимости.
О системе
cat /proc/cpuinfo | grep «model name» -m 1 — Intel® Core(TM) i7-5500U CPU @ 2.40GHz
uname -r — 4.14.13-1-ARCH
Таблица Результатов
Реализация | время (нс) |
---|---|
int 80h | 498 |
sysenter | 338 |
syscall | 278 |
vsyscall emulate | 692 |
vsyscall native | 278 |
vDSO | 37 |
vDSO-32 | 51 |
Как можно увидеть, каждая новая реализация системного вызова является производительней предыдущей, не считая vsysvall, так как это эмуляция. Как вы наверное уже догадались, если бы vsyscall был таким, каким его задумывали, время вызова было бы аналогично vDSO.
Все текущие сравнения производительности были произведены с патчем KPTI, исправляющим уязвимость meltdown.
Бонус: Производительность системных вызовов без KPTI
Патч KPTI был разработан специально для исправления уязвимости meltdown. Как известно, данный патч замедляет производительность ОС. Проверим производительность с выключенным KPTI (pti=off).
Таблица результатов с выключенным патчем
Реализация | Время (нс) | Увеличение времени исполнения после патча (нс) | Ухудшение производительности после патча (t1 — t0) / t0 * 100% |
---|---|---|---|
int 80h | 317 | 181 | 57% |
sysenter | 150 | 188 | 125% |
syscall | 103 | 175 | 170% |
vsyscall emulate | 496 | 196 | 40% |
vsyscall native | 103 | 175 | 170% |
vDSO | 37 | 0 | 0% |
vDSO-32 | 51 | 0 | 0% |
Переход в режим ядра и обратно в среднем после патча стал занимать примерно на 180 нс. больше времени, видимо это и есть цена сброса TLB-кэша.
Производительность системного вызова через vDSO не ухудшилась по причине того, то в данном типе вызова нет перехода в режим ядра, и, следовательно, нет причин сбрасывать TLB-кэш.
Источник