Tags: os

поумнеть

О динамическом связывании

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

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

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

Кроме того, приходится много парсить, как сами бинарники, так и 'хорошо известные' пути к библиотекам, что явно не очень быстро. И зачем всё это? Только для того, чтоб некоторый общий-стандартный бинарник мог запуститься на нашей-конкретной системе, которая, возможно, имеет отличия от той системы, в которой собирался тот бинарник. А у библиотек могут быть всякие циклические зависимости, что делает процесс загрузки более весёлым и продолжительным. Итого, имеем кучу нетривиальных действий при в общем-то довольно скудном результате. Честного 'execute in plase' нет ни у кого, даже у ДОСа и то какие-то нездоровые перверсивные фиксапы есть.

Получается, что почти все плюсы динамической компоновки съедаются накладными расходами. А ведь бинарник может и в процессе работы позвать какой-нибудь dlopen()... Совсем мрак выходит. :)

Что предлагаю я: Простой и эффективный ipc, никакой динамической компоновки. В адресном пространстве нового процесса заранее лежит маленький код простой процедурки с функционалом, аналогичным некоему dlopen+getprocaddress.
Процесс тупо стартует, по мере надобности обращается к сторонним сервисам, которые живут в виде сторонних процессов в отдельных адресных пространствах.

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

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

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

Правда, при этом придется чуть аккуратнее продумывать архитектуру чтоб, например, не дергать внешний процесс ради чтения каждого байта из внешнего 'хранилища', но при этом и отлаживаться будет намного проще, и данные-код будут разделены, и мега-супер-библиотека в памяти будет ровно в одном экземпляре, причём, ровно в том же виде, что и на диске. (это нам даст и исполнение на месте, и тупой ммап вместо парсинга при загрузке и никакого сложного выноса в своп кусков кода)

Вот. Комментарии?