Общность и общение

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

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

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

Математической моделью такого распараллеливания служит объединение семейства непересекающихся множеств. Мы разбиваем исходное множество на части, те могут в свою очередь делиться как угодно — и возникает некоторая древовидная структура, а в сумме все равно получается исходное множество. Отсюда можно усмотреть условия применимости подобной технологии:

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

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

  3. независимость обработки элементов каждого подмножества от работы в других подмножествах; в противном случае результат существенно зависит от случайных вариаций порядка вычислений в разных подсистемах — это модель квантового вычислителя;

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

Разумеется, список неполон, и копать можно до бесконечности. Но если слишком интенсивно копать — совсем уроешься, и всегда важно где-то остановиться, предположить то, что никогда не может быть строго доказано. На этом целиком стоит величественное здание современной математики — в том числе вычислительной.

Противоположный подход — пустить дело на самотек, дать возможность нескольким процессам заниматься одним и тем же, во славу свободной конкуренции. И надеяться, что в итоге все это придет к чему-то, пусть даже не очень разумному — но хоть сколько-нибудь удобоваримому. Мы уже не заботимся о совместности и независимости, нам бы только удержаться в рамках глобальных параметров... А кто параметры задает? Опять же, какие-то из участников всей этой деятельности. То есть, возникает социальное расслоение, когда одни участники оказываются почему-то главнее других. Вариантов такой организации много — но всем хорошо известно, к чему она приводит на практике. Неизбежность кризисов в развитии капиталистической экономики давным-давно установлена товарищем Марксом, и мы сейчас имеем все возможности испытывать справедливость его выводов на собственной шкуре. Но вот какая штука: кризисы, как правило, не меняют глобальных параметров самого верхнего уровня, и тем самым базарная экономика в целом оказывается относительно устойчивой. Разумеется, до поры до времени. Пока совсем в разнос не пойдет. Чтобы максимально оттянуть этот момент, появляются гибридные схемы, допускающие централизованный контроль в ограниченных масштабах. И здесь мы попадаем в главное русло (mainstream) современных технологий распараллеливания.

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

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

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

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

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

Для того, чтобы передать сообщение, процесс должен его куда-то записать; получатель должен сообщение откуда-то прочесть. Чем это отличается от разделяемых данных? По сути — ничем. Однако апологеты взаимоизоляции поднимают на щит принцип передачи параметров только по значению — и подсовывают получателю не исходный текст сообщения, а его копию, наивно полагая, что тем самым пресекают любую возможность воздействия получателя на данные отправителя. Но если сам получатель не может изменить исходное сообщение — он вполне может попросить об этом отправителя, и тот имеет право поддаться на уговоры. Тут господа-ригористы звереют и требуют, чтобы исходный текст был уничтожен сразу же после отправки, чтобы и отправитель не смог ничего с ним поделать. Однако отправитель не дурак — он может сгенерировать новое сообщение, в точности воспроизводящее прежнее, но уже с поправками, предложенными кем-то из собеседников. И все опять вернется к разделяемым данным, с учетом того, что данные — это не область памяти или сектор на диске, а нечто идеальное, что вполне может кочевать из одного места в другое, меняя носитель, но не меняя содержания. То, что Наполеон родился на Корсике, никак не зависит от количества и качества копий этой информации в миллиардах голов, книг или электронных носителей. И если кому-то вздумается усомниться в историческом факте и посчастливится найти более достоверные данные — он так или иначе внесет поправку во всеобщее понимание сути вопроса, а всевозможные материальные носители будут приведены в соответствие.

В мире нет ничего, что можно было бы надежно изолировать от всего остального. У любых двух объектов всегда найдется что-то общее — начиная с того, что они всегда принадлежат одному и тому общему для всех миру, а других миров по определению нет. Способов взаимодействия разных вычислительных процессов не меньше, чем всевозможных природных прототипов. Одна только квантовая теория дает кучу разнообразнейших примеров, даже в рамках стандартной модели. Например, в каких-то условиях процесс может вести себя как частица: передача сообщения тогда происходит путем распада частицы (завершения процесса) и рождения новых частиц (дочерних процессов), которые могут поглощаться другими частицами (получателями сообщения), меняя их состояние или вызывая новые акты рождения-уничтожения. Бывают ситуации, когда исходная частица не исчезает при испускании частицы посредника, а лишь меняет состояние движения и может потом принять "ответное сообщение" от другой частицы; так возникают коррелированные квантовые системы — аналог нынешних технологий распараллеливания. Но это частный случай, а реальность гораздо богаче. В качестве одной из альтернатив можно взять не частицу, а волну, которая взаимодействует сразу со всеми частицами, но может поглотиться одной из них, — похоже и на очередь сообщений Windows, и на обычный программный переключатель, и на некоторые сетевые протоколы... Наконец, есть вариант интерференции волн — и это возвращает нас к технике разделяемых данных. Но помимо квантов существует много другой физики, а еще и химия, и биология, и психология, и культурология, и экономика... Так что вычислительным средам есть "делать жизнь с кого".

После всех этих глобальных соображений, давайте пофилософствуем помельче, на уровне простых моделей.

В технология обмена сообщениями часто предполагается, что сообщение посылает один процесс другому процессу — а стало быть, один процесс должен знать о существовании другого и о доступных партнеру каналах коммуникации. Схема это содрана с простейших актов общения у людей. Самые примитивные уровни такого общения — отправка сигналов и команд. В более развитом варианте, возможен диалог, а значит, собеседники должны принадлежать к некоторой общности более высокого уровня — и в частности, пользоваться одним и тем же протоколом общения (тоже разделяемые данные!), говорить на одном языке. Минимальное обобщение такого парного взаимодействия — конференция, когда одно и то же сообщение поступает сразу нескольким участникам, и те имеют возможность его активно комментировать (или реагировать действием). Групповое общение, конечно, нарушает нравственную чистоту — но в ряде случаев оказывается намного более эффективным. Допустим, на сервере подняты несколько демонов, обслуживающих распределенные приложения; если по каким-то причинам связь с другими серверами прерывается — каждый такой процесс выясняет это самостоятельно, пытаясь установить внешний контакт и нарываясь на неудачу. Если же потеря (или восстановление) связи порождает сообщение, доступное сразу всем демонам, они не будут тратить время и ресурсы на дозвон, а займутся более полезной деятельностью (например, поиском альтернативных способов коммуникации или организацией буферов — так сказать, заготовкой консервов). В более развитой системе конференций сообщения обрабатываются всеми членами некоторой группы процессов и каждый участник адресуется ко всем остальным. Например, если у кого-то из участников есть информация о причинах нарушения связи и предположения о возможных сроках ее восстановления, это может влиять на организацию работы многих процессов, в том числе и того, который занимается обслуживанием внешних коммуникаций.

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

Базовый цикл жизнедеятельности процесса выглядит тогда следующим образом: проверяем наличие условий для работы → если есть возможность, выполняем какие-то из доступных операций → отсылаем результат во внешнюю среду. И все повторяется сначала. Если условий для продолжения нет, включается один из альтернативных способов деятельности — например, переход в состояние ожидания, или активный поиск ресурсов. Где? Да в той же самой среде, в которой живут и трудятся самые разнообразные процессы. Это их главный и непосредственный разделяемый ресурс, косвенным образом реализующий общность данных на всех остальных уровнях. И здесь появляется иерархия общения, связывающая процессы по самым разным параметрам, от элементарной синхронизации до обмена деятельностями (распределение нагрузки, кластеризация и т. д.), а через это — переход к совместной постановке задач, включая взаимное программирование.

В качестве примера иерархической организации можно упомянуть память. Элемент памяти вполне может работать как агент, получающий запросы на чтение, запись или управляющие действия — а результатом становится некоторое изменение во внешней среде, доступное другим процессам, которое те интерпретируют в зависимости от своих текущих потребностей — как индикацию работоспособности, как запрос на обслуживание или как сохраненные данные. Эта схема буквально воспроизводится на аппаратном уровне. В системах программирования, как правило, существуют специальные средства управления памятью, и здесь запросы посылаются не непосредственно в аппаратную среду, а в специализированный программный модуль, который опять-таки принимает запросы и выдает в ответ какие-то данные; но кроме этого занимается распределением и освобождением памяти, предотвращая возможные конфликты программных сущностей (процессы внутри программы). Что-то из этого реализовано на уровне языка программирования, какие-то компоненты включаются в исполняемый код. Различие этих уровней весьма и весьма относительно — поэтому можно полагать, что в общем случае процессы верхнего уровня не работают с памятью напрямую, а только посылают запросы в некоторую программную среду и проверяют наличие требуемых данных; если данные есть — они могут быть ассоциированы с другими данными (например, присвоение значения переменной создает связь между данными, представляющими значение, и данными, представляющими переменную) или переданы другому процессу путем перезаписи в условленное место внешней среды (например, в стек). Тут важно, что опосредованный характер получения и записи данных позволяет отвлечься от конкретного физического представления: совершенно неважно, будут ли данные храниться в оперативной памяти, или выгружены в своп, записаны на флешку или переданы по сети на деревню дедушке. Модуль управления памятью должен уметь прозрачным образом обрабатывать все эти возможности, а отдельному процессу вовсе не обязательно разбираться в деталях инфраструктуры — он стандартным образом взаимодействует со средой и получает все необходимое, — или не получает и имеет полное право болезненно на это реагировать. В общем случае в ту же схему включаются и неэлектронные носители информации, а также носители более высокого уровня — распределенные процессы в аппаратных комплексах и в сетях. То есть, данными может быть нечто такое, что принципиально непредставимо определенным состоянием физической (а также биологической или социальной) системы, но требует постоянного изменения этой системы, перехода из одного состояния в другое в соответствии с некоторым законом, который и представляет собой содержание памяти, некоторое значение, которое процесс более низкого уровня имеет право запросить у своей программной среды, получить ответ и обработать в соответствии со своими надобностями. На практике оказывается, что таких данных нам по жизни требуется очень много — с некоторого уровня развития цивилизации они начинают преобладать над элементарными данными, представимыми состояниями физических систем.

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

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


[Компьютеры] [Наука] [Унизм]