Программы и библиотеки Linux

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

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

Различают машинный язык, понятный центральному процессору, и языки более высоких уровней (алгоритмические), понятные составителю программы — программисту.

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

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

Например, командный интерпретатор при интерактивном режиме пошагово, выполняет команды, вводимые пользователем, а в пакетном режиме так же пошагово выполняет команды, записанные в файле сценария.

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

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

Поэтому различают последовательные и параллельные алгоритмы и соответствующие им последовательные и параллельные программы.

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

Согласно hier, откомпилированные до машинного языка программы размещаются в каталогах /bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin, /usr/local/sbin, а библиотеки — в каталогах /lib, /usr/lib, /usr/local/lib.

Программы имеют специальный бинарный «запускаемый» формат W:[ELF] executable и зависят от библиотек, что проиллюстрировано в листинге ниже при помощи команды ldd (loader dependencies). Каждая зависимость отображается именем библиотеки  (SONAME, shared object name), Найденным в системе файлом библиотеки и адресом (для противодействия эксплуатации уязвимости в программах адрес выбирается случайным образом, см. W:[ASLR]). в памяти процесса (32- или 48-битным, в зависимости от платформы), куда библиотека будет загружена.

Программы и библиотеки

john@ubuntu:~$ which ls

/bin/ls

john@ubuntu:~$ file /bin/ls

/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[shal]=0x83531f308flfal8221be53eaf399303400cl4638, stripped

john@ubuntu:~$ ldd /bin/ls

linux-gate.so.l => (0xb7744000)

libselinux.so.l => /lib/i386-linux-gnu/libselinux.so.1 (0xb7708000) librt.so.l => /lib/i386-linux-gnu/librt.so.l (0xb76ff000) libacl.so.l => /lib/i386-linux-gnu/libacl.so.l (0xb76f5000)

libc.so.6 =>  /lib/1386-linux-gnu/libc. so.6 (0xb754b000)

libdl.so,.2 => /lib/i386- linux — gnu/libdl. so. 2 (0xb7546000)

/lib/ld-linux.so.2 (0xb7745000)

libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xb752b000) libattr.so.l => /lib/i386-linux-gnu/libattr.so.l (0xb7525000)

john@ubuntu:~$ file lib/i386-linux-gnu/libc.so.6 

/lib/i386-linux-gnu/libc.so.6: symbolic link to ‘libc-2.15.so’

Нужно заметить, что файла библиотеки linux-gate.so.1 (реализующей интерфейс системных вызовов к ядру) не существует, т. к. она является виртуальной (VDSO, virtual dynamic shared object), т. e. предоставляется и отображается в память процесса самим ядром, «как будто» является настоящей библиотекой.

Кроме того, библиотека ld-llnux.so.2 указана абсолютным путевым именем, поэтому поиск ее файла не производится.

Для большинства библиотек зависимость устанавливается при помощи SONAME вида libNAME.so.x где lib — стандартный префикс (библиотека), .so — суффикс (разделяемый объект), NAME — имя «собственное», а .X — номер версии ее интерфейса. По имени SONAME в определенных (конфигурацией компоновщика —  ld.so и ldconfig) каталогах производится поиск одноименного файла библиотеки, который на самом деле оказывается символической ссылкой  на «настоящий» файл библиотеки.

Например, для 6-й версии интерфейса динамической библиотеки языка с (libc.so.6) настоящий файл библиотеки называется libc-2.15.so, что указывает на версию самой библиотеки как 2.15.

Версии библиотек

john@ubuntu:~$ file /lib/1386-linux-gnu/libacl.so. 1

/lib/1386-linux-gnu/libacl.so.1: symbolic link to ‘libacl.so.1.1.0’

Аналогично, в листинге выше показано, что для 1-й версии интерфейса динамической библиотеки списков контроля доступа acl (libacl.so. 1) настоящий файл библиотеки называется libacl.so.1.1.0, а это указывает на версию самой библиотеки как 1.1.0.

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

При обновлении библиотеки libc-2.15.so, например, до libc-2.i8.so достаточно установить символическую SONAME-ссылку libc.so.6 на libc-2.18.so, в результате чего ее начнут использовать все программы с зависимостями от libc.so.6.

Более того, в системе может быть одновременно установлено любое количество версий одной и той же библиотеки, реализующих одинаковые или разные версии интерфейсов, выбор которых будет указан соответствующими SONAME-ссылками.

Библиотеки- это незапускаемые программы

john@ubuntu:~$ file /lib/i386-linux-gnu/libc-2.15.so

/lib/i386-linux-gnu/libc-2.15.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs),

BuildID[shal]=0x87bb99bc34Ofl345950611a3d9cfeclcb49532dc, for GNU/Linux 2.6.24, stripped

john@ubuntu:~$: file /llb/i386-linux-gnu/libacl.so.1.1.0

/llb/1386-llnux-gnu/llbacl.so.1.1.0: ELF 32-blt LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BulldID[shal]=0xd48c066c7c7deba7c88505fe434d4601e3e91f50, stripped

john@ubuntu:~$: ldd /llb/l386-llnux-gnu/llbacl.so.1.1.0

linux-gate.so.1 => (0xb76fd000)

libattr.so.l => /lib/1386-linux-gnu/llbattr.so.1 (0xb76ca000)

libc.so.6 => /lib/i386-ltnux-gnu/libc.so.6 (0xb7520000)

/lib/ld-linux.so.2 (0xb76fe000)

Библиотеки имеют тот же бинарный формат W:[ELF], что и «запускаемые» программы, но не «запускаемый» executable, а «совместно используемый» shared object.

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

Практически, «запускаемость» ELF-файлов  зависит не от их типа, а от прав доступа и осмысленности точки входа адреса первой инструкции, которой передается управление при попытке, запуска. Например, библиотеку libc-2.15.so можно запустить, в результате чего будет выведена статусная информация.

 Запускаемые библиотеки

john@ubuntu:~$ Is -l lib/l386-linux-gnu/libc-2.15.so

-rwxr-xr-x 1 root root 1730024 окт. 6 2018 /lib/i386-linux-gnu/libc-2.15.so

john@ubuntu:~$ lib/l386-linux-gnu/libc-2.15.so

GNU C Library (Ubuntu EGLIBC 2.15-0ubuntu10.3) stable release version 2.15, by Roland McGrath et al.

. . .                        . . .                               . . .                                   . . .                                    . . .

Available extensions:

crypt add-on version 2.1 by Michael Glad and others GNU Libidn by Simon Josefsson

★ Native POSIX Threads Library by Ulrich Drepper et al

BIND-8.2.3-T5B

. . .                        . . .                               . . .                                   . . .                                    . . .

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

Основной задачей этих подсистем является организация эффективного распределения ресурсов между массой их потребителей — процессами и нитями.

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

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

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

Не лишними эти навыки будут и при выборе качественного программного обеспечения для эксплуатации в заданных условиях и с требуемыми характеристиками.

Добавить комментарий