На примере собранной статистики с утилиты bootchart или подобных можно увидеть количество запускаемых утилит и сервисов при загрузке системы: www.bootchart.org/samples.html
Каждый запускаемы бинарник, обычно динамически, слинкован минимум с glibc, и другими ему нужными библиотеками. По правильной схеме, приложение открывает библиотеку через вызов dlopen(),
handle = dlopen("libm.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); }
Дальше подключаются функции из libdl.so и поиск нужно библиотеки идет с учетом строк в файле /etc/ld.so.conf, /etc/ld.so.preload, переменных LD_LIBRARY_PATH и LD_PRELOAD и в кэше /etc/ld.so.cache, и некоторых других, зависящих от системы и glibc, условий. Напомню, ld.so.cache формируется утилитой ldconfig, с учетом путей указанных в /etc/ld.so.conf. Порядок обхода путей поиска совпадает с последовательностью, указанной в этом файле.
Так вот, сборщики пакетов конечно соблюдают эти правила, но на своих рабочих компьютерах вынуждены использовать разные библиотеки, для разных архитектур, дистрибутивов и просто для тестирования. По этой причине в рабочих дистрибутивах мы получим что-то вроде:
# strace -e open ps open("/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib64/tls/x86_64/libprocps.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/tls/libprocps.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libprocps.so.0", O_RDONLY|O_CLOEXEC) = 0 open("/usr/lib64/tls/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/tls/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/librt.so.1", O_RDONLY|O_CLOEXEC) = 0
… итд.
Как видите, библиотеки libprocps и librt нашлись только с третьего раза, выделил жирным. (… почему glibc не использует комбинацию stat+open, не ясно, но не об этом …).
Уже наверно догадались, что для поиска подобных ошибок, мы будем использовать утилиту strace. Не вдаваясь в подробности, strace — отслеживает системные вызовы вызываемой исследуемой программой. У неё есть два, нас интересующих параметра -e c аргументом, имени интересующего нас системного вызова, и флаг -f — отслеживающий системные вызовы у дочерних процессов.
(-ff и -o, но оних позже).
1. Библиотеки
Сама оптимизация, если это можно так назвать, заключается в подсовывании нужных каталогов нашим бинарникам. Плодить копии одних и тех же библиотек мы не будем, а просто создадим ссылки с нужными на каталоги или на файлы с нужными на библиотеками. Из примера выше, мы видим, что приложение в первую очередь ищет в каталоге /usr/lib64/tls/x86_64, которого у нас нет. Нужно его создать, но только на один уровень меньше, т.е /usr/lib64/tls, а в нем ссылку x86_64 на каталог /usr/lib64:
# mkdir /usr/lib64/tls/ # ln -s /usr/lib64/tls/x86_64 /usr/lib64
посмотрим результат
# ls -l /usr/lib64/tls/x86_64 /usr/lib64/tls/x86_64 -> /usr/lib64
Запустим ещё раз проверку
$ strace -e open ps open("/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib64/libprocps.so.0", O_RDONLY|O_CLOEXEC) = 0 open("/usr/lib64/librt.so.1", O_RDONLY|O_CLOEXEC) = 0
Прекрасно, минус четыре системных вызова и это только с двух библиотек и одного бинарника.
2. Локали.
Аналогичная ситуация с файлами локализаций и локальными настройками LC_CTYPE, LC_MESSAGES и остальные. Эта бяда тянется ещё с лохматых 90-х, по крайней мере в SuSE. Данные файлы и настройки должны лежать в каталоге $LOCALES/$USER_LOCALE (имена условные).
LOCALES — это общая свалка для всех поддерживаемых локалей. В реальности это переменная I18NPATH (у кого в дистрибутиве она установлена? 🙂 (используемые каталоги локалей можно посмотреть командой localedef —help )
USER_LOCALE — это пользовательская настройка, которую можно узнать командой locale.
$ locale LANG=ru_RU.UTF-8 LC_CTYPE=ru_RU.UTF-8 LC_MESSAGES=ru_RU.UTF-8 …
Приложение, если поддерживает локализацию вызывает нужные функции из glibc, которые ищут эти файлы в забавном, но логичном, инкрементальном порядке.
$ strace -e open ps open("/usr/lib/locale/ru_RU.UTF-8/LC_MESSAGES", O_RDONLY|O_CLOEXEC) = -1 open("/usr/lib/locale/ru_RU/LC_MESSAGES", O_RDONLY|O_CLOEXEC) = -1 open("/usr/lib/locale/ru/LC_MESSAGES", O_RDONLY|O_CLOEXEC) = 3
То есть, сначала в /usr/lib/locale/ru_RU.UTF-8, потом в /usr/lib/locale/ru_RU, затем в /usr/lib/locale/ru
Исправляем по предыдущей схеме — создаём ссылку /usr/lib/locale/ru_RU.UTF-8 на каталог /usr/lib/locale/ru
# ln -s /usr/lib/locale/ru_RU.UTF-8 /usr/lib/locale/ru;
и скажем glibc, что мы используем ru_RU.UTF-8, а не ru.
(кто не знает как исправить в случае глюков, лучше не делайте)
# localedef -f UTF-8 -i ru_RU ru_RU.UTF-8;
и проверим:
$ strace -e open ps open("/usr/lib/locale/ru_RU.UTF-8/LC_MEASUREMENT", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/ru_RU.UTF-8/LC_TELEPHONE", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/ru_RU.UTF-8/LC_ADDRESS", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/ru_RU.UTF-8/LC_NAME", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/ru_RU.UTF-8/LC_PAPER", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/ru_RU.UTF-8/LC_MESSAGES", O_RDONLY|O_CLOEXEC) = 3 …
замечательно, минус по два open на каждый параметр!
3. Пользовательские и графические приложения.
Начнём с классики — xterm. Работает в полном соответствии с POSIX, LSB, X/Open, IEEE, ISO, ГОСТ, DIN 🙂 Лезет в shared library, в локали, локали Xorg и далее специфичные для Xorg/Xfree86 конфиги. Опять же без вникания в суть, Xorg дублирует конфиги, точнее их имена: Но в отличии от glibc, Xorg иногда использует функцию stat()
$ strace -e open,stat xterm # (через запятую - список нужны сискалов) stat("/home/pavel/XTerm", {st_mode=S_IFREG|0600, st_size=333, ...}) = 0 open("/home/pavel/XTerm", O_RDONLY) = 4 open("/usr/share/X11/app-defaults/XTerm", O_RDONLY) = 4
Первый файл это мои настройки, второй — общесистемный.
Тут медаль о двух сторонах, независимо от наличия или отсутствия моего файла, мы получим два системных вызова, но один из них менее ресурсоёмкий — stat(). Получается, если у Вас нет каких-то специфичных настроек для xterm, то лучше выкинуть такие файлы из домашней папки. Это могу быть .Xdefaults-$(hostname), .Xdefaults, .terminfo (файлы со словами auth, лучше не трогать :))
Похожая, но отличная ситуация с курсорами мыши. Если приложение, а это почти все графические, используют библиотеку libXcursor, значит оно будет искать курсоры и иконки сначала в $HOME/.icons/ и затем в /usr/share/icons
$ strace -e open,stat xterm open("/home/pavel/.icons/DMZ/cursors/top_left_arrow", O_RDONLY) = -1 ENOENT (No such file or directory) open("/home/pavel/.icons/DMZ/index.theme", O_RDONLY) = -1 ENOENT (No such file or directory) open("/usr/share/icons/DMZ/cursors/top_left_arrow", O_RDONLY) = 5 open("/home/pavel/.icons/DMZ/cursors/sb_v_double_arrow", O_RDONLY) = -1 ENOENT (No such file or directory) open("/home/pavel/.icons/DMZ/index.theme", O_RDONLY) = -1 ENOENT (No such file or directory) open("/usr/share/icons/DMZ/cursors/sb_v_double_arrow", O_RDONLY) = 5
Просто делаем ссылку с /usr/share/icons на $HOME/.icons и проверяем:
$ ln -s /usr/share/icons $HOME/.icons $ strace -e open,stat xterm open("/home/pavel/.icons/DMZ/cursors/top_left_arrow", O_RDONLY) = 5 open("/home/pavel/.icons/DMZ/cursors/sb_v_double_arrow", O_RDONLY) = 5
Великолепно, минус 4 опена()!
Далее добавим к strace флаг -f, для отслеживания дочерних процессов.
$ strace -f -e open,stat xterm
Вывод очень громоздкий, оставляю вам для изучения в качестве домашнего задания. Скажу лишь, что для многонитиевых приложений лучше делать вывод в файл или даже в отдельные файлы для каждой «дочки».
$ mkdir /tmp/Xterm (Отдельный каталог для чистоты феншуя) $ cd /tmp/Xterm $ strace -o Xterm.trace -ff xterm $ ls Xterm.trace.6001 Xterm.trace.6009 Xterm.trace.6017 Xterm.trace.6025 Xterm.trace.6034 Xterm.trace.6042 Xterm.trace.6050 Xterm.trace.6002 Xterm.trace.6010 Xterm.trace.6018 Xterm.trace.6026 Xterm.trace.6035 Xterm.trace.6043 Xterm.trace.6051 Xterm.trace.6003 Xterm.trace.6011 Xterm.trace.6019 Xterm.trace.6027 Xterm.trace.6036 Xterm.trace.6044 Xterm.trace.6052 Xterm.trace.6004 Xterm.trace.6012 Xterm.trace.6020 Xterm.trace.6028 ...
изучайте 🙂
все сразу
$ egrep "open|stat" ./Xterm.trace.* | grep --color " = -"
или по одному
$ egrep "open|stat" ./Xterm.trace.6022 | grep --color " = -"
Даже сейчас, нашел, что кто-то упорно ищет libreadline.so.6
./Xterm.trace.28465:open("/usr/lib64/tls/x86_64/libreadline.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) ./Xterm.trace.28465:open("/usr/lib64/tls/libreadline.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) ./Xterm.trace.28465:open("/usr/lib64/x86_64/libreadline.so.6", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied) ./Xterm.trace.28465:open("/usr/lib64/libreadline.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
В данном случае косяк в том, что программеры указали явно (специально или через парсер) версию библиотеки. Вместо libreadline.so, захаркодили libreadline.so.6. Исправляем.
$ cd /usr/lib64/tls/x86_64/ # ln -s libreadline.so libreadline.so.6 $ cd /tmp/Xterm && rm ./*; $ strace -o Xterm.trace -ff xterm $ egrep "open|stat" ./Xterm.trace.* | grep --color "libreadline" ./Xterm.trace.21278:open("/usr/lib64/tls/x86_64/libreadline.so.6", O_RDONLY|O_CLOEXEC) = 3
Ура!
Заключение
Для оптимизации KDE, GNOME, Xfce приложений не обязательно настраивать сами менеджеры gdm/kdm/xfwm4, достаточно любого приложения из набора к вашему десктопу, схема работы практически идентичная.
Займитесь в первую очередь всеми бинарниками запускаемыми при загрузке.
Таким способом время загрузки компьютера можно легко уменьшить до 10-15 сек. Время загрузки — это от загрузчика до открытия страницы в фейсбуке, а не появление обоины на экране, как думает Microsoft и Поттеринг.
Потом можно взяться за большие программы Google Chrome, Firefox, Gimp, LibreOffice, etc.
Возможные косяки
Как и в любом деле — важно знать меру, создание ссылок на каталоги может привести к «зацикленным каталогам» и вызов, скажем find ./, может устроить локальный DoS системе. (вроде в ядре уже исправили, но тем не менее ). Года два назад, на SuSE были проблемы при обновлении glibc. Дополнительно рекомендую почитать про TLS (Thread Local Storage)
ссылка на оригинал статьи http://habrahabr.ru/post/192264/
Добавить комментарий