Эффективное программирование TCP-IP

       

Система буферов в разделяемой памяти


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

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

#include “etcp.h”

void  init_smb( int init_freelist);

void *smballoc( void );

Возвращаемое значение: указатель на буфер в разделяемой памяти.

void smbfrее( void *smbptr );

void smbsend( SOCKET s, void * smbptr );

void *smbrecv( SOCKET s );

Возвращаемое значение: указатель на буфер в разделяемой памяти.

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

Для получения буфера в разделяемой памяти служит функция smballoc, воз­вращающая указатель на только что выделенный буфер. Когда буфер уже не ну­жен, процесс может вернуть его системе с помощью функции smb_frее.

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



В данной системе для передачи индексов буферов используется TCP в качестве механизма IPC, но это не единственное и даже не оптимальное решение. Так Удобнее, поскольку этот механизм работает как под UNIX, так и под Windows, к тому же можно воспользоваться уже имеющимися средствами, а не изучать другие методы IPC. В системе UNIX можно было бы применить также сокеты в адрес­ом домене UNIX или именованные каналы. В Windows доступны SendMessage, QueueUserAPC и именованные каналы.


Начнем рассмотрение реализации с функций smballoc и smbfrее (листинг 3.28).

Листинг 3.28. Функции smballoc и smbfree

1    #include "etcp.h"

2    #define FREE_LIST  smbarray[ NSMB ].nexti

3    typedef  union

4    {

5    int nexti;

6    char buf[ SMBUFSZ ];

7    }smb_t;

8    smb_t *smbarray;

9    void *smballoc( void )

10   {

11   smb_t *bp;

12   lock_buf();

13   if ( FREE_LIST < 0 )

14   error( 1, 0, "больше нет буферов в разделяемой памяти\n" }

15   bр = smbarray + FREE_LIST;

16   FREE_LIST = bp->nexti;

17   unlock_buf ();

18   return bp;

19   }

20   void smbfree( void *b )

21   {

22   smb_t *bp;

23   bp = b;

24   lock_buf();

25   bp->nexti = FREE_LIST;

26   FREE_LIST  =  bp  -  smbarray;

27   unlock_buf();

28   }

Заголовок

2-8 Доступные буфера хранятся в списке свободных. При этом в первых sizeof( int ) байтах буфера хранится индекс следующего свободного буфера. Такая организация памяти отражена в объединении smb_t. В конце массива буферов есть одно целое число, которое содержит либо индекс первого буфера в списке свободных, либо -1, если этот список пуст. Доступ к этому числу вы получаете, адресуя его как smbarray [ NSMB ] . nexti. Для удобства это выражение инкапсулировано в мак­рос FREE_LIST. На сам массив буферов указывает переменная smbarray. Это, по сути, указатель на область разделяемой памяти, которую каж­дый процесс отображает на свое адресное пространство. В массиве ис­пользованы индексы, а не адреса элементов, так как в разных процес­сах эти адреса могут быть различны.

smballoc

12 Вызываем функцию lock_buf, чтобы другой процесс не мог обратиться к списку свободных. Реализация этой функции зависит от системы. В UNIX будут использованы семафоры, а в Windows - мыотексы.

13-16 Получаем буфер из списка свободных. Если больше буферов нет, то выводим диагностическое сообщение и завершаем сеанс. Вместо этого можно было бы вернуть NULL.

17-18 Открываем доступ к списку свободных и возвращаем указатель на буфер.



smbfree

23- 27 После блокировки списка свободных, возвращаем буфер, помещая его индекс в начало списка. Затем разблокируем список свободных и воз­вращаем управление.

Далее рассмотрим функции smbsend и smbrecv (листинг 3.29). Они посылают и принимают целочисленный индекс буфера, которым обмениваются процессы. Эти функции несложно адаптировать под иной механизм межпроцессного вза­имодействия.

Листинг 3.29. Функции smbsend и smbrecv

smb.c

1    void smbsend( SOCKET s, void *b )

2    {

3    int index;

4    index = ( smb_t * )b - smbarray;

5    if ( send( s, ( char * )&index, sizeoff (index ), 0 ) < 0 )

6      error( 1, errno, "smbsend: ошибка вызова send" );

7    }

8    void *smbrecv( SOCKET s )

9    {

10   int index;

11   int rc;

12   rc = readn( s, ( char * )&index, sizeoff index ) );

13   if ( rc == 0 )                 *,

14   error( 1, 0, "smbrecv: другой конец отсоединился\n" };

15   else if ( rc != sizeof( index ) )

16   error( 1, errno, "smbrecv: ошибка вызова readn" );

17   return smbarray + index;

18   }

smbsend

4-6 Вычисляем индекс буфера, на который указывает Ь, и посылаем его другому процессу с помощью send.

smbrecv

12-16 Вызываем readn для чтения переданного индекса буфера. В случае ошибки чтения или при получении неожиданного числа байт, выводим сообщение и завершаем работу.

17 В противном случае преобразуем индекс буфера в указатель на негеи возвращаем этот указатель вызывающей программе.


Содержание раздела