Процессы операционной системы в большинстве случаев отождествляются с выполняющимися программами, что не совсем верно, точнее — совсем не верно. В современных операционных системах, включая 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
. . . . . . . . . . . . . . .
Выполняющиеся программы являются основными активными сущностями, инструкции которых при помощи механизма системных вызовов потребляют ресурсы, находящиеся под управлением операционной системы. Распределением этих ресурсов и занимаются подсистемы управления процессами, управления памятью и ввода-вывода, в достаточно детальной мере рассмотренные в этой главе.
Основной задачей этих подсистем является организация эффективного распределения ресурсов между массой их потребителей — процессами и нитями.
Фактическая эффективность их работы при прочих равных будет во многом зависеть от понимания пользователем их внутренних алгоритмов и значений конфигурационных параметров этих алгоритмов, в зависимости от характеристик самих потребителей и желаемых результатов. Например, эффективность распределения процессорного времени будет напрямую зависеть от свойств процессов и нитей, их приоритетов, их классов и процессорных привязок.
Кроме того, понимание алгоритмов работы подсистем может ответить на многие вопросы о количестве потребляемых ресурсов и дать ответ об их достаточности или недостатке. Например, важно понимать, что недостаток ресурса оперативной памяти вовсе не определяется суммарными размерами виртуальной памяти, потребленной процессами, а напрямую связан с суммарными размерами их резидентной памяти.
Навыки мониторинга и трассировки потребления ресурсов процессами помогут сделать массу полезнейших выводов о свойствах выполняющихся в них программ, что чрезвычайно полезно при разработке качественного программного обеспечения.
Не лишними эти навыки будут и при выборе качественного программного обеспечения для эксплуатации в заданных условиях и с требуемыми характеристиками.