Гораздо проще имитировать exec, - достаточно вызвать CreateProcess и “прибить” текущий процесс, имитируя его замещение новым. Остается всего лишь переустановить идентификатор, сохраняя генеалогическую линию. Если этого не сделать, вновь порожденный процесс станет потомком процесса, вызвавшего exec, а в оригинальной системе UNIX функция.exec не создает дочернего процесса и сохраняет идентификатор текущего. Но идентификатор процесса выдается операционной системой и не может быть изменен по желанию приложения! Другими словами, если “Процесс 0” породил “Процесс 1” и запомнил его идентификатор, а “Процесс 1”, имитируя вызов exec, обратился к функции CreateProcess и вызвал exit для завершения своей работы, то “Процесс 0” будет по-прежнему ссылаться на уже несуществующий “Процесс 1”, ничего не зная о порожденном “Процессе 2”, обладающего иным идентификатором.
Ситуация разрешается созданием собственной таблицы идентификаторов эмулятором UNIX. Родительский процесс в качестве идентификатора получает индекс ячейки такой таблицы, содержащей настоящий идентификатор дочернего процесса. В результате появляется возможность «подменить» идентификатор «Процесса 1» на «Процесс 2» незаметно для родительского процесса. (Смотри рисунок 003.txt)
В отличие от Windows, UNIX поддерживает сигналы - удобное средство межпроцессорного взаимодействия, своеобразный аналог программно-аппаратных прерываний. Механизм же сообщений, реализуемый Windows, требует постоянного опроса очереди сообщений на предмет обнаружения новых поступлений. Так, например, при закрытии окна приложению посылается сообщение WM_CLOSE, но если оно в этот момент не читает очередь, а занято чем-то другим, скажем, форматированием очередного трека дискеты или сортировкой данных, то проигнорирует попытку закрытия, и продолжит работу вплоть до следующей проверки очереди. Поэтому, практически в каждом Windows-приложении присутствует следующий код:
Исключения составляют консольные приложения, а во всех остальных случаях никакое однопоточное приложение не может «забыть» о проверке очереди сообщений больше чем на десятую долю секунды, не вызывая протестов со стороны пользователя, ругающегося ни на что не реагирующую программу.
В UNIX все гораздо проще - операционная система автоматически прерывает работу процесса-получателя и передают управление на подпрограмму обработки сигнала, а после ее завершения возобновляет выполнение прерванного процесса с того же самого места.
К счастью, в Windows 9x/Winwos NT существует многопоточность и множество механизмов межпроцессорных взаимодействий, поэтому имитировать поддержку сигналов вполне реально. Для этого в каждом эмулируемом приложении необходимо выделить отдельный поток, ожидающий ну, например, события (
Следует отметить, функция “kill” вовсе не убивает процесс, как это следует из ее названия, а отправляет ему сигнал, который тот может либо проигнорировать, либо обработать по своему усмотрению.
Другое существенное отличие заключается в поведении кучи (