Процессы и с чем их едят




Процессы и IPC

Сразу хочу огорчить программистов под Windows. К сожалению, некоторые из описанных ниже рецептов под Windows работать не будут. Я и сам долго скрежетал зубами, когда в ответ на переопределение STDOUT с помощью разветвляющего open в логи валились сообщения о том, что, мол, нет такой команды. Кого уж тут винить, не знаю, и искать не собираюсь. А советую всем, дабы не тратить свое драгоценное время, писать свои программы с расчетом на UNIX. Честно говоря, после всего, что я пережил, программируя под Windows, и узнав, что хостинг на IIS гораздо дороже (как очень редкий зверь что ли?) чем на UNIX-ах, я чуть из кресла не вывалился.

Процессы и с чем их едят

Давным-давно в одной далекой галактике жил-был DOS. И было у него всего-навсего 640 килов памяти, и мог выполнять он лишь только одну задачу. И появился грозный монстр Windows, который разделил процессорное время на части. Одну часть отвел он сонмищу процессов, а другую часть – сонмищу потоков. И перекликались они с помощью мьютексов и семафоров. А данные передавались где как. Была там и общая куча (heap), и пайпы кой где работали… В общем рассказывать дальше думаю не имеет смысла. Еще много чего можно написать - все это скучно и отвлекает от основных целей.

Так как все здесь описанное связанно с Perl, который проектировался с ориентировкой на UNIX-системы, все системные фишки, которые здесь будут рассмотрены, относятся именно к UNIX-системам. Конечно, реализация Perl для Windows скрывает кое-какие несоответствия, но далеко не все. Так что, сильно не огорчайтесь, если что-то не работает. Безвыходных ситуаций не бывает. Пишите в форум, а еще лучше побольше экспериментируйте – тогда все получится.

Итак, процесс – это программа, код которой выполняется независимо от других программ. Может быть это и не академическое определение, но ведь и наша задача не обозвать это правильно, а понять – как это работает и какая нам может быть от этого польза. Дабы не углубляться в системные тонкости, представим ситуацию на известных нам примерах.

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

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

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

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

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

fork;

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

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

Так вот, вернемся к нашим баранам. Одному процессу достается значение, идентифицирующее порожденный процесс. То есть, дописываем в бумажку первого человека фразу типа "у второго номер 2". Это бывает необходимым, в случае если подразумевается взаимодействие с порожденным процессом. Порожденный процесс, получает от fork значение 0. Но возможны такие ошибочные ситуации, когда создать процесс не удалось. Возвращаясь к нашему примеру, человек заходит за угол и… Ничего не происходит. Человек думает, странно, здесь на меня должен упасть кирпич, видимо погода нелетная. Разворачивается и идет домой. Когда fork не возвращает значения (undef), значит породить процесс не удалось. Это, несомненно, ошибочная ситуация и ее необходимо обработать. В общем случае вызов fork должен выглядеть так

unless defined(fork) { print "обработайте ошибку, или вместо этого вызовите die" }

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

#!/usr/bin/perl –w

# fork.pl

die "Non-flying weather" unless defined(fork);

print "I'm number $$\n";

В результате выполнения вы увидите что-то вроде

[root@avalon tests]#./fork.pl

I'm number 6773

I'm number 6774

Так как процессы полностью дублируются, каждый процесс получает собственную копию данных, включая файловые дескрипторы и дескрипторы потоков ввода-вывода. Таким образом, оператор print в обоих процессах работает с одним и тем же дескриптором. Если кто не знает, встроенная переменная $$ содержит идентификатор текущего процесса Perl. Кстати, в качестве домашнего задания, можете добавить код, показывающий значение переменной $$ до выполнения fork. Тогда вы увидете, какой процесс является родительским и сделать вывод - в какой последовательности два дубликата начинают работать.

Да, чуть не забыл, важно завершать процесс оператором exit. Ну, думаю, хватит с вас бесполезной писанины. Пора заняться чем-нибудь более серьезным.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-07-23 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: