Читать интересную книгу UNIX: взаимодействие процессов - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 86 87 88 89 90 91 92 93 94 ... 128

server PID = 308, uniquifier = 18, DOOR_UNREF

solaris % doorinfo /etc/.syslog_door

server PID = 282, uniquifier = 1635

solaris % ps –f -p 308

root 308 1 0 Apr 01 ? 0:34 /usr/sbin/nscd

solaris % ps –f -p 282

root 282 1 0 Apr 01 ? 0:10 /usr/sbin/syslogd –n –z 14

Команду ps мы используем для того, чтобы узнать, какая программа выполняется с идентификатором, возвращаемым door_info.

Буфер результатов слишком мал

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

Листинг 15.4. Вывод адреса полученного результата

//doors/client2.c

1  #include "unpipc.h"

2  int

3  main(int argc, char **argv)

4  {

5   int fd;

6   long ival, oval;

7   door_arg_t arg;

8   if (argc != 3)

9    err_quit("usage: client2 <server-pathname> <integer-value>");

10  fd = Open(argv[1], O_RDWR); /* открываем дверь */

11  /* подготовка аргументов и указателя на результат */

12  ival = atol(argv[2]);

13  arg.data_ptr = (char *) &ival; /* аргументы-данные */

14  arg.data_size = sizeof(long); /* объем данных */

15  arg.desc_ptr = NULL;

16  arg.desc_num = 0;

17  arg.rbuf = (char *) &oval; /* возвращаемые данные */

18  arg.rsize = sizeof(long); /* объем возвращаемых данных */

19  /* вызов процедуры сервера и вывод результата */

20  Door_call(fd, &arg);

21  printf("&oval = %p, data_ptr = %p, rbuf = %p, rsize = %dn",

22   &oval, arg.data_ptr, arg.rbuf, arg.rsize);

23  printf("result: %ldn", *((long *) arg.data_ptr));

24  exit(0);

25 }

19-22 В этой версии программы на экран выводится адрес переменной oval, содержимое указателя data_ptr, который должен указывать на возвращаемые функцией door_call данные, и адрес и размер приемного буфера (rbuf и rsize).

Запустим эту программу, не изменяя размер приемного буфера по сравнению с листингом 15.2. Мы ожидаем, что data_ptr и rbuf будут указывать на переменную oval и rsize будет иметь значение 4 (4 байта в буфере). И действительно, вот что мы видим:

solaris % client2 /tmp/server2 22

&oval = effff740, data_ptr = effff740, rbuf = effff740, rsize = 4

result: 484

Изменим только одну строку в листинге 15.4, уменьшив размер буфера клиента до одного байта. Новый вариант строки 18 будет иметь вид:

arg.rsize = sizeof(long) – 1; /* размер буфера данных */

Запустим новую программу и увидим, что библиотека автоматически выделила место под новый буфер результатов и data_ptr теперь указывает на новый буфер:

solaris % client3 /tmp/server3 33

&oval = effff740, data_ptr = ef620000, rbuf = ef620000, rsize = 4096

result: 1089

Размер выделенного буфера равен 4096 байт, что совпадает с размером страницы в данной системе, который мы узнали в разделе 12.6. Этот пример показывает, что следует всегда обращаться к результатам через указатель data_ptr, а не через переменные, адреса которых были переданы в rbuf. В нашем примере к результату типа «длинное целое» следует обращаться как *(long*)arg.data_ptr, а не oval (что мы делали в листинге 15.2).

Новый буфер выделяется вызовом mmap и может быть возвращен системе с помощью munmap. Клиент может повторно использовать этот буфер при новых вызовах door_call.

Функция door_cred и информация о клиенте

На этот раз мы изменим нашу функцию servproc из листинга 15.3, добавив в нее вызов door_cred для получения информации о пользователе. В листинге 15.5 приведен текст новой процедуры сервера; функции main клиента и сервера не претерпевают изменений по сравнению с листингами 15.2 и 15.3.

Листинг 15.5. Процедура сервера, получающая информацию о клиенте

//doors/server4.c

1  #include "unpipc.h"

2  void

3  servproc(void *cookie, char *dataptr, size_t datasize,

4   door_desc_t *descptr, size_t ndesc)

