Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Переназначение хэндлов для доступа к каналу

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

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

int dup2(hsource, htarget).

Она создает новый хэндл для дескриптора, заданного хэндлом hsource, и размещает эту копию на месте с номером htarget. Возвращаемое значение, равное -1, указывает на неудачу выполнения функции. Существенно, что сами хэндлы при этом не меняются, изменяются специализированные структуры описателей открытых файлов в рабочей области ОС. Дело в том, что между числовым значением хэндла и дескриптором имеется промежуточное звено – специальная таблица соответствия, расположенная в области памяти, не доступной для прикладного процесса. Конкретный вид таблицы зависит от операционной системы. Числовому значению хэндла соответствует номер строки этой таблицы (в большинстве ОС хэндл является индексом строки в такой таблице). При дублировании хэндлов с помощью функции dup2 та из имеющихся строк этой таблицы, номер которой соответствует хэндлу hsource, дублируется в строку, задаваемую htarge.

Кроме рассмотренной функции dup2, для копирования хэндлов файлов имеется еще одна разновидность функции, называемая в Unix dup и имеющая прототип

int dup(hsource).

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

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

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

Следующая пара программ для родительского в листинге 11.2.1 и дочернего в листинге 11.2.2 процессов демонстрирует описываемую технику переназначения хэндлов при организации работы с каналами.

 

#include <stdio.h>

#define PIPESIZE 16

 

int main()

{int hpipe[2], hstdout=1, hsave;

int rc, cbRead;

char bufread[PIPESIZE];

 

if(pipe(hpipe)= = -1)

{printf(“Error create Pipe\n”);getchar();exit(1);};

hsave=dup(hstdout);

dup2(hpipe[1], hstdout);

rc=fork();

if (!rc)

{close(hpipe[0]);

execl(“childpip.exe”,0);}

close(hpipe[1]);

dup2(hsave, hstdout);

do

{cbRead=read(hpipe[0], bufread, 4);

sleep(1);

bufread[cbRead]=’\0';

printf(“%s\n”,bufread);

}

while (cbRead!=0);

printf(“\nEnd work with pipe\n”); return 0;

}

Листинг 11.2.1. Программа родительского процесса с переназначаемыми хэндлами

#include <stdio.h>

int main()

{int k, cb;

char text[ ]=”Мой дядя самых честных правил, \

когда не в шутку занемог, он уважать себя заставил \

и лучше выдумать не мог...”;

char *ptxt;

int hstdout=1;

sleep(2); ptxt=text;

for (k=0; k<12; k++)

{fprintf(stderr,”————I am Child... (k=%d)\n\r”, k);

cb=write(hstdout, ptxt, 9);

ptxt +=9; sleep(1);

}

close(hstdout); return 0;

}

Листинг 11.2.2. Программа childpip.exe дочернего процесса

 

В программе родительского процесса после создания канала с массивом хэндлов hpipe выполняется дублирование хэндла стандартного вывода в хэндл с номером, выбранным программистом. (Практически копируется строка таблицы соответствия числовых значений хэндлов и дескрипторов файлов, именно строка с номером хэндла стандартного вывода.) Этот хэндл, задаваемый переменной hsave, имеет фиксированное значение. Далее копируется хэндл конца вывода канала в хэндл hstdout (более точно – строка таблицы для хэндла конца вывода в строку, соответствующую номеру hstdout). Последнее значение является стандартным для всех процессов Unix и численно равно 1, что позволяет в дальнейшем использовать значение этого хэндла в дочернем процессе без передачи собственно его значения. Теперь в передающий конец канала можно записывать данные, используя при этом хэндл, равный 1 (фиксированное значение переменной hstdout в нашей программе).

После этого процесс разветвляется на собственно родительский и дочерний с помощью вызова fork(). Дочерний процесс, идентифицируя себя по возвращаемому fork() нулевому значению, закрывает свою копию hpipe [0] хэндла принимающего конца канала и меняет программу на childpip.exe с помощью системного вызова execl(“childpip.exe”,0). Дальнейшие действия выполняет программа, приведенная в листинге 11.2.2. Описанный в ней текст пересылается порциями в 9 байтов через канал, используя вызов функции read в виде write(hstdout, ptxt, 9). Собственно запись в канал производится с помощью хэндла hstdout, который хотя и содержит стандартное значение 1, с учетом предыдущих действий по копированию хэндлов, задает передающий конец канала. Доступ к последующим порциям организуется через вспомогательный указатель ptxt, вначале установленный на начало текста, а затем наращиваемый на переданное число байтов. Для наглядности процесса передачи по каналу задается задержка на 1 с. после передачи каждой порции байтов. По завершению цикла передачи выполняется закрытие дескриптора с хэндлом hstdout, закрывая тем самым передающую сторону канала и вызывая далее на его принимающем конце ситуацию конца файла.

