Посторонним вход воспрещен
Тебе никогда не приходила в голову мысль о том, что идея использовать логин и пароль для входа в систему несколько устарела? Почему, имея в одном кармане флешку, в другом — телефон, а на компе установленный SSH с настроенной авторизацией при помощи ключей, мы продолжаем вводить эти запутанные пароли?
Пароли и учетные записи были придуманы в эпоху больших мейнфреймов, движения хиппи, войны в Афганистане и больших аналоговых магнитофонов, самым технологичным компонентом из которых были транзисторы. В те времена использование паролей для входа в мейнфрейм выглядело действительно круто. Можно было придумать комбинацию вроде sexhero или iamsuperman и действительно гордиться собой.
Сегодня же пароли выглядят не только архаичной, но и ужасно неудобной вещью, старые понтовые комбинации уже не работают, и вместо них приходится придумывать зубодробительные наборы символов, которые не только нереально запомнить, но и проблематично ввести с первого раза. Мы живем в XXI веке, имеем доступ к огромному количеству гаджетов и технических средств, которые предлагают гораздо более простой и качественный механизм аутентификации, но продолжаем вбивать пароли, матерясь на всю квартиру при каждой неудачной попытке войти в систему. Пора это исправить.
В этой статье я расскажу о том, как раз и навсегда избавиться от паролей и превратить свой комп в по-настоящему технологичное устройство, для входа в которое будет достаточно вставить флешку, взглянуть в веб-камеру или просто положить на стол сотовый телефон.
Высокотехнологичный ключ
Ключ — самый простой и эффективный метод защиты чего бы то ни было. Мы пользуемся ключами ежедневно: для включения двигателя автомобиля, для входа в квартиру, для отпирания ящиков и шкафов. Ключи удобны и просты в изготовлении, благодаря современным программируемым замкам их не страшно потерять. Множество раз человечество пыталось придумать замену ключам, но все попытки провалились (все мы помним, к чему привела идея использовать кодовые замки на подъездных дверях). Почему же мы не используем столь хорошую и проверенную временем технологию для защиты компьютеров?
Современный аналог ключа — USB-флешка. Вне зависимости от того, для каких целей ты обычно используешь флешку, из нее всегда можно сделать полноценный высокотехнологичный ключ, с помощью которого войти в систему будет так же легко, как отпереть дверь в квартиру.
Есть несколько способов сделать это, но наиболее простой и универсальный метод — это использовать PAM-модуль pam_usb, который будет проверять каждую вставленную в комп флешку на предмет ее соответствия указанным требованиям и, в зависимости от результата, разблокировать или блокировать учетную запись пользователя.
Никакой модификации таблицы разделов или информации, хранимой на флешке, при этом не потребуется. Для идентификации «правильной» флешки используется ее серийный номер, модель, производитель, а также набор случайных данных, которые записываются в резервную область флешки и изменяются при каждой удачной аутентификации (если кто-то скопирует твою флешку, но ты успеешь войти в систему раньше злоумышленника, данные будут изменены, и его копия уже не сработает). В случае утери всегда останется возможность войти в систему, используя пароль, и перекодировать pam_usb на новую флешку. Также в качестве ключа можно использовать различные карты памяти (SD, MMC) и другие съемные накопители.
Начать использовать pam_usb довольно просто. Полная настройка системы состоит из пяти шагов.
1. Ставим библиотеку libpam_usb.so и утилиты управления модулем:
$ sudo apt-get install libpam-usb pamusb-tools
2. Берем флешку, которую собираемся использовать в качестве ключа, вставляем ее в USB-порт и выполняем следующую команду:
$ sudo pamusb-conf —add-device имя
Так pam_usb соберет всю необходимую информацию о флешке, добавит в свою базу данных и запишет 2 Кб случайных данных. Для поиска флешки в системе будет использован Udisks (аргумент «имя» здесь используется, чтобы дать флешке произвольное название, а не для указания ее файла-устройства), так что важно, чтобы другие внешние накопители на время работы этой команды были отключены.
3. Теперь даем pam_usb понять, чтобы эта флешка была ассоциирована с нужной нам учетной записью (путь это будет vasya):
$ sudo pamusb-conf —add-user vasya
4. Запускаем проверку правильности собранных данных на случай, если флешка не была корректно идентифицирована. Или мы забыли отсоединить какой-то другой накопитель, и он был использован вместо нужного нам:
$ sudo pamusb-check vasya
5. Добавляем pam_usb в список модулей, необходимых для проведения успешной аутентификации пользователя. В Ubuntu и других дистрибутивах, основанных на Debian, это делается с помощью модификации файла /etc/pam.d/common-auth. В нем необходимо найти строку примерно следующего вида (она может отличаться): auth required pam_unix.so . И прямо перед ней добавить следующую строку: auth sufficient pam_usb.so .
Так мы сообщим PAM, что перед логином любого пользователя нужно отдавать управление модулю pam_usb, который проверит наличие нужной флешки, и лишь в случае неудачи этой операции запрашивать пароль. Поэтому, если ты хочешь впускать пользователя только по флешке, полностью блокируя аккаунт в случае неудачи, слово «sufficient» следует заменить словом «required».
В принципе, всего этого должно быть достаточно для того, чтобы система просто работала (попытайся выйти и войти, чтобы это проверить), однако функциональность pam_usb можно несколько расширить, если использовать демон pamusb-agent.
Задача pamusb-agent — автоматизировать работу по блокированию и разблокированию учетной записи пользователя при извлечении и втыкании флешки в комп. Чтобы агент заработал, необходимо добавить в конфигурационный файл /etc/pamusb.conf следующие строки:
Это рецепт для Gnome. Чтобы использовать pamusb-agent с другими средами, команды «gnome-screensaver-command —lock» и «gnome-screensaver-command —deactivate» необходимо изменить.
Теперь можно запустить pamusb-agent и проверить его работоспособность:
Если все работает нормально, можно добавить его в автозапуск:
$ cd ~/.config/autostart
$ ln -s /usr/bin/pamusb-agent pamusb-agent
Обкатаем пальчики?
Модуль pam_usb удобно использовать в качестве метода защиты ноутбуков, оснащенных кард-ридером. Можно носить небольшую SD-карту в кошельке или внутреннем кармане и втыкать ее в ноутбук, не беспокоясь о том, что она будет мешать (как это происходит в случае с USB-флешкой). Однако этот подход будет выглядеть несколько архаично, если ноутбук уже оснащен сканером для снятия отпечатков пальцев.
Ноутбуки со сканером отпечатков пальцев выпускают многие производители. Как правило, они не намного дороже других сходных по характеристикам моделей, однако их сенсор отпечатков работает только в Windows. Для устранения этого недостатка freedesktop.org запустил проект fprint, в рамках которого разработана открытая реализация библиотеки для распознавания отпечатков и соответствующий PAM-модуль, позволяющий задействовать возможности библиотеки во время логина пользователя и других манипуляций над аккаунтом.
Сегодня libfprint есть в любом дистрибутиве, поэтому установить его можно с помощью любого пакетного менеджера:
$ sudo apt-get install libfprint0 libpam-fprint fprint-demo
Далее сканер можно проверить с помощью специальной демонстрационной программы с графическим интерфейсом:
Если все работает корректно и без сбоев, можно начать настройку аутентификации. Для этого следует запустить программу pam_fprint_enroll, которая позволит сделать эталонный слепок отпечатка пальца, который затем будет использован для идентификации его владельца:
# pam_fprint_enroll –enroll-finger 7
Цифра 7 здесь означает указательный палец правой руки. Система fprint нумерует пальцы слева направо, так что цифрой 1 будет обозначен мизинец левой руки, а 10 — мизинец правой.
Когда слепок будет готов, добавим модуль pam_fprint в стек PAM-модулей всех приложений, для этого открываем файл /etc/pam.d/common-auth, находим все ту же строку «auth required pam_unix.so» и добавляем прямо перед ней строку, отвечающую за загрузку pam_fprint: auth sufficient pam_fprint.so .
При следующем логине все должно заработать.
Добавляем модули pam_usb и pam_fprint в стек PAM
Память на лица
Пальцы — не единственное, что отличает людей друг от друга. У всех нас разные лица, поэтому для идентификации пользователя система может использовать снимок лица, сделанный веб-камерой. Это не особо секьюрно, так как атакующий может показать камере обычную фотографию, отпечатанную на бумаге, но произведет очень сильное впечатление на друзей и знакомых.
В Linux-дистрибутивах нет встроенных средств распознавания лиц, однако их можно добавить с помощью установки комплекта ПО под названием pam-face-authentication, который включает в себя библиотеку, реализующую алгоритм распознавания лиц, PAM-модуль для осуществления аутентификации и приложение для генерирования эталонного снимка.
Все это можно собрать из исходников или же установить из сторонних репозиториев в Ubuntu. Так как проект еще сырой, и пакеты подготовлены не для всех дистрибутивов, мы рассмотрим оба варианта установки. Итак, для установки из исходников нам понадобятся пакеты с компилятором, линковщиком и заголовочными файлами для всех зависимостей. В Ubuntu (да и в других дистрибах) их можно установить, выполнив одну команду:
$ sudo apt-get install build-essential cmake qt4-qmake libx11-dev libcv-dev libcvaux-dev libhighgui4 libhighgui-dev libqt4-dev libpam0g-dev
Далее скачиваем исходники со страницы проекта и распаковываем:
$ cd
$ wget goo.gl/dpD1s
$ tar -xzf pam-face-authentication-0.3.tar.gz
Для сборки используется cmake, поэтому здесь все просто:
$ cd pam-face-authentication-0.3
$ cmake && make
$ sudo make install
Для установки уже прекомпилированного пакета в Ubuntu можно использовать репозиторий antonio.chiurazzi:
$ sudo add-apt-repository ppa:antonio.chiurazzi/ppa
$ sudo apt-get update
$ sudo apt-get install pam-face-authentication
После окончания установки запускаем обучающую программу:
Вертим лицом перед камерой, постоянно нажимая кнопку «Capture». Важно сделать хотя бы десяток фотографий, чтобы система научилась распознавать твое лицо под любым углом. Также будет неплохо сделать фотографии при разной освещенности. Не забываем протестировать работу системы.
Теперь добавим модуль pam_face_authentication.so в стек загружаемых PAM-модулей. Для этого открываем файл /etc/pam.d/gdm или /etc/pam.d/kdm (если ты пользуешься KDE) и добавляем в его начало следующую строку: auth sufficient pam_face_authentication.so enableX /
Файл /etc/pam.d/common-auth изменять не надо, так как он используется не только графическими менеджерами входа в систему, но и стандартными консольными /bin/login и /bin/su, а pam_face требует доступа к иксам.
Также необходимо создать профиль для нового PAM-модуля. Открываем (создаем) файл /usr/share/pam-configs/face_authentication следующего содержания:
Name: Manually installed face_authentication profile
Default: yes
Priority: 900
Auth-Type: Primary
Auth:
[success=end default=ignore] pam_face_authentication.so enableX
И активируем его:
$ sudo pam-auth-update —package face_authentication
Ключ из телефона
Сотовый телефон — символ 21-го века. Мы давно привыкли к тому, что его можно использовать не только для звонков, но и для выхода в интернет, игр, прослушивания музыки, просмотра видео и даже оплаты счетов. Но можно ли использовать его как ключ для входа в компьютерную систему?
Конечно, да. Как и USB-флешка, телефон имеет множество признаков, которые делают его уникальным. Это все те же идентификаторы производителя и модели, серийный номер, MAC-адреса, IMEI, в конце концов. Любой из них можно использовать для однозначной идентификации устройства и его владельца, но мы остановимся только на одном из них — MAC-адресе Bluetooth-интерфейса.
Любой, даже очень древний и простой телефон имеет поддержку протокола Bluetooth и, как следствие, уникальный MAC-адрес, который передает в сеть в ответ на любой запрос поиска устройств другим Bluetooth-адаптером. Многие современные ноутбуки имеют на борту такой адаптер, а его внешний USB’шный вариант стоит копейки, так что для нас синий зуб будет идеальным вариантом для настройки беспарольной и беспроводной аутентификации. Зашел в комнату — доступ открыт, вышел — система заблокирована.
Берем телефон, включаем Bluetooth, делаем так, чтобы он был «видим» другим устройствам. Садимся за комп и запускаем утилиту hcitool (входит в пакет bluez-utils) в режиме поиска устройств:
Получаем имя своего устройства и его MAC-адрес, копируем последний в буфер обмена. Устанавливаем пакет libpam_blue (или pam_blue, где как):
$ sudo apt-get install libpam_blue
Создаем файл конфигурации /etc/security/bluesscan.conf и пишем в него следующее:
# Общие настройки
general # Продолжительность сканирования в секундах (от 3 до 15)
timeout = 15;
>
# Настройки пользователей и их устройств
mylogin = name = Имя устройства;
bluemac = MAC-адрес устройства;
>
Сохраняем файл, открываем уже знакомый нам конфиг /etc/pam.d/common-auth и добавляем строку auth sufficient pam_blue.so перед строкой, содержащей «pam_unix.so».
Теперь для входа в систему будет достаточно положить телефон рядом с ноутом и ввести имя. Далее управление будет передано модулю pam_blue, который просканирует сеть, найдет MAC-адрес телефона и впустит пользователя. В противном случае придется ввести пароль.
Выводы
Настроить беспарольную аутентификацию с помощью альтернативных методов в Linux довольно просто. Для этого не нужно быть матерым гиком, уметь писать код или иметь глубокие познания в области безопасности, все делается за несколько минут и работает поразительно эффективно. Ты можешь сказать, что большинство из этих методов потенциально небезопасны и легко обходятся, но перед тем как это сделать, задумайся о том, насколько безопасны обычные пароли.
Безопасность машины, к которой могут получить доступ сторонние люди — чистой воды миф. Обойти стандартную защиту паролем проще простого. Есть огромное количество способов сделать это, и все они известны даже детям. Пароль — это лишь небольшой указательный знак, несущий информацию о том, что у компа есть владелец, и он не хочет видеть непрошенных гостей. Применив методы, описанные в статье, мы не сделаем систему более уязвимой, но сможем сделать свою жизнь проще и удобнее.
Аналог файла /etc/pam.d/common-auth в Gentoo и Mandriva носит имя /etc/pam.d/system-auth, во FreeBSD вместо него используется /etc/pam.d/system. В ArchLinux придется править PAM-конфиги отдельно для каждого приложения.
Еще один способ обезопасить машину от вторжения — удаленно создать файл /etc/nologin с помощью любого мобильного SSH-клиента. Для разблокировки придется войти как root и удалить этот файл.
Журнал Хакер, Сентябрь (09) 152
Евгений Зобнин (execbit.ru).
- 1 999 р. за 12 номеров бумажного варианта
- 1249р. за годовую подписку на iOS/iPad (релиз Android’а скоро!)
- «Хакер» на Android
Процессы и память в Linux. Отрывок из книги «Внутреннее устройство Linux»
Процессы операционной системы в большинстве случаев отождествляются с выполняющимися программами, что не совсем верно, точнее — совсем неверно. В современных операционных системах, включая Linux, между программой и процессом есть очевидная взаимосвязь, но далеко не такая непосредственная, как кажется на первый взгляд.
О книге
Совсем недавно в издательстве БХВ вышло второе издание книги Дмитрия Кетова «Внутреннее устройство Linux». Я прочел предоставленный издательством экземпляр и сделал свои выводы о качестве книги, ее плюсах и целевой аудитории. В придачу мы публикуем отрывок книги, который позволит тебе составить собственное мнение.
Несмотря на громкое название, эта книга — не об устройстве Linux. Из нее ты не узнаешь, как работает система управления виртуальной памятью ядра Linux или файловая система Btrfs. Эта книга — скорее учебник «GNU/Linux для продолжающих», то есть тех, кто понял, что такое командный интерпретатор и права доступа, но хотел бы копнуть глубже и понять, почему этот интерпретатор именно такой, с какой целью появился инструмент sudo и почему, несмотря на весь хейт, менеджер Systemd стал стандартом.
Главное достоинство книги — грамотное балансирование между теорией и практикой. Автор сопровождает текст большим количеством исторических справок и деталей, которые бывают незнакомы даже мне, умудренному двадцатилетним опытом линуксоиду. При этом практически каждое высказывание автора сопровождается примером, поясняющим теорию. Хочешь увидеть, как работают управляющие последовательности терминала? Вот тебе листинг команд с отметками, где, что и как надо нажать.
Второй плюс — полнота информации. Несмотря на массу подробностей о реализации компонентов типичного дистрибутива и сравнительно небольшой объем, книга охватывает практически все, что может быть интересно пользователю, начиная от эмулятора терминала и командного интерпретатора и заканчивая графической системой Wayland, которая только готовится занять место стандартной в дистрибутивах Linux, планировщиком BFQ и контейнерной системой Docker, о которых ты не узнаешь из более «академической» и «проверенной временем» литературы.
В целом эта книга для тех, кто стремится стать тем самым гуру Linux, который не только сможет рассказать, как удалить «неудаляемый» файл или исправить терминал после вывода в него бинарного файла, но и будет понимать, почему эти инструкции работают. Но это точно книга не для тех, кто хотел бы разобраться, как работают операционные системы на уровне ядра.
info
На сайте издательства можно купить эту книгу со скидкой 20% по промокоду linuxakep. Промокод следует ввести на этапе оформления заказа, нажав на ссылку «У вас есть купон? Нажмите здесь для введения кода».
Программы и библиотеки
Программа представляет собой алгоритм, записанный на определенном языке, понятном исполнителю программы. Различают машинный язык, понятный центральному процессору, и языки более высоких уровней (алгоритмические), понятные составителю программы — программисту.
Программы, составленные на языке высокого уровня, в любом случае перед исполнением должны быть транслированы (переведены) на язык исполнителя, что реализуется при помощи специальных средств — трансляторов. Различают два вида трансляторов программ — компиляторы и интерпретаторы. Компилятор транслирует в машинный код сразу целиком всю программу и не участвует в ее исполнении. Интерпретатор, наоборот, пошагово транслирует отдельные инструкции программы и немедленно выполняет их. Например, командный интерпретатор при интерактивном режиме пошагово выполняет команды, вводимые пользователем, а в пакетном режиме так же пошагово выполняет команды, записанные в файле сценария.
Алгоритм, в свою очередь, есть некоторый набор инструкций, выполнение которых приводит к решению конкретной задачи. В большинстве случаев инструкции алгоритма имеют причинно‑следственные зависимости и выполняются исполнителем последовательно. Однако если выделить «независимые» поднаборы инструкций (независимые ветви), то их можно выполнять несколькими исполнителями одновременно — параллельно. Поэтому различают последовательные и параллельные алгоритмы и соответствующие им последовательные и параллельные программы. Некоторые программы реализуют алгоритмы общего назначения, например алгоритмы сжатия или шифрования информации, алгоритмы сетевых протоколов и т. д. Такие программы, востребованные не столько конечными пользователями, сколько другими программами, называют библиотеками.
Согласно 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), найденным в системе файлом библиотеки ❷ и адресом в памяти процесса ❸ (32- или 48-битным, в зависимости от платформы), куда библиотека будет загружена.
Программы и библиотеки
fitz@ubuntu:~$ which ls
/usr/bin/ls
fitz@ubuntu:~$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2f15ad836be3339dec0e2e6a3c637e08e48aacbd, for GNU/Linux 3.2.0, stripped
fitz@ubuntu:~$ ldd /usr/bin/ls
linux-vdso.so.1 (0x00007ffcb529d000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fb02f58d000)
❶ libc.so.6 => ❷ /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb02f39c000) ❸
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fb02f317000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb02f311000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb02f5f1000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb02f2ee000)
fitz@ubuntu:~$ file /lib/x86_64-linux-gnu/libc.so.6
❹ /lib/x86_64-linux-gnu/libc.so.6: symbolic link to libc-2.30.so
Нужно заметить, что файла библиотеки linux-vdso. so. 1 (реализующей интерфейс системных вызовов к ядру) не существует, так как она является виртуальной (VDSO, virtual dynamic shared object), т. е. предоставляется и отображается в память процесса самим ядром, «как будто» является настоящей библиотекой. Кроме того, библиотека ld-linux-x86-64. so. 2 указана абсолютным путевым именем, поэтому поиск ее файла не производится.
Для большинства библиотек зависимость устанавливается при помощи SONAME вида libNAME. so. X , где lib — стандартный префикс (library, библиотека), . so — суффикс (shared object, разделяемый объект), NAME — имя «собственное», а . X — номер версии ее интерфейса. По имени SONAME в определенных (конфигурацией компоновщика) каталогах производится поиск одноименного файла библиотеки, который на самом деле оказывается символической ссылкой ❹ на «настоящий» файл библиотеки. Например, для 6-й версии интерфейса динамической библиотеки языка с ( libc. so. 6 ) настоящий файл библиотеки называется libc2. 30. so , что указывает на версию самой библиотеки как 2.30.
Версии библиотек
fitz@ubuntu:~$ file /lib/x86_64-linux-gnu/libpcre2-8.so.0
/lib/x86_64-linux-gnu/libpcre2-8.so.0: symbolic link to libpcre2-8.so.0.7.1
Аналогично, в приведенном выше листинге показано, что для 0-й версии интерфейса динамической библиотеки регулярных perl-выражений pcre2 ( libpcre2-8. so. 0 ) настоящий файл библиотеки называется libpcre2-8. so. 0. 7. 1 , а это указывает на версию самой библиотеки как 0.7.1.
Такой подход позволяет заменять (исправлять ошибки, улучшать неэффективные алгоритмы и пр.) библиотеки (при условии неизменности их интерфейсов) отдельно от программ, зависящих от них. При обновлении библиотеки libc2. 30. so , например, до libc2. 32. so достаточно установить символическую SONAME-ссылку libc. so. 6 на libc-2. 32. so , в результате чего ее начнут использовать все программы с зависимостями от libc. so. 6 . Более того, в системе может быть одновременно установлено любое количество версий одной и той же библиотеки, реализующих одинаковые или разные версии интерфейсов, выбор которых будет указан соответствующими SONAME-ссылками.
Библиотеки — это незапускаемые программы
fitz@ubuntu:~$ file /lib/x86_64-linux-gnu/libc-2.30.so
/lib/x86_64-linux-gnu/libc-2.30.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2155f455ad56bd871c8225bcca85ee25c1c197c4, for GNU/Linux 3.2.0, stripped
fitz@ubuntu:~$ file /lib/x86_64-linux-gnu/libpcre2-8.so.0.7.1
/lib/x86_64-linux-gnu/libpcre2-8.so.0.7.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=815e1acbcc22015f05d62c17fe982c1b573125b1, stripped
fitz@ubuntu:~$ ldd /lib/x86_64-linux-gnu/libpcre2-8.so.0.7.1
linux-vdso.so.1 (0x00007ffe22093000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8ec2bdd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8ec29ec000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8ec2c99000)
Библиотеки имеют тот же бинарный формат W:[ELF], что и «запускаемые» программы, но не «запускаемый» executable, а «совместно используемый» shared object. Библиотеки, являясь пусть и незапускаемыми, но программами, естественным образом тоже зависят от других библиотек, что показано в следующем листинге. Практически «запускаемость» ELF-файлов зависит не от их типа, а от прав доступа и осмысленности точки входа — адреса первой инструкции, которой передается управление при попытке запуска. Например, библиотеку libc-2. 30. so можно запустить, в результате чего будет выведена статусная информация.
Запускаемые библиотеки
fitz@ubuntu:~$ ls –l /lib/x86_64-linux-gnu/libc-2.30.so
-rwxr-xr-x 1 root root 2025032 сен 16 17:56 /lib/x86_64-linux-gnu/libc-2.30.so
fitz@ubuntu:~$ /lib/i386-linux-gnu/libc-2.15.so
GNU C Library (Ubuntu GLIBC 2.30-0ubuntu2) stable release version 2.30.
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.2.1 20190909.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
< https:// bugs. launchpad. net/ ubuntu/ +source/ glibc/ +bugs>.
Ядро Linux
Не стоит забывать, что самой главной программой операционной системы является ее ядро, которое в Linux состоит из статического стартового модуля в формате ELF executable и динамически пристыковываемых программных модулей формата ELF relocatable. Для выполнения процедуры начальной загрузки стартовый модуль упакован в «самораспаковывающийся» gzip-архив формата bzImage (big zipped image), который состоит из программы распаковки и собственно запакованного стартового модуля.
В приведенном ниже листинге проиллюстрирован процесс извлечения стартового модуля из архива / boot/ vmlinuz-3. 13. 0-49-generic формата bzImage ⓿, который предварительно копируется ❶ в / tmp/ vmlinuz . Для извлечения используется сценарий extract-vmlinux ❷ из пакета заголовочных файлов ядра. Распакованный ❸ стартовый модуль / tmp/ vmlinux ожидаемо оказывается статически скомпонованной (т. е. не использующей библиотеки ELF shared object) исполняемой ELF-программой.
Ядро операционной системы
fitz@ubuntu:~$ uname -r
5.3.0-23-generic
fitz@ubuntu:~$ file /boot/vmlinuz-5.3.0-23-generic
/boot/vmlinuz-5.3.0-23-generic: regular file, no read permission
fitz@ubuntu:~$ ls -l /boot/vmlinuz-5.3.0-23-generic
-rw——- 1 root root 11399928 ноя 12 11:51 /boot/vmlinuz-5.3.0-23-generic
fitz@ubuntu:~$ sudo file /boot/vmlinuz-5.3.0-23-generic
⓿ /boot/vmlinuz-5.3.0-23-generic: Linux kernel x86 boot executable bzImage, version 5.3.0-23-generic (buildd@lgw01-amd64-002) #25-Ubuntu SMP Tue Nov 12 09:22:33 UTC 2019, RO-rootFS, swap_dev 0xA, Normal VGA
❶ fitz@ubuntu:~$ sudo cat /boot/vmlinuz-5.3.0-23-generic > /tmp/vmlinuz
❷ fitz@ubuntu:~$ /usr/src/linux-headers-5.3.0-23/scripts/extract-vmlinux /tmp/vmlinuz > /tmp/vmlinux
fitz@ubuntu:~$ file /tmp/vmlinux
/tmp/vmlinux: ELF 64-bit LSB executable ❸, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=b23ff3f6790319ec538278e3269af619ba2ca642, stripped
Динамические модули загружаются в пространство ядра и пристыковываются к стартовому модулю позднее, уже при работе операционной системы при помощи системных утилит insmod или modprobe . Для отстыковки и выгрузки ненужных модулей предназначена системная утилита rmmod , для просмотра списка ❶ загруженных модулей — lsmod , а для идентификации свойств и параметров ❷ модулей — утилита modinfo . Загрузка и выгрузка модулей реализуется специальными системными вызовами init_module и delete_module , доступ к списку загруженных модулей — при помощи файла / proc/ modules псевдофайловой системы proc , а идентификация свойств и параметров модулей — чтением специальных секций ELF-файлов модулей.
Модули ядра
❶ fitz@ubuntu:~$ lsmod
Module Size Used by
.
i915 1949696 4
.
btusb 57344 0
.
uvcvideo 98304 0
.
e1000e 258048 0
.
❷ fitz@ubuntu:~$ modinfo i915
filename: /lib/modules/5.3.0-23-generic/kernel/drivers/gpu/drm/i915/i915.ko
license: GPL and additional rights
description: Intel Graphics
.
fitz@ubuntu:~$ file /lib/modules/5.3.0-23-generic/kernel/drivers/gpu/drm/i915/i915.ko
/lib/modules/5.3.0-23-generic/kernel/drivers/gpu/drm/i915/i915.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=49e59590c1a718074b76b6541702f6f794ea7eae, not stripped
Динамические модули ядра зачастую являются драйверами устройств, что проиллюстрировано в листинге при помощи утилит lspci и lsusb , которые сканируют посредством псевдофайловой системы sysfs списки обнаруженных ядром на шинах PCI и USB устройств и обслуживающих их драйверов.
Драйверы устройств
fitz@ubuntu:~$ lspci -k
.
00:02.0 VGA compatible controller: Intel Corporation 2nd Generation Core Process
or Family Integrated Graphics Controller (rev 09)
Subsystem: Dell 2nd Generation Core Processor Family Integrated Graphics
Controller
Kernel driver in use: i915
Kernel modules: i915
.
00:19.0 Ethernet controller: Intel Corporation 82579LM Gigabit Network Connection (Lewisville) (rev 04)
Subsystem: Dell 82579LM Gigabit Network Connection (Lewisville)
Kernel driver in use: e1000e
Kernel modules: e1000e
fitz@ubuntu:~$ lsusb –t
.
/: Bus 01. Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/ 3p, 480M
|__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/ 6p, 480M
|__ Port 4: Dev 3, If 2, Class=Vendor Specific Class, Driver=, 12M
|__ Port 4: Dev 3, If 0, Class=Wireless, Driver=btusb, 12M
|__ Port 4: Dev 3, If 3, Class=Application Specific Interface, Driver=, 12M
|__ Port 4: Dev 3, If 1, Class=Wireless, Driver=btusb, 12M
|__ Port 5: Dev 4, If 0, Class=Video, Driver=uvcvideo, 480M
|__ Port 5: Dev 4, If 1, Class=Video, Driver=uvcvideo, 480M
Процессы и нити
Сущность процесса неразрывно связана с мультипрограммированием и многозадачностью операционной системы. Например, в однозадачных операционных системах программы существуют, а процессы — нет. В однозадачных операционных системах единовременно одна последовательная программа выполняется одним исполнителем (центральным процессором), имея возможность безраздельно использовать все доступные ресурсы (память, устройства ввода‑вывода и пр.).
В любой программе можно выделить перемежающиеся блоки инструкций, использующих или центральный процессор (ЦП), или устройства ввода‑вывода (УВВ). При этом центральный процессор вынужден простаивать при выполнении программой операций ввода‑вывода, например, при ожидании окончания записи (или чтения) блока данных на внешний носитель, или при ожидании окончания передачи (или приема) сетевого кадра, или при ожидании событий с устройств человеко‑машинного взаимодействия. С другой стороны, устройства ввода‑вывода тоже вынуждены простаивать при выполнении программой вычислительных операций, например, ожидая результата, подлежащего выводу, или ожидая возникновения у программы потребности в новых исходных данных.
Используя такую модель поведения программ, можно провести анализ потребления ими ресурсов при выполнении. Например, компрессоры gzip, bzip и xz считывают очередной блок данных исходного файла, относительно долго упаковывают его и записывают в результирующий файл, а затем повторяют процедуру до исчерпания блоков исходного файла. Количество времени, потраченного на вычислительные операции упаковки, будет много больше количества времени, потраченного на чтение исходных данных и запись результатов, поэтому нагрузка на ЦП будет высокой, а на УВВ — нет. Такой же анализ можно привести и для дубликатора dd, копировщика rsync или архиватора tar, которые, наоборот, почти не выполняют никаких вычислений, а сосредоточены на вводе‑выводе больших объемов данных, поэтому при их использовании нагрузка на ЦП будет довольно низкой, а на УВВ — высокой.
Для командного интерпретатора bash, текстовых редакторов nano и vim и других интерактивных программ, взаимодействующих с пользователем, характерны длительные ожидания ввода небольших команд, простая и недолгая их обработка и вывод короткого результата. В результате коэффициент полезного использования и ЦП, и УВВ будет приближен к нулю.
Подобный анализ и желание увеличить коэффициенты полезного использования ресурсов привели к созданию многозадачных операционных систем, основывающихся на простой идее псевдоодновременного выполнения нескольких последовательных программ одним исполнителем. Для этого вместо простоя в ожидании окончания операции ввода‑вывода, начатой некоторой программой, центральный процессор переключается на выполнение другой программы, тем самым увеличивая интегральный коэффициент его полезного использования.
С появлением мультипрограммной смеси (так называют набор программ, между которыми переключается процессор) каждая из ее программ больше не может безраздельно использовать все доступные ресурсы (например, всю память — она одновременно нужна всем программам смеси), в связи с чем операционная система берет на себя задачи диспетчеризации (распределения) ресурсов между ними. В Linux, как и во многих других операционных системах, программы изолируются друг от друга в специальных «виртуальных» средах, обеспечивающих их процесс выполнения. Каждая такая среда называется процессом и получает долю доступных ресурсов — выделенный участок памяти, выделенные промежутки процессорного времени. Процесс эмулирует для программы «однозадачный» режим выполнения, словно программа выполняется в одиночку, и «безраздельное» использование ресурсов процесса, как будто это все доступные ресурсы.
Параллельные программы, как указывалось ранее, состоят из независимых ветвей, каждая из которых сама по себе укладывается в модель поведения последовательной программы, поэтому одну параллельную программу можно выполнять в нескольких процессах в псевдоодновременном режиме. Процессы операционной системы, таким образом, являются контейнерами для многозадачного выполнения программ, как последовательных, так и параллельных.
В следующем листинге при помощи команды ps показаны процессы пользователя, упорядоченные в дерево, построенное на основе дочерне‑родительских отношений между процессами. Уникальный идентификатор, отличающий процесс от других, выведен в столбце PID (process identifier), а имя и аргументы программы, запущенной в соответствующем процессе — в столбце COMMAND.
В столбце STAT показано текущее состояние процесса, например S (сон, sleep) или R (выполнение, running, или готовность к выполнению, runnable). Процессы, ожидающие завершения их операций ввода‑вывода, находятся в состоянии сна, в противном случае либо выполняются, либо готовы к выполнению, т. е. ожидают, когда текущий выполняющийся процесс заснет и процессор будет переключен на них. В столбце TIME показано чистое потребленное процессом процессорное время от момента запуска программы, увеличивающееся только при нахождении им в состоянии выполнения.
Дерево процессов пользователя
fitz@ubuntu:~$ ps fx
PID TTY STAT TIME COMMAND
.
17764 tty3 Ssl+ 0:00 /usr/lib/gdm3/gdm-x-session —run-script .
17766 tty3 Sl+ 3:09 _ /usr/lib/xorg/Xorg vt3 -displayfd 3 .
17774 tty3 Sl+ 0:00 _ /usr/lib/gnome-session/gnome-session-binary .
.
2987 ? Ss 0:04 /lib/systemd/systemd —user
2992 ? S 0:00 _ (sd-pam)
17373 ? Ssl 0:08 _ /usr/bin/pulseaudio —daemonize=no
17444 ? Ss 0:02 _ /usr/bin/dbus-daemon —session —address=systemd: .
.
17921 ? Ssl 10:04 _ /usr/bin/gnome-shell
.
⓿ 30192 ? Ssl 0:00 _ /usr/libexec/gnome-terminal-server
❶ 30202 pts/1 Ss 0:00 _ bash
❷ 30226 pts/1 S+ 0:00 _ man ps
30236 pts/1 S+ 0:00 _ pager
❶ 30245 pts/3 Ss 0:00 _ bash
❷ 30251 pts/3 R+ 0:00 _ ps fx
❸ 30315 ? Sl 0:04 _ /usr/lib/firefox/firefox -new-window
30352 ? Sl 0:02 _ /usr/lib/firefox/firefox -contentproc -childID 1 .
30396 ? Sl 0:00 _ /usr/lib/firefox/firefox -contentproc -childID 2 .
30442 ? Sl 0:00 _ /usr/lib/firefox/firefox -contentproc -childID 3 .
Управляющий терминал процесса, показанный в столбце TTY, используется для доставки ему интерактивных сигналов (см. разд. 4.8) при вводе управляющих символов intr ^C, quit ^\ и пр. у части процессов ⓿, ❸ управляющий терминал отсутствует, потому что они выполняют приложения, взаимодействующие с пользователем не посредством терминалов, а через графическую систему.
Процесс по своему определению изолирует свою программу от других выполняющихся программ, что затрудняет использование процессов для выполнения таких параллельных программ, ветви которых не являются полностью независимыми друг от друга и должны обмениваться данными. Использование предназначенных для этого средств межпроцессного взаимодействия при интенсивном обмене приводит к обременению неоправданными накладными расходами, поэтому для эффективного выполнения таких параллельных программ используются легковесные процессы (LWP, light-weight processes), они же нити (threads).
info
Существует еще один (неудачный, на мой взгляд) перевод понятия thread на русский язык — поток. Во‑первых, он конфликтует с переводом понятия stream — поток, а во‑вторых, в отличие от stream, thread никуда не течет. А вот процесс (process) содержит в себе нити (thread) абсолютно таким же образом, как и обычная веревка состоит из нитей.
Механизм нитей позволяет переключать центральный процессор между параллельными ветвями одной программы, размещаемыми в одном (!) процессе. Нити никак не изолированы друг от друга, и им доступны абсолютно все ресурсы своего процесса, поэтому задача обмена данными между нитями попросту отсутствует, т. к. все данные являются для них общими.
В примере из cktle. otuj следующего листинга показаны нити процесса в BSD-формате вывода. Выбор процесса производится по его идентификатору PID, предварительно полученному командой pgrep по имени программы, выполняющейся в искомом процессе.
В выводе наличие нитей процесса отмечает флаг l ( lwp) в столбце состояния STAT, а каждая строчка без идентификатора PID символизирует одну нить. Так как в многонитевой программе переключение процессора производится между нитями, то и состояния сна S, выполнения или ожидания R приписываются отдельным нитям.
Нити процессов, BSD-формат вывода
fitz@ubuntu:~$ pgrep firefox
30315
fitz@ubuntu:~$ ps mp 30315
PID TTY STAT TIME COMMAND
PID TTY STAT TIME COMMAND
30315 ? — 0:05 /usr/lib/firefox/firefox -new-window
— — Sl 0:03 —
.
— — Sl 0:00 —
— — Sl 0:00 —
— — Sl 0:00 –
В нижеследующем листинге показаны нити процесса в SYSV-формате вывода. Выбор процесса производится по имени его программы. Общий для всех нитей идентификатор их процесса отображается в столбце PID, уникальный идентификатор каждой нити — в столбце LWP (иногда называемый TID, thread identifier), а имя процесса (или собственное имя нити, если задано) — в столбце CMD.
Нити процессов, SYSV-формат вывода
fitz@ubuntu:~$ ps -LC firefox
PID LWP TTY TIME CMD
30315 30315 ? 00:00:04 firefox
30315 30320 ? 00:00:00 gmain
30315 30321 ? 00:00:00 gdbus
.
30315 30328 ? 00:00:00 Socket Thread
30315 30332 ? 00:00:00 Cache2 I/O
30315 30333 ? 00:00:00 Cookie
.
30315 30371 ? 00:00:00 HTML5 Parser
30315 30373 ? 00:00:00 DNS Resolver #3
.
Порождение процессов и нитей, запуск программ
Несмотря на очевидные различия, историю возникновения и развития, нити и процессы объединяет общее назначение — они являются примитивами выполнения некоторого набора последовательных инструкций. Откровенно говоря, нити, в общем, появились в операционных системах раньше, чем изолированные UNIX-процессы, в которые со временем вернулись UNIX-нити.
Процессы выполняют или разные последовательные программы целиком, или ветви одной параллельной программы, но в изолированном окружении со своим «частным» (private) набором ресурсов. Нити, наоборот, выполняют ветви одной параллельной программы в одном окружении с «общим» (shared) набором ресурсов. В многозадачном ядре Linux вообще используется универсальное понятие «задача», которая может иметь как общие ресурсы (память, открытые файлы и т. д.) с другими задачами, так и частные ресурсы для своего собственного использования.
Порождение нового процесса реализуется при помощи системного вызова fork , в результате которого ядро операционной системы создает новый дочерний (child) процесс PID 2 — полную копию (COPY) процесса‑родителя (parent) PID 1. Вся (за небольшими исключениями) память процесса — состояние, свойства, атрибуты (кроме идентификатора PID) и даже содержимое (программа с ее библиотеками) — наследуется дочерним процессом. Даже выполнение порожденного и порождающего процесса продолжится с одной и той же инструкции их одинаковой программы. Такое клонирование обычно используют параллельные программы с ветвями, выполняющимися в дочерних процессах.
Уничтожение процесса (например, при штатном окончании программы) производится с помощью системного вызова exit . При этом родительскому процессу доставляется сигнал SIGCHILD , оповещающий о завершении дочернего процесса. Статус завершения status , переданный дочерним процессом через аргументы exit , будет сохраняться ядром до момента его востребования родительским процессом при помощи системного вызова wait , а весь этот промежуток времени дочерний процесс будет находиться в состоянии Z (zombie).
Родительский процесс может завершиться раньше своих дочерних процессов, тогда логично предположить, что все «осиротевшие» процессы окажутся зомби по завершении, потому как просто некому будет востребовать их статус завершения. На самом деле этого не происходит, потому что «осиротевшим» процессам назначается приемный родитель, в качестве которого выступает прародитель всех процессов init с идентификатором PID = 1 .
Запуск новой программы (см. рис.) реализуется при помощи системного вызова exec , в результате которого содержимое процесса PID 1 полностью замещается запускаемой программой и библиотеками, от которых она зависит, а свойства и атрибуты (включая идентификатор PID) остаются неизменными. Такое замещение обычно используется программами, устанавливающими нужные значения свойств и атрибутов процесса и подготавливающими ресурсы процесса к выполнению запускаемой программы. Например, обработчик терминального доступа getty открывает заданный терминал, устанавливает режимы работы порта терминала, перенаправляет на терминал стандартные потоки ввода‑вывода, а затем замещает себя программой аутентификации login .
Для запуска новой программы в новом процессе используются оба системных
вызова fork и exec согласно принципу fork-and-exec «раздвоиться и запустить», показанного на рисунке ниже. Например, командный интерпретатор bash по командам ps fx или man ps порождает дочерние процессы ❷ и замещает их программами ps и man . Тем же образом действует ⓿ графический эмулятор терминала gnome-terminal-server — запуская новый сеанс пользователя ❶ на каждой из своих вкладок, он замещает свои дочерние процессы программой интерпретатора bash .
Следующий листинг иллюстрирует команду интерпретатора, запущенную в «фоновом» режиме при помощи конструкции асинхронного списка. Аналогично всем предыдущим командам, интерпретатор использует fork-and-exec для запуска программы в дочернем процессе с идентификатором 23228 , но не дожидается его завершения при помощи системного вызова wait , как обычно, а немедленно ❶ продолжает интерактивное взаимодействие с пользователем, сообщив ему PID порожденного процесса и «номер задания» [ 1] команды «заднего фона». Оповещение о завершении своего дочернего процесса интерпретатор получит позже, при помощи сигнала SIGCHLD , и отреагирует соответствующим сообщением ❷ об окончании команды «заднего фона».
Фоновое выполнение программ
fitz@ubuntu:~$ dd if=/dev/dvd of=plan9.iso &
❶ [1] 23228
fitz@ubuntu:~$ ps f
PID TTY STAT TIME COMMAND
23025 pts/1 S 0:00 -bash
23228 pts/1 R 1:23 _ dd if=/dev/dvd of=plan9.iso
23230 pts/1 R+ 0:00 _ ps f
fitz@ubuntu:~$
.
fitz@ubuntu:~$ 586896+0 записей получено
586896+0 записей отправлено
300490752 байт (300 MB, 286 MiB) скопирован, 14,6916 c, 20,5 MB/c
❷ [1]+ Завершён dd if=/dev/dvd of=plan9.iso
В следующем листинге показана конвейерная конструкция интерпретатора, при помощи которой осуществляется поиск самого большого файла с суффиксом . html вниз по дереву каталогов, начиная с / usr/ share/ doc . Эта конструкция реализуется при помощи fork-and-exec четырьмя параллельно порожденными дочерними процессами интерпретатора, в каждом из которых запущена программа соответствующей части конвейера, при этом дочерние процессы связаны неименованным каналом pipe — простейшим средством межпроцессного взаимодействия. Встроенная команда интерпретатора wait реализует одноименный системный вызов и используется для ожидания окончания всех дочерних процессов конвейера, целиком запущенного в «фоновом» режиме.
Параллельный запуск взаимодействующих программ
fitz@ubuntu:~$ find /usr/share/doc -type f -name ‘.html’ | xargs -n1 wc –l | sort -k 1 –nr | head -1 &
[1] 12827
fitz@ubuntu:~$ ps fj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
11715 11716 11716 9184 pts/0 14699 S 1006 0:01 -bash
11716 12824 12824 9184 pts/0 14699 R 1006 0:00 _ find . -type f -name *.html
11716 12825 12824 9184 pts/0 14699 R 1006 0:00 _ xargs -n1 wc -l
11716 12826 12824 9184 pts/0 14699 S 1006 0:00 _ sort -k 1 -nr
11716 12827 12824 9184 pts/0 14699 S 1006 0:00 _ head -1
11716 14699 14699 9184 pts/0 14699 R+ 1006 0:00 _ ps fj
fitz@ubuntu:~$ wait
15283 /usr/share/doc/xterm/xterm.log.html
[ 1] + Завершён find /usr/share/doc -type f -name ‘.html’ | xargs -n1 wc -l | sort -k 1 -nr | head -1
Параллельные многопроцессные программы
Как указывалось ранее, параллельные программы зачастую используют процессы для выполнения отдельных ветвей. В эту категорию часто попадают программы сетевых служб, например сервер баз данных W:[PostgreSQL], служба удаленного доступа W:[SSH] и подобные. Следующий листинг иллюстрирует программу postgres, выполняющуюся в шести параллельных процессах, один из которых — диспетчер ❶, четыре служебных ❷ и еще один ❸ вызван подключением пользователя fitz к одноименной базе данных fitz . При последующих подключениях пользователей к серверу будут порождены дополнительные дочерние процессы для обслуживания их запросов — по одному на каждое подключение.
Параллельные многопроцессные сервисы
fitz@ubuntu:~$ ps f -C postgres
PID TTY STAT TIME COMMAND
❶ 6711 ? S 0:00 /usr/lib/postgresql/11/bin/postgres -D /var/lib/postgresql.
❷ 6713 ? Ss 0:00 _ postgres: 11/main: checkpointer
│ 6714 ? Ss 0: 00 \ _ postgres: 11/ main: background writer
│ 6715 ? Ss 0: 00 \ _ postgres: 11/ main: walwriter
│ 6716 ? Ss 0: 00 \ _ postgres: 11/ main: autovacuum launcher
│ 6717 ? Ss 0: 00 \ _ postgres: 11/ main: stats collector
6718 ? Ss 0: 00 \ _ postgres: 11/ main: logical replication launcher
❸ 9443 ? Ss 0:00 _ postgres: 11/main: fitz fitz [local] idle
fitz@ubuntu:~$ ssh ubuntu
fitz@ubuntu’s password:
.
Last login: Sat Nov 21 13:29:33 2015 from localhost
fitz@ubuntu:~$ ps f -C sshd
PID TTY STAT TIME COMMAND
① 655 ? Ss 0:00 /usr/sbin/sshd -D
② 21975 ? Ss 0:00 _ sshd: fitz [priv]
③ 22086 ? S 0:00 _ sshd: fitz@pts/1
fitz@ubuntu: ~$ ^Dвыход
Connection to ubuntu closed.
Аналогично, при удаленном доступе по протоколу SSH программа sshd, работая в качестве диспетчера ① в одном процессе, на каждое подключение порождает один свой клон ②, который, выполнив аутентификацию и авторизацию пользователя в системе, порождает еще один свой клон ③, имперсонирующийся в пользователя и обслуживающий его запросы.
Параллельные многонитевые программы
Для управления нитями в Linux используют стандартный POSIX-интерфейс pthreads , реализующийся библиотекой W:[NPTL], которая является частью библиотеки libc . Интерфейс предоставляет «нитевой» вызов создания нити pthread_create , который является условным аналогом «процессных» fork и exec , вызов завершения и уничтожения нити pthread_exit , условно аналогичный exit , и вызов для получения статуса завершения нити pthread_join , условно аналогичный wait .
В качестве типичных примеров применения нитей можно привести сетевые сервисы, которые для параллельного обслуживания клиентских запросов используют нити вместо процессов. Например, WEB-сервер apache, как показано в следующем листинге, использует два многонитевых процесса по 27 нитей в каждом, что позволяет экономить память (за счет работы всех нитей процесса с общей памятью) при обслуживании большого количества одновременных клиентских подключений.
Параллельные многонитевые сервисы
fitz@ubuntu:~$ ps f -C apache2
PID TTY STAT TIME COMMAND
10129 ? Ss 0:00 /usr/sbin/apache2 -k start
10131 ? Sl 0:00 _ /usr/sbin/apache2 -k start
10132 ? Sl 0:00 _ /usr/sbin/apache2 -k start
fitz@ubuntu:~$ ps fo pid,nlwp,cmd -C apache2
PID NLWP CMD
10129 1 /usr/sbin/apache2 -k start
10131 27 _ /usr/sbin/apache2 -k start
10132 27 _ /usr/sbin/apache2 -k start
fitz@ubuntu:~$ ps -fLC rsyslogd
UID PID PPID LWP C NLWP STIME TTY TIME CMD
syslog 606 1 606 0 4 ноя18 ? 00:00:00 /usr/sbin/rsyslogd -n -iNONE
syslog 606 1 680 0 4 ноя18 ? 00:00:00 /usr/sbin/rsyslogd -n -iNONE
syslog 606 1 681 0 4 ноя18 ? 00:00:00 /usr/sbin/rsyslogd -n -iNONE
syslog 606 1 682 0 4 ноя18 ? 00:00:00 /usr/sbin/rsyslogd -n -iNONE
Аналогично, сервис централизованной журнализации событий rsyslogd использует нити для параллельного сбора событийной информации из разных источников, ее обработки и журнализации. Одна нить считывает события ядра из / proc/ kmsg , вторая принимает события других служб из файлового сокета / run/ systemd/ journal/ syslog ( / dev/ log в ранних, до systemd системах), третья фильтрует поток принятых событий и записывает в журнальные файлы каталога / var/ log/ * и т. д. Параллельная обработка потоков поступающих событий при помощи нитей производится с минимально возможными накладными расходами, что позволяет достигать колоссальной производительности по количеству обрабатываемых сообщений в единицу времени.
Распараллеливание используется не только для псевдоодновременного выполнения ветвей параллельной программы, но и для их настоящего одновременного выполнения несколькими центральными процессорами. В примере из следующего листинга показано, как сокращается время сжатия ISO-образа файла при использовании параллельного упаковщика pbzip2 по сравнению с последовательным bzip2. Для измерения времени упаковки применяется встроенная команда интерпретатора time , при этом сначала измеряется время упаковки ❶ и время распаковки ❷ последовательным упаковщиком, а затем — время упаковки ① и время распаковки ② параллельным упаковщиком. Команды упаковки запускаются на «заднем фоне», оценивается наличие процессов и нитей паковщиков, после чего они переводятся на «передний фон» встроенной командой интерпретатора fg (foreground) и оцениваются затраты времени.
Параллельные многонитевые утилиты
fitz@ubuntu:~$ ls -lh plan9.iso
-rw-r—r— 1 fitz fitz 287M нояб. 28 15:47 plan9.iso
❶ fitz@ubuntu:~$ time bzip2 plan9.iso &
[ 1] 5545
fitz@ubuntu:~$ ps f
PID TTY STAT TIME COMMAND
4637 pts/0 S 0:00 -bash
5545 pts/0 S 0:00 _ -bash
5546 pts/0 R 0:12 _ bzip2 plan9.iso
5548 pts/0 R+ 0:00 _ ps f
fitz@ubuntu:~$ ps -fLp 5546
UID PID PPID LWP C NLWP STIME TTY TIME CMD
fitz 5546 5545 5546 96 1 10:50 pts/0 00:00:22 bzip2 plan9.iso
fitz@ubuntu:~$ fg
time bzip2 plan9.iso
real 0m54.780s
user 0m51.772s
sys 0m0.428s
fitz@ubuntu:~$ ls -lh plan9.iso.bz2
-rw-r—r— 1 fitz fitz 89M нояб. 28 15:47 plan9.iso.bz2
❷ fitz@ubuntu:~$ time bzip2 -d plan9.iso.bz2
real 0m20.705s
user 0m19.044s
sys 0m1.168s
① fitz@ubuntu:~$ time pbzip2 plan9.iso &
[ 1] 5571
fitz@ubuntu:~$ ps f
PID TTY STAT TIME COMMAND
4637 pts/0 S 0:00 -bash
5571 pts/0 S 0:00 -bash
5572 pts/0 Sl 0:03 _ pbzip2 plan9.iso
5580 pts/0 R+ 0:00 _ ps f
fitz@ubuntu:~$ ps -fLp 5572
UID PID PPID LWP C NLWP STIME TTY TIME CMD
fitz 5572 5571 5578 92 8 10:52 pts/0 00:00:43 pbzip2 plan9.iso
.
fitz 5572 5571 5579 1 8 10:52 pts/0 00:00:00 pbzip2 plan9.iso
fitz@ubuntu:~$ fg
time pbzip2 plan9.iso
real 0m24.259s
user 1m22.940s
sys 0m1.888s
fitz@ubuntu:~$ ls -lh plan9.iso.bz2
-rw-r—r— 1 fitz fitz 89M нояб. 28 15:47 plan9.iso.bz2
② fitz@ubuntu:~$ time pbzip2 -d plan9.iso.bz2
real 0m7.384s
user 0m25.972s
sys 0m1.396s
В результате оценки оказывается, что последовательный упаковщик bzip2 использует один однонитевой процесс и затрачивает ≈54,7 с реального времени на упаковку, из них ≈51,7 с проводит в пользовательском режиме user и лишь ≈0,4 с в режиме ядра sys (выполняя системные вызовы, например read или write ). Соотношение между временем режимов говорит о вычислительном характере программы, т. е. о существенном превалировании времени вычислительных операций упаковки над временем операций ввода‑вывода для чтения исходных данных и записи результатов. Это означает, что нагрузка последовательного упаковщика на центральный процессор (в случае, если бы он был единственный) близка к максимальной, и его параллельная реализация для псевдоодновременного выполнения ветвей (которые практически никогда не спят) лишена смысла.
Параллельный упаковщик pbzip2 использует один многонитевой процесс из восьми нитей и затрачивает ≈24,4 с реального времени на упаковку, при этом ≈1 мин 22,9 с (!) проводит в пользовательском режиме и ≈1,8 с в режиме ядра. Прирост производительности упаковки и, как следствие, сокращение времени упаковки достигаются за счет настоящего параллельного выполнения нитей на нескольких процессорах (разных ядрах процессора). Соотношение между реальным временем упаковки и суммарно затраченным временем режима пользователя, которое примерно в 3 раза больше, означает использование в среднем трех процессоров для параллельного выполнения вычислительных операций упаковки.
Двойственность процессов и нитей Linux
Как указывалось ранее, процессы и нити в ядре Linux сводятся к универсальному понятию «задача». Задача, все ресурсы которой (память, открытые файлы и т. д.) используются совместно с другими такими же задачами, является нитью. И наоборот, процессами являются такие задачи, которые обладают набором своих частных, индивидуальных ресурсов.
Универсальный системный вызов clone позволяет указать, какие ресурсы станут общими в порождаемой и порождающей задачах, а какие — частными. Системные вызовы порождения POSIX-процессов fork и POSIX-нитей pthread_create оказываются в Linux всего лишь «обертками» над clone , что проиллюстрировано в двух следующих листингах.
В первом примере архиватор tar PID = 11801 создает при помощи системного вызова clone дочерний процесс PID = 11802 , в который помещает программу компрессора gzip, используя системный вызов execve . В результате параллельной работы двух взаимодействующих процессов будет создан компрессированный архив docs. tgz каталога / usr/ share/ doc .
Системный вызов clone — порождение нового процесса
fitz@ubuntu:~$ strace -fe clone,fork,execve tar czf docs.tgz /usr/share/doc
execve(«/usr/bin/tar», [«tar», «czf», «docs.tgz», «/usr/share/doc»], . ) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID| . ) = 12403
tar: Удаляется начальный / ‘ из имен объектов
strace: Process 12403 attached [pid 12403] execve(«/bin/sh», [«/bin/sh», «-c», «gzip»], 0x7ffd8dd597c0 . ) = 0 [pid 12403] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID| . ) = 12404 strace: Process 12404 attached [pid 12404] execve(«/usr/bin/gzip», [«gzip»], 0x55e2e45bbb48 /* 35 vars */) = 0`
В примере из следующего листинга компрессор pbzip2 создает при помощи системного вызова clone семь «дочерних» нитей ❶. ❼ PID = 12514-> 12520 , которые имеют общую память CLONE_VM , общие открытые файлы CLONE_FILES и прочие общие ресурсы.
Системный вызов clone — порождение новой нити
fitz@ubuntu:~$ strace -fe clone,fork,execve pbzip2 plan9.iso
execve(«/usr/bin/pbzip2», [«pbzip2», «plan9.iso»], 0x7ffd28884938 /* 35 vars */) = 0
❶ clone(child_stack=0x7f1d46d38fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|. ) = 12514
5 .
❼ clone(child_stack=0x7f1d46537fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|. ) = 12520
strace: Process 12520 attached
5 .
strace: Process 12514 attached
[ pid 12514] +++ exited with 0 +++
5 .
[ pid 12520] +++ exited with 0 +++
+++ exited with 0 +++
Дерево процессов
Процессы, попарно связанные дочерне‑родительскими отношениями, формируют дерево процессов операционной системы. Первый процесс init , называемый прародителем процессов, порождается ядром операционной системы после инициализации и монтирования корневой файловой системы, откуда и считывается программа / sbin/ init (в современных системах является символической ссылкой на актуальный / lib/ systemd/ systemd ). Прародитель процессов всегда имеет PID = 1 , а его основной задачей является запуск разнообразных системных служб, включая запуск обработчиков алфавитно‑цифрового терминального доступа getty, менеджера дисплеев графического доступа, службы дистанционного доступа SSH и прочих (см. главу 10). Кроме того, systemd назначается приемным родителем для «осиротевших» процессов, а также отслеживает аварийные завершения запускаемых им служб и перезапускает их.
В примере из следующего листинга показано дерево процессов, построенное при помощи специальной команды pstree , а в листинге “Процессы ядра, демоны, прикладные процессы” — «классическое» представление дерева процессов при помощи команды ps .
Дерево процессов
fitz@ubuntu:~$ pstree -cnAhT
systemd-+-systemd-journal
❷ |-systemd-udevd
❷ |-systemd-resolve
❷ |-rsyslogd
.
|-gdm3—gdm-session-wor-+-gdm-session-wor
| |-gdm-x-session-+-Xorg
| | -gnome-session-b
.
|-systemd-+-( sd-pam)
.
| |-gnome-terminal—+-bash—man—pager ❸
| | -bash
.
❷ |-postgres-+-postgres
| |-postgres
| |-postgres
| |-postgres
| |-postgres
| -postgres
❷ |-apache2-+-apache2
| -apache2
.
❷ |-sshd-+-sshd—sshd—bash
| `-sshd—sshd—bash
.
|-agetty
|-login—bash—pstree ❸
.
Процессы операционной системы принято классифицировать на системные (ядерные), демоны и прикладные, исходя из их назначения и свойств (см. листинг “Процессы ядра, демоны, прикладные процессы”).
Прикладные процессы ❸ выполняют обычные пользовательские программы (например, утилиту man ), для чего им выделяют индивидуальную память, объем которой указан в столбце VSZ вывода команды ps . Такие процессы обычно интерактивно взаимодействуют с пользователем посредством управляющего терминала (за исключением графических программ), указанного в столбце TTY.
Демоны (daemons) ❷ выполняют системные программы, реализующие те или иные службы операционной системы. Например, cron реализует службу периодического выполнения заданий, atd — службу отложенного выполнения заданий, rsyslogd — службу централизованной журнализации событий, sshd — службу дистанционного доступа, systemd-udevd — службу «регистрации» подключаемых устройств, и т. д. Демоны запускаются на ранних стадиях загрузки операционной системы и взаимодействуют с пользователем не интерактивно при помощи терминала, а опосредованно — при помощи своих утилит. Таким образом, отсутствие управляющего терминала в столбце TTY отличает их от прикладных процессов.
Процессы ядра, демоны, прикладные процессы
fitz@ubuntu:~$ ps faxu
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2 0.0 0.0 0 0 ? S ноя18 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< ноя18 0:00 _ [rcu_gp]
root 4 0.0 0.0 0 0 ? I< ноя18 0:00 _ [rcu_par_gp]
root 6 0.0 0.0 0 0 ? ❶ I< ноя18 0:00 _ [kworker/0:0H. ]
root 8 0.0 0.0 0 0 ? I< ноя18 0:00 _ [mm_percpu_wq]
root 9 0.0 0.0 0 0 ? S ноя18 0:09 _ [ksoftirqd/0]
.
root 1 0.0 0.2 168400 11684 ? Ss ноя18 0:12 /sbin/init splash
.
root 333 0.0 0.1 21844 5348 ? Ss ноя18 0:07 /lib/systemd/systemd-udevd
syslog 606 0.0 0.1 224360 4244 ? Ssl ноя18 0:01 /usr/sbin/rsyslogd -n –i.
.
root 649 0.0 0.0 20320 3036 ? ❷ Ss ноя18 0:00 /usr/sbin/cron -f
daemon 675 0.0 0.0 3736 2184 ? Ss ноя18 0:00 /usr/sbin/atd –f
.
root 21545 0.0 0.0 5560 3420 tty4 Ss ноя18 0:00 /bin/login -p —
fitz 28152 0.0 0.0 2600 1784 tty4 S 01:38 0:00 _ -sh
fitz 28162 0.0 0.0 12948 3584 tty4 S+ 01:38 0:00 _ bash
finn 12989 0.2 0.012092 3988 tty4 ❸ S+ 13:47 0:00 _ man ps
finn 13000 0.0 0.0 10764 2544 tty4 S+ 13:47 0:00 _ pager
info
Зачастую демоны имеют суффикс d в конце названия, например sshd — это secure shell daemon, а rsyslogd — rocket system logging daemon, и т. д.
Системные (ядерные) ❶ процессы (gравильнее — ядерные нити, т. к. выполняются они в общей памяти ядра операционной системы) выполняют параллельные части ядра операционной системы, поэтому не обладают ни индивидуальной виртуальной памятью VSZ, ни управляющим терминалом TTY. Более того, ядерные процессы не выполняют отдельную программу, загружаемую из ELF-файла, поэтому их имена COMMAND являются условными и изображаются в квадратных скобках, а кроме того, они имеют особое состояние I в столбце STAT.
Атрибуты процесса
Процесс в операционной системе является основным активным субъектом, взаимодействующим с окружающими его объектами — файлами и файловыми системами, другими процессами, устройствами и пр. Возможности процесса выполнять те или иные действия по отношению к другим объектам определяются его специальными свойствами — атрибутами процесса.
Маркеры доступа
Возможности процесса по отношению к объектам, доступ к которым разграничивается при помощи дискреционных механизмов (в частности, к файлам дерева каталогов) определяются значениями его атрибутов, формирующих его DAC-маркер доступа, а именно — атрибутами RUID, RGID, EUID, EGID, см. credentials .
Эффективные идентификаторы EUID (effective user identifier) и EGID (effective group identifier) указывают на «эффективных» пользователя и группу, использующихся дискреционными механизмами для определения прав доступа процесса к файлам и другим объектам согласно назначенному им режиму или списку доступа. Атрибуты RUID (real user identifier) и RGID (real group identifier) указывают на «настоящих» пользователя и группу, «управляющих» процессом.
Первому процессу пользовательского сеанса (в случае регистрации в системе
с использованием алфавитно‑цифрового терминала — командному интерпретатору) назначают атрибуты RUID/EUID и RGID/EGID равными идентификаторам зарегистрировавшегося пользователя и его первичной группы. Последующие процессы пользовательского сеанса наследуют значения атрибутов, т. к. порождаются в результате клонирования при помощи fork . В примере из следующего листинга при помощи команды id показаны значения EUID/EGID пользовательского сеанса и их наследование от командного интерпретатора, что явным образом подтверждает команда ps .
DAC-маркер доступа процесса — атрибуты RUID, EUID, RGID, EGID
fitz@ubuntu:~$ id
uid=1006(fitz) gid=1008(fitz) группы=1008(fitz)
fitz@ubuntu:~$ ps fo euid,ruid,egid,rgid,user,group,tty,cmd
EUID RUID EGID RGID USER GROUP TT CMD
1006 1006 1008 1008 fitz fitz pts/2 -bash
1006 1006 1008 1008 fitz fitz pts/2 _ ps fo euid,uid,egid. tty,cmd
Изменение идентификаторов EUID/EGID процесса происходит при срабатывании механизма неявной передачи полномочий, основанном на дополнительных атрибутах SUID/SGID файлов программ. При запуске таких программ посредством системного вызова exec атрибуты EUID/EGID запускающего процесса устанавливаются равными идентификаторам UID/GID владельца запускаемой программы. В результате процесс, в который будет загружена такая программа, будет обладать правами владельца программы, а не правами пользователя, запустившего эту программу.
В следующем листинге приведен типичный пример использования механизма неявной передачи полномочий при выполнении команд passwd и wall . При смене пароля пользователем при помощи программы / usr/ bin/ passwd ее процесс получает необходимое право записи ① в файл / etc/ shadow в результате передачи полномочий ❶ суперпользователя root ( UID=0) . При передаче широковещательного сообщения всем пользователям при помощи / usr/ bin/ wall необходимо иметь право записи ② в их файлы устройств / dev/ tty* , которое появляется ❷ в результате передачи полномочий группы tty ( GID = 5) .
Атрибуты файла SUID/SGID и атрибуты процесса RUID, EUID, RGID, EGID
fitz@ubuntu:~$ who
fitz pts/0 2019-11-22 00:52 (:0.0)
fitz pts/1 2019-11-22 00:53 (:0.0)
fitz pts/2 2019-11-22 01:06 (:0.0)
fitz@ubuntu:~$ ls -la /etc/shadow /dev/pts/*
crw—w—- 1 fitz tty 136, 2 ноя 19 12:00 /dev/pts/1
② crw—w—- 1 fitz tty 136, 3 ноя 19 13:53 /dev/pts/2
c——— 1 root root 5, 2 ноя 17 03:30 /dev/pts/ptmx
① -rw-r—— 1 root shadow 1647 ноя 19 12:27 /etc/shadow
fitz@ubuntu:~$ ls -l /usr/bin/passwd /usr/bin/wall
-rwsr-xr-x 1 root root 67992 авг 29 16:00 /usr/bin/passwd
-rwxr-sr-x 1 root tty 35048 авг 21 16:19 /usr/bin/wall
fitz@ubuntu:~$ ls -ln /usr/bin/passwd /usr/bin/wall
-rwsr-xr-x 1 0 0 67992 авг 29 16:00 /usr/bin/passwd
-rwxr-sr-x 1 0 5 35048 авг 21 16:19 /usr/bin/wall
fitz@ubuntu:~$ ps ft pts/1,pts/2 o pid,ruid,rgid,euid,egid,tty,cmd
PID RUID RGID EUID EGID TT CMD
27883 1006 1008 1006 1008 pts/2 bash
❷ 27937 1006 1008 1006 5 pts/2 _ wall
27124 1006 1008 1006 1008 pts/1 bash
❶ 27839 1006 1008 0 1008 pts/1 _ passwd
По отношению к объектам, доступ к которым ограничивается при помощи мандатных механизмов, возможности процесса определяются значениями его МАС‑маркера доступа, а именно — атрибутом мандатной метки LABEL. Как и RUID/EUID/RGID/EGID, атрибут LABEL назначается первому процессу сеанса пользователя явным образом, а затем наследуется при клонировании процессами‑потомками от процессов‑родителей. В примере из приведенного ниже листинга при помощи команды id показан атрибут LABEL сеанса пользователя, а при помощи команды ps — его явное наследование от процесса‑родителя.
Аналогично изменениям EUID/EGID процесса, происходящим при запуске SUID-ной/SGID-ной программы, изменение метки LABEL процесса происходит (согласно мандатным правилам ❶) в системном вызове exec при запуске программы, помеченной соответствующей мандатной меткой файла. Так, например, при запуске программы / usr/ sbin/ dhclient с типом dhcpc_exec_t ее мандатной метки ❸ процесс приобретает тип dhcpc_t своей мандатной метки ❹, в результате чего существенно ограничивается в правах доступа к разным объектам операционной системы.
MAC-маркер доступа процесса — мандатная метка selinux
fitz@ubuntu:~$ ssh lich@fedora
lich@fedora’s password:
Last login: Sat Nov 21 14:25:16 2015
[ lich@centos ~] $ id -Z
staff_u:staff_r:staff_t:s0-s0:c0.c1023
[ lich@centos ~] $ ps Zf
LABEL PID TTY STAT TIME COMMAND
staff_u:staff_r:staff_t:s0-s0:c0.c1023 31396 pts/0 Ss 0:00 -bash
staff_u:staff_r:staff_t:s0-s0:c0.c1023 31835 pts/0 R+ 0:00 _ ps Zf
staff_u:staff_r:staff_t:s0-s0:c0.c1023 31334 tty2 Ss+ 0:00 -bash
[ lich@centos ~] $ sesearch -T -t dhcpc_exec_t -c process
Found 19 semantic te rules:
.
❶ type_transition NetworkManager_t dhcpc_exec_t : process dhcpc_t;
.
[ lich@centos ~] $ ls -Z / usr/ sbin/ dhclient
❷ -rwxr-xr-x. root root system_u:object_r:dhcpc_exec_t:s0 /usr/sbin/dhclient
[ lich@centos ~] $ ps -ZC dhclient
LABEL PID TTY TIME CMD
❸ system_u:system_r:dhcpc_t:s0 2120 ? 00:00:00 dhclient
system_u:system_r:dhcpc_t:s0 4320 ? 00:00:00 dhclient
Привилегии
Еще одним важным атрибутом процесса, определяющим его возможности по использованию системных вызовов, являются привилегии процесса cababilities . Например, обладание привилегией CAP_SYS_PTRACE разрешает процессам трассировщиков strace и ltrace , использующих системный вызов ptrace , трассировать процессы любых пользователей (а не только «свои», EUID которых совпадает с EUID трассировщика). Аналогично, привилегия CAP_SYS_NICE разрешает изменять приоритет, устанавливать привязку к процессорам и назначать алгоритмы планирования процессов и нитей любых пользователей, а привилегия CAP_KILL разрешает посылать сигналы процессам любых пользователей.
Явная привилегия «владельца» CAP_FOWNER позволяет процессам изменять режим и списки доступа, мандатную метку, расширенные атрибуты и флаги любых файлов так, словно процесс выполняется от лица владельца файла. Привилегия CAP_LINUX_IMMUTABLE разрешает управлять флагами файлов i , immutable и a , append , а привилегия CAP_SETFCAP — устанавливать «файловые» привилегии запускаемых программ.
Необходимо отметить, что именно обладание полным набором привилегий делает пользователя root ( UID=0) в Linux суперпользователем. И наоборот, обычный, непривилегированный пользователь (в смысле UID≠0 ) не обладает никакими явными привилегиями (неявно он обладает привилегией владельца для всех своих объектов). Назначение привилегий процесса (здесь допущено намеренное упрощение механизма наследования и назначения привилегий при fork и exec без потери смысла) происходит при запуске программы при помощи системного вызова exec , исполняемый файл которого помечен «файловыми» привилегиями.
В примере из следующего листинга иллюстрируется получение списка привилегий процесса при помощи утилиты getpcaps . Как и ожидалось, процесс postgres ( PID=6711) , работающий от лица обычного (непривилегированного, в смысле UID≠0 ) псевдопользователя postgres , не имеет ❶ никаких привилегий, а процесс apache2 ( PID=10129) , работающий от лица суперпользователя root ( UID=0) , имеет полный ❷ набор привилегий. Однако процесс NetworkManager ( PID=646) выполняется от лица «суперпользователя», лишенного ❸ большинства своих привилегий, т. к. ему их умышленно уменьшили при его запуске (см. systemd в главе 8) до минимально необходимого набора, достаточного для выполнения его функций (xто способствует обеспечению защищенности операционной системы).
Привилегии (capabilities) процесса
fitz@ubuntu:~$ ps fo user,pid,cmd -C NetworkManager,postgres,apache2
USER PID CMD
root 10129 /usr/sbin/apache2 -k start
www-data 10131 _ /usr/sbin/apache2 -k start
www-data 10132 _ /usr/sbin/apache2 -k start
postgres 6711 /usr/lib/postgresql/11/bin/postgres -D /var/lib/postgresql/11/main .
postgres 6713 _ postgres: 11/main: checkpointer
postgres 6714 _ postgres: 11/main: background writer
postgres 6715 _ postgres: 11/main: walwriter
postgres 6716 _ postgres: 11/main: autovacuum launcher
postgres 6717 _ postgres: 11/main: stats collector
postgres 6718 _ postgres: 11/main: logical replication launcher
root 646 /usr/sbin/NetworkManager —no-daemon
fitz@ubuntu:~$ getpcaps 6711
❶ Capabilities for 6711′: =
❷ fitz@ubuntu: ~$ getpcaps 10129
Capabilities for 10129′: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_
admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,
cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_
sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_
setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,
cap_audit_read+ep
❸ fitz@ubuntu:~$ getpcaps 646
Capabilities for `646′: = cap_dac_override,cap_kill,cap_setgid,cap_setuid,cap_net_bind_service,cap_net_admin,
cap_net_raw,cap_sys_module,cap_sys_chroot,cap_audit_write+ep
В листинге ниже показан типичный пример применения отдельных привилегий там, где классически применяется неявная передача всех полномочий суперпользователя при помощи механизма SUID/SGID. Например, «обычная» утилита ping для выполнения своей работы должна создать «необработанный» raw сетевой сокет, что является с точки зрения ядра привилегированной операцией. В старых системах (актуально для Ubuntu до версии 18.10 включительно, начиная с 19.04 все уже «правильно из коробки») программа / bin/ ping наделялась атрибутом SUID ❶ и находилась во владении суперпользователя root , чьи права и передавались при ее запуске. С точки зрения защищенности системы это не соответствует здравому смыслу, подсказывающему наделять программы минимально необходимыми возможностями, достаточными для их функционирования. Для создания «необработанных» raw и пакетных packet сокетов достаточно только привилегии CAP_NET_RAW , а весь суперпользовательский набор привилегий более чем избыточен.
Делегирование привилегий программы ping
fitz@ubuntu-1804:~$ ls -l /bin/ping
❶ -rwsr-xr-x 1 root root 64424 Jun 28 11:05 /bin/ping
fitz@ubuntu-1804:~$ ping ubuntu-1804
PING ubuntu-1804 (127.0.1.1) 56(84) bytes of data.
64 bytes from ubuntu-1804 (127.0.1.1): icmp_req=1 ttl=64 time=0.074 ms
^C
— ubuntu ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.074/0.074/0.074/0.000 ms
fitz@ubuntu-1804:~$ sudo chmod u-s /bin/ping
fitz@ubuntu-1804:~$ ls -l /bin/ping
-rwxr-xr-x 1 root root 64424 Jun 28 11:05 /bin/ping
fitz@ubuntu-1804:~$ ping ubuntu-1804
ping: icmp open socket: Operation not permitted
❸ fitz@ubuntu-1804:~$ sudo setcap cap_net_raw+ep /bin/ping
fitz@ubuntu-1804:~$ getcap /bin/ping
/bin/ping = cap_net_raw+ep
fitz@ubuntu-1804:~$ ping ubuntu-1804
PING ubuntu (127.0.1.1) 56(84) bytes of data.
64 bytes from ubuntu (127.0.1.1): icmp_req=1 ttl=64 time=0.142 ms
^C
— ubuntu ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.142/0.142/0.142/0.000 ms
При отключении передачи полномочий ❷ программа / bin/ ping лишается возможности выполнять свои функции, а при назначении ей при помощи команды setcap «файловой» привилегии CAP_NET_RAW ❸ функциональность возвращается в полном объеме, т. к. приводит к установке «процессной» привилегии CAP_NET_RAW при запуске этой программы. Для просмотра привилегий, делегируемых при запуске программ, используется парная команда getcap .
Аналогично, при использовании анализаторов сетевого трафика tshark и/или wireshark, вызывающих для захвата сетевых пакетов утилиту dumpcap, требуется открывать как «необработанные» raw , так и пакетные packet сетевые сокеты, что требует той же привилегии CAP_NET_RAW . Классический способ применения анализаторов пакетов состоит в использовании явной передачи всех полномочий суперпользователя (при помощи su или sudo ) при их запуске, что опять не соответствует минимально необходимым и достаточным требованиям к разрешенным возможностям программ.
Делегирование привилегий программе tshark
fitz@ubuntu:~$ tshark
tshark: There are no interfaces on which a capture can be done
itz@ubuntu:~$ strace -fe execve tshark
execve( «/ usr/ bin/ tshark», [ «tshark»], [/ * 23 vars */]) = 0
Process 8951 attached
[ pid 8951] execve( «/ usr/ bin/ dumpcap», [ «/ usr/ bin/ dumpcap», «-D», «-Z», «none»]. ) = 0
Process 8951 detached
— SIGCHLD ( Child exited) @ 0 ( 0) —
tshark: There are no interfaces on which a capture can be done
fitz@ubuntu:~$ ls -la /usr/bin/dumpcap
-rwxr-xr-x 1 root root 104688 Sep 5 19:43 /usr/bin/dumpcap
fitz@ubuntu:~$ getcap /usr/bin/dumpcap
fitz@ubuntu:~$ sudo setcap cap_net_raw+ep /usr/bin/dumpcap
fitz@ubuntu:~$ getcap /usr/bin/dumpcap
/usr/bin/dumpcap = cap_net_raw+ep
fitz@ubuntu:~$ tshark -i wlan0
Capturing on wlan0
0.307205 fe80::895d:9d7d:f0b3:a372 → ff02::1:ff96:2df6 ICMPv6 86 Neighbor Solicitation
0.307460 SuperMic_74:0e:90 → Spanning-tree-(for-bridges)_00 STP 60 Conf. Root = 32768/0/00:25:90:74:0e:90 Cost = 0 Port = 0x8001
.
Для эффективного использования анализаторов трафика непривилегированными пользователями достаточно делегировать их процессам захвата пакетов привилегию CAP_NET_RAW при помощи «файловых» привилегий CAP_NET_RAW для программы захвата /usr/bin/dumpcap, что и проиллюстрировано в предыдущем листинге.
info
Необходимо заметить, что все это уже достаточно давно умеет проделывать инсталлятор при установке пакета wireshark-common (от которого зависят пакеты tshark и wireshark), если утвердительно ответить на вопрос инсталлятора ‘Should non-superusers be able to capture packets?’. Однако для более простого tcpdump такой услуги не предоставлено ☺.
Другие атрибуты
Переменные окружения и текущий рабочий каталог на поверку тоже оказываются атрибутами процесса, которые можно получить при помощи команд ps и pwdx соответственно.
Переменные окружения процесса
fitz@ubuntu:~$ ps fe
PID TTY STAT TIME COMMAND
21872 pts/2 S 0:00 -bash USER=fitz LOGNAME=fitz HOME=/home/fitz PATH=/usr/.
22904 pts/2 R+ 0:00 _ ps fe LANGUAGE=ru:ko:en LC_ADDRESS=ru_RU.UTF-8 .
Текущий рабочий каталог процесса
fitz@ubuntu:~$ ps fx
PID TTY STAT TIME COMMAND
22984 pts/0 S 0:00 -bash
23086 pts/0 S+ 0:00 _ man ps
23097 pts/0 S+ 0:00 _ pager
21872 pts/2 S 0:00 -bash
23103 pts/2 R+ 0:00 _ ps fx
fitz@ubuntu:~$ pwdx 23097 22984
23097: /home/fitz
22984: /home/fitz
What does pam_systemd.so do?
I switched my SD card from the fast RaspberryPi 4 to the RaspberryPi 2. Now, logging into it with ssh -vv took about 3 minutes and got stuck at pledge: network . This question discusses such behaviour and my solution was to comment out session optional pam_systemd.so in /etc/pam.d/common-session . But I would like to know what this actually does. I understand that it is related to openening a user-session but I am not familiar with why it is important. My journalctl | grep pam shows this:
Dec 29 15:59:07 RPi4 sshd[1392]: pam_unix(sshd:session): session opened for user admin by (uid=0) Dec 29 15:59:08 RPi4 systemd: pam_unix(systemd-user:session): session opened for user admin by (uid=0) Dec 29 16:02:01 RPi4 sudo: pam_unix(sudo:session): session opened for user root by admin(uid=0) Dec 29 16:02:01 RPi4 sudo: pam_unix(sudo:session): session closed for user root Dec 29 16:02:23 RPi4 sudo: pam_unix(sudo:session): session opened for user root by admin(uid=0)
asked Dec 29, 2020 at 16:33
439 5 5 silver badges 18 18 bronze badges
Have you already read the output of man pam_systemd ?
Dec 29, 2020 at 16:48
Well, I did now. But I am not able to grasp the thing. Why do I need such a module to register a user session with the systemd login manager? Why am I able to login without this module beeing active? What is the difference? Maybe the environment variables are important. Does it make a difference if my server is headless?
Dec 29, 2020 at 17:52
1 Answer 1
pam_systemd is the plug-in that makes the difference between the classic (pre-systemd) login process and the systemd -aware one.
Without it, the login process works as it did with SysVinit: if a user process daemonizes (i.e. closes its standard input, output & error streams, forks twice, then has the child process die leaving the grandchild an orphan, reparented to UID #1 by the kernel), there will be no way to track which session it belonged to. Instead of the entire login session and all its children being reliably encapsulated into a separate control group, all the processes go into the common pile. So the enforcement of per-session resource limits can’t be done either.
Also, the user run-time directory /run/user// won’t get created, which might disable some features of some desktop environments, and things that attempt to be multi-session friendly like GnuPG Agent for example. And last but not least, the user@.service system service and any per-user systemd services managed by it will not be started: there will be no session D-Bus for this user/session, nor Pulseaudio. See systemctl —user (in a system with a working per-user systemd services).
At logout, without pam_systemd being in play, the logout process won’t be able to cleanly enforce that all the processes of the user session are definitely cleaned up at end of session (should the system administrator wish so; see KillUserProcesses= in logind.conf(5) ).
You’ll also lose a simple way to plug in non-root actions to events like your user login/logout, or non-root actions conditional to things like filesystems being mounted or hotpluggable network/storage/bluetooth/other devices being present or appearing later during the login session.
If your goal is a lean, mean single-task machine, you might regard this as a successful amputation of a lot of unnecessary stuff; but depending on your goal, you might not.
Anyway, the question you referred to indicates the problem might be caused by something causing the system D-Bus to restart, which would require restarting systemd-logind and possibly other system services that depend on the system D-Bus.
The root cause for your login slowness problem might also have been something like Pulseaudio being still configured to expect RasPi 4 hardware and being confused on not finding it.
On a systemd -based installation, there may be at least two relevant instances of D-Bus for each user session: a system D-Bus that enables selective passing of system-wide status information and control as configured by polkit , and a session D-Bus for user-level things. Some GUI desktop environments may fire up a small extra D-Bus instance or two for handling accessibility and/or internationalization (input methods for Chinese/Japanese/Korean characters and similar).
Статус сервисов systemd
Перевод: systemd services status
systemctl status
Совсем недавно узнал для себя, что можно запускать systemctl status без параметров – никакого имени сервиса не указывать. И тогда получается очень полезный список в виде дерева сервисов и процессов на вашей Linux системе. Сервисы systemd
Как вы уже можете знать, в современных Linux системах уже не используются скрипты инициализации в /etc/init.d. Вместо них создаются и используются сервисы systemd – очень удобно и для обслуживания сервисов, и для подтверждения их текущего статуса (команда journalctl отлично подходит для просмотра логов).
Смотрим на сервисы systemd: команда systemctl
Без всяких дополнительных параметров, systemctl status показывает дерево вроде такого:
greys@sd-147674:~$ systemctl status ● sd-147674 State: running Jobs: 0 queued Failed: 0 units Since: Sat 2019-11-23 08:45:20 CET; 1 months 20 days ago CGroup: / ├─user.slice │ └─user-1000.slice │ ├─[email protected] │ │ └─init.scope │ │ ├─19250 /lib/systemd/systemd --user │ │ └─19251 (sd-pam) │ └─session-1309.scope │ ├─19247 sshd: greys [priv] │ ├─19264 sshd: greys@pts/0 │ ├─19265 -bash │ ├─19278 systemctl status │ └─19279 pager ├─init.scope │ └─1 /sbin/init └─system.slice ├─systemd-udevd.service │ └─361 /lib/systemd/systemd-udevd ├─cron.service │ └─541 /usr/sbin/cron -f ├─bind9.service │ └─587 /usr/sbin/named -u bind ├─systemd-journald.service │ └─345 /lib/systemd/systemd-journald ├─mdmonitor.service │ └─484 /sbin/mdadm --monitor --scan ├─ssh.service │ └─599 /usr/sbin/sshd -D ├─openntpd.service │ ├─634 /usr/sbin/ntpd -f /etc/openntpd/ntpd.conf │ ├─635 ntpd: ntp engine │ └─637 ntpd: dns engine ├─rsyslog.service │ └─542 /usr/sbin/rsyslogd -n -iNONE .
Например, видно названия systemd сервисов вроде cron.service или ssh.service, а также название процесса, который эти сервисы предоставляет, и номер этого процесса (PID).
ИНТЕРЕСНО: заметили, как сервис времени openNTPd.service теперь предоставляется аж тремя процессами сразу: ntpd и ещё две его вариации (NTP engine и DNS engine).
Полезные Ссылки
- systemd reference
- systemctl command
- journalctl command
- Linux Commands