5  {

6   long arg, result;

7   door_cred_t info;

8   /* получение и вывод информации о клиенте */

9   Door_cred(&info);

10  printf("euid = %ld, ruid = %ld, pid = %ldn",

11  (long) info.dc_euid, (long) info.dc_ruid, (long) info.dc_pid);

12  arg = *((long *) dataptr);

13  result = arg * arg;

14  Door_return((char *) &result, sizeof(result), NULL, 0);

15 }

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

solaris % client4 /tmp/server4 77 первый запуск клиента

result: 5929

solaris % su                      вход под именем привилегированного пользователя

Password:

Sun Microsystems Inc. Sun OS 5.6 Generic August 1997

solaris # cd                     каталог, в котором находится исполняемый файл

solaris # ls –l client4

-rwxrwxr-x 1 rstevens other1 139328 Apr 13 06:02 client4

solaris # chown root client4     смена владельца на привилегированного пользователя

solaris # chmod u+s client4      включение бита SUID

solaris # ls -l client4           проверка разрешений и владельца файла

-rwsrwxr-x 1 root     other1 139328 Apr 13 06:02 client4

solaris # exit

solaris % ls -l client4

-rwsrwxr-x 1 root     other1 139328 Apr 13 06:02 client4

solaris % client4 /tmp/server477 и еще раз запускаем программу-клиент

result: 5929

Если мы посмотрим, что в это время выводил сервер, то увидим следующую картину:

solaris % server4 /tmp/server4

euid = 224, ruid = 224, pid = 3168

euid = 0, ruid = 224, pid = 3176

Действующий идентификатор пользователя при втором запуске изменился. Значение 0 означает привилегированного пользователя.

Автоматическое управление потоками сервера

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

Листинг 15.6. Процедура сервера, выводящая идентификатор потока

//doors/server5.c

1  #include "unpipc.h"

2  void

3  servproc(void *cookie, char *dataptr, size_t datasize,

4   door_desc_t *descptr, size_t ndesc)

5  {

6   long arg, result;

7   arg = *((long *) dataptr);

8   printf("thread id %ld, arg = %ldn", pr_thread_id(NULL), arg);

9   sleep(5);

10  result = arg * arg;

11  Door_return((char*)&result, sizeof(result), NULL, 0);

12 }

Здесь используется новая функция из нашей библиотеки — pr_thread_id. Она принимает один аргумент (указатель на идентификатор потока или нулевой указатель вместо идентификатора вызвавшего потока) и возвращает идентификатор этого потока (обычно небольшое целое число, но всегда в формате длинного целого). Процессу всегда можно сопоставить целое число — его идентификатор. Хотя мы и не знаем, к какому типу принадлежит идентификатор процесса (int или long), мы просто преобразуем значение, возвращаемое getpid, к типу long и выводим значение (листинг 9.2). Однако идентификатор потока принадлежит к типу pthread_t, который не обязательно является одним из целых типов. И действительно, в Solaris 2.6 идентификаторами потоков являются короткие целые, тогда как в Digital Unix используются указатели. Однако часто возникает необходимость сопоставлять потокам небольшие целые числа для задач отладки (как в данном примере). Наша библиотечная функция, текст которой приведен в листинге 15.7, решает этот вопрос.

Листинг 15.7. Функция pr_thread_id: возвращает небольшой целочисленный идентификатор потока

//lib/wrappthread.c

245 long

246 pr_thread_id(pthread_t *ptr)

247 {

248 #if defined(sun)

249  return((ptr == NULL) ? pthread_self() : *ptr); /* Solaris */

250 #elif defined(__osf__) && defined(__alpha)

251  pthread_t tid;

252  tid = (ptr == NULL) ? pthread_self() : *ptr; /* Digital Unix */

253  return(pthread_getsequence_np(tid));

254 #else

255  /* прочие системы */

256  return((ptr == NULL) ? pthread_self() : *ptr);

257 #endif

258 }

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

1 ... 86 87 88 89 90 91 92 93 94 ... 128
На этом сайте Вы можете читать книги онлайн бесплатно русская версия UNIX: взаимодействие процессов - Уильям Стивенс.

Оставить комментарий