Родительский процесс в программе листинга 11.2.1, определив себя как именно родительский по неравному нулю значению переменной rc, которое возвращается вызовом fork(), переходит в цикл многократного чтения из принимающего конца канала с помощью оператора cbRead=read(hpipe[0], bufread, 4). В этом операторе используется хэндл принимающего конца канала и производится чтение с запросом четырех байтов. Прочитанная порция данных выдается на экран консоли функцией printf. Завершение цикла определяется по получении нулевого значения cbRead функции read при чтении из принимающего конца канала.

Заметим, что при необходимости дальнейшего использования стандартного вывода в родительском процессе целесообразно выполнить оператор dup2(hsave, hstdout), который восстановит ситуацию, когда хэндл hstdout автоматически задает дескриптор стандартного вывода. Альтернативой такому решению является использование хэндла hsave для доступа к стандартному выводу, что обеспечивает все требуемые возможности, но менее наглядно.

В операционных системах Windows также имеется системная функция дублирования хэндлов, причем здесь она имеет потенциально большее значение. Это связано с тем, что множество типов системных объектов в этих ОС доступны через их хэндлы и последние не специализированы. Указанные принципиальные решения позволяют поэтому дублировать хэндлы не только файлов, как в других ОС, но и практически любые хэндлы системных объектов. Такие возможности, по существу, вынужденно, заложены разработчиками Windows для получения прин­ципиаль­ных возможностей добраться из одного процесса до системных объектов других родственных процессов.

Упомянутая системная функция имеет прототип

BOOL DuplicateHandle(HANDLE hSourceProcessHandle,

HANDLE hSourceHandle, // handle to duplicate

HANDLE hTargetProcessHandle, // handle to process to duplicate to

HANDLE *TargetHandle, // pointer to duplicate handle

DWORD DesiredAccess, // access for duplicate handle

BOOL InheritHandle, // handle inheritance flag

DWORD Options); // optional actions.

Параметр Options в простейших случаях задается равным нулю. Эта функция берет строку в таблице дескрипторов объектов для исходного процесса, задаваемого хэндлом hSourceProcessHandle, а именно строку, определяемую в таблице хэндлом hSourceHandle. Она создает копию этой строки в таблице дескрипторов объектов для процесса с хэндлом hTargetProcessHandle, причем необходимо, чтобы процесс, выполняющий вызов функции DuplicateHandle, имел в качестве действующих хэндлы обоих упомянутых процессов. (Так, получается для процесса, создавшего дочерний процесс, который далее рассматривается как процесс с хэндлом hTargetProcessHandle.) Аргумент TargetHandle используется для возвращения значения хэндла, под номером которого размещается во второй таблице копия исходного дескриптора.

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

Для наших текущих целей построения и использования программного канала, соединяющего родительский и дочерний процесс, только что рассмотренная функция дублирования хэндлов оказывается бесполезной. Эта бесполезность связана с тем, что в Windows процессы изолированы друг от друга значительно сильнее, чем в других ОС и, в частности, отсутствуют средства получения действующего хэндла родительского процесса. Родительский процесс может построить копию своего дескриптора, который возникает при создании программного канала, путем вызова функции DuplicateHandle и разместить эту копию в таблице дескрипторов дочернего процесса, но последний не имеет непосредственных средств получить значение этого хэндла, а хэндлы стандартных устройств не имеют здесь постоянных значений. (Теоретически для передачи этого значения хэндла для копии дескриптора можно использовать другие средства межпроцессорных взаимодействий, но это существенно более сложный путь, чем рассмотренное далее более простое решение.)

 

<== предыдущая лекция | следующая лекция ==>
Неименованные коммуникационные каналы Unix | Неименованные каналы в Windows
Поделиться с друзьями:


Дата добавления: 2014-01-05; Просмотров: 351; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.024 сек.