Уильям Стивенс - UNIX: взаимодействие процессов
ПРИМЕЧАНИЕ
64-разрядное целое поддерживается многими компиляторами С как формат long long int или просто long long. Многие, но не все компиляторы и операционные системы поддерживают такой формат. Поскольку в созданном заголовочном файле объявляются переменные типа longlong_t, в другом заголовочном файле должно содержаться следующее определение:
typedef long long longlong_t;
Длинное целое в XDR занимает 32 бита, но длинное целое языка С в 64-разрядных системах Unix может занимать и 64 бита (например, в модели LP64, описанной в [24, с. 27]). Имена формата XDR устарели лет на десять и не слишком соответствуют современным стандартам. Лучше, если бы они назывались как-нибудь вроде int8_t, int16_t и т. д.
4. Пять целых типов без знака. Первые 4 передаются как 32-разрядные значения, а последнее — как 64-разрядное.
5. Три типа данных с плавающей точкой. Первый передается как 32-разрядное значение, второй — как 64-разрядное, а третий — как 128-разрядное.
ПРИМЕЧАНИЕ
Четверная точность для чисел с плавающей точкой (quadruple precision) поддерживается в С для типов long double. He все компиляторы и операционные системы его воспринимают. Ваш компилятор может пропустить long double, но работать с этой переменной как с double. Поскольку в созданном заголовочном файле объявляются переменные типа quadruple, нужно создать другой заголовочный файл с объявлением
typedef long double quadruple;
В Solaris 2.6, например, нам пришлось бы включить строку
%#include <floatingpoint.h>
в начало файла спецификации RPC, потому что этот заголовочный файл содержит требуемое определение. Знак процента перед #include говорит программе rpcgen о необходимости поместить остаток строки непосредственно в создаваемый заголовочный файл.
6. Тип boolean эквивалентен целому со знаком. Заголовки RPC также определяют константу TRUE равной 1, a FALSE равной 0.
7. Перечисление (enumeration) эквивалентно целому со знаком и совпадает с типом данных enum в С. rpcgen также создает определение типа для данной переменной.
8. Скрытые данные фиксированной длины передаются библиотекой как 8-разрядные значения без интерпретации.
9. Скрытые данные переменной длины также представляют собой последовательность неинтерпретируемых данных, но количество реально передаваемых данных помещается в целочисленную переменную и посылается перед самими данными. При отправке данных такого типа (например, при заполнении списка аргументов перед вызовом RPC) следует указать длину, прежде чем делать вызов. При приеме данного типа данных следует выяснить значение длины, чтобы определить, сколько данных будет принято.
10. Строка представляет собой последовательность ASCII-символов. В памяти строка хранится как обычная строка символов языка С, завершаемая нулем, но при передаче перед ней отправляется целое без знака, в которое помещается количество символов данной строки (без завершающего нуля). При отправке данных такого типа размер строки определяется библиотекой с помощью вызова strlen. При приеме данные такого типа помещаются в строку символов С, завершаемую нулем.
11. Массив фиксированной длины любого типа передается как последовательность n элементов данного типа.
12. Массив переменной длины любого типа передается как целое без знака, указывающее количество элементов, и последовательность элементов данного типа. Максимальное количество элементов в объявлении может быть опущено. Но если это количество указать при компиляции программы, библиотека будет проверять, не превосходит ли реальная длина указанного значения m.
13. Структура передается как последовательность полей. rpcgen также создает определение типа для данного имени переменной (typedef).
14. Размеченное объединение состоит из целочисленного дискриминанта и набора типов данных (ветвей), зависящих от значения дискриминанта. В табл. 16.2 мы показываем, что дискриминант должен быть типа int, но он может быть и unsigned int, и enum, и bool (все эти типы передаются как 32-разрядные целые). При передаче размеченного объединения передается 32-разрядное значение дискриминанта, за которым следует значение той ветви, которая ему соответствует. В ветви default часто объявляется тип void, что означает отсутствие передаваемой вслед за дискриминантом информации. Ниже мы продемонстрируем это на примере.
15. Дополнительные данные представляют собой специальный тип объединения, описанный в примере из листинга 16.24. Объявление XDR выглядит как объявление указателя в языке С, и именно указатель объявляется в созданном заголовочном файле.
На рис. 16.3 сведена информация о кодировании различных типов данных в XDR.
Рис. 16.3. Кодирование типов данных в XDR
Пример: использование XDR без RPC
Приведем пример использования XDR без RPC. Мы воспользуемся стандартом XDR для кодирования структуры данных в машинно-независимое представление, в котором они могут быть обработаны другими системами. Этот метод может использоваться для написания файлов или для отправки данных по сети в машинно-независимом формате. В листинге 16.11 приведен текст файла спецификации data .х, который на самом деле является файлом спецификации XDR, поскольку мы не объявляем никаких процедур RPC.
ПРИМЕЧАНИЕ
Суффикс имени файла (.х) происходит от термина «файл спецификации XDR». Спецификация RPC утверждает, что язык RPC (RPCL) идентичен XDR в части, относящейся к описанию данных. В RPCL была добавлена только возможность описания процедур.
Листинг 16.11. Файл спецификации XDR//sunrpc/xdr1/data.x
1 enum result_t {
2 RESULT_INT = 1, RESULT_DOUBLE = 2
3 };
4 union union_arg switch (result_t result) {
5 case RESULT_INT:
6 int intval;
7 case RESULT_DOUBLE:
8 double doubleval;
9 default:
10 void;
11 };
12 struct data {
13 short short_arg;
14 long long_arg;
15 string vstring_arg<128>; /* строка переменной длины */
16 opaque fopaque_arg[3]; /* скрытые данные фиксированной длины */
17 opaque vopaque_arg<>; /* скрытые данные переменной длины */
18 short fshort_arg[4]; /* массив фиксированной длины */
19 long vlong_arg<>; /* массив переменной длины */
20 union_arg uarg;
21 };
Объявление перечисления и размеченного объединения1-11 Мы объявляем перечислимый тип с двумя значениями и размеченное объединение, использующее это перечисление в качестве дискриминанта. Если дискриминант имеет значение RESULT_INT, после значения дискриминанта передается целое число. Если дискриминант имеет значение RESULT_DOUBLE, за ним передается число с плавающей точкой двойной точности. В противном случае после дискриминанта не передается ничего.
Объявление структуры12-21 Мы объявляем структуру, состоящую из различных типов, поддерживаемых XDR.
Поскольку мы не объявляем процедур RPC, программа rpcgen не создаст заглушку клиента и заглушку сервера. Однако она создаст заголовочный файл data.h и файл data_xdr.с, содержащий функции XDR, обеспечивающие кодирование и декодирование данных, объявленных в файле data.х.
В листинге 16.12 приведен получающийся в результате работы rpcgen заголовочный файл data.h. Содержимое этого файла выглядит так, как мы и предполагали (табл. 16.2).
Листинг 16.12. Заголовочный файл data.h, созданный rpcgen из файла data.x//sunrpc/xdr1/data.h
1 /*
2 * Please do not edit this file. It was generated using rpcgen.
3 */
4 #ifndef _DATA_H_RPCGEN
5 #define _DATA_H_RPCGEN
6 #include <rpc/rpc.h>
7 enum result_t {
8 RESULT_INT = 1,
9 RESULT_DOUBLE = 2
10 };
11 typedef enum result_t result_t;
12 struct union_arg {
13 result_t result;
14 union {
15 int intVal;
16 double doubleval;
17 } union_arg_u;
18 };
19 typedef struct union_arg union_arg;
20 struct data {
21 short short_arg;
22 long long_arg;
23 char *vstring_arg;
24 char fopaque_arg[3];
25 struct {
26 u_int vopaque_arg_len;
27 char *vopaque_arg_val;
28 } vopaque_arg;
29 short fshort_arg[4];
30 struct {
31 u_int vlong_arg_len;
32 long *vlong_arg_val;
33 } vlong_arg;
34 union_arg uarg;
35 };
36 typedef struct data data:
37 /* 4the xdr functions */
38 extern bool_t xdr_result_t(XDR *, result_t*);
39 extern bool_t xdr_union_arg(XDR *, union_arg*);
40 extern bool_t xdr_data(XDR *, data*);
41 #endif /* !_DATA_H_RPCGEN */
В файле data_xdr.с объявляется функция xdr_data, вызываемая для кодирования и декодирования структуры data, которую мы определили. Суффикс имени функции _data соответствует имени нашей структуры из листинга 16.11. Первая программа, которую мы напишем, будет называться write.с. Она будет присваивать значения полям структуры data, вызывать xdr_data для кодирования всех полей в формат XDR и записывать результат в стандартный поток вывода.