/* Общение процессов при помощи общей памяти и семафоров.
* Вызов: shms &
* shmc a & shmc b & shmc c &
*/
/* --------------------------- файл shm.h ----------------------- */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>
#include <errno.h>
extern errno; /* Системный код ошибки */
struct connect { /* Структура почтового ящика */
int pid; int msgnum; int max;
char message[128]; /* текст сообщения */
};
#define NSEMS 3 /* число семафоров */
/* Имена семафоров */
#define EMPTY 0 /* 1 - ящик пуст; 0 - содержит письмо */
#define NOTEMPTY 1 /* негатив для EMPTY */
#define ACCESS 2 /* 1 - ящик доступен (закрыт);
* 0 - ящик уже открыт кем-то еще */
/* Значения семафоров */
#define YES 1
#define NO 0
/* Операции */
#define OPEN 1
#define CLOSE (-1)
#define TEST_NO 0
#ifdef COMMENT
Алгоритм одновременного изменения семафоров: semop
Дано:
аргумент: число семафоров : nsems
аргумент: величины изменения : sem_op[i]
в ядре: текущие значения семафоров группы sem_id: sem[i]
Алгоритм:
again: Сохранить значения всех семафоров (для отмены изменений);
for(i=0; i<nsems; i++)
/* OPEN */ if( sem_op[i] > 0 ){
sem[i] += sem_op[i];
разбудитьЖдущихСобытие( "sem[i]++" );
/* CLOSE */ }else if( sem_op[i] < 0 ){
if((newsm = sem[i] + sem_op[i]) >= 0 ){
sem[i] = newsm;
if( sem[i] == 0 )
разбудитьЖдущихСобытие( "sem[i]==0" );
}else{
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]++" );
goto again;
}
/* TEST0 */ }else{ /* sem_op[i] == 0 */
if( sem[i] != 0 ){
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]==0" );
goto again;
}
}
Алгоритм синхронизации в нашей схеме КЛИЕНТ-СЕРВЕР:
|----------------------------------------------------------------|
|семафоры: EMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO YES |
|сделать: NO(test0) NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO NO |
|сделать: YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: YES YES |
|сделать: YES(test!=0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO |
|сделать: NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
К сожалению, операции test!=0 не существует - приходится вводить
дополнительный семафор NOTEMPTY, негативный для EMPTY:
|----------------------------------------------------------------|
|семафоры: EMPTY NOTEMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES NO YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO - YES |
|сделать: NO(test0) - NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO YES NO |
|сделать: YES(open) NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: - NO YES |
|сделать: - NO(test0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO NO |
|сделать: NO(close) YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
#endif /*COMMENT*/
/* Общая часть сервера и клиента ------------------------------- */
key_t key = 1917; /* Уникальный ключ для доступа */
int shm_id; /* Дескриптор для доступа к общей памяти */
int sem_id; /* Дескриптор для доступа к семафорам */
char name[40]; /* имя программы */
char far *addr;
struct connect far *caddr;
struct sembuf ops[NSEMS];
/* EMPTY NOTEMPTY ACCESS */
short values[NSEMS] = { YES, NO, YES };
void semtell(msg, name) char *msg, *name; { int i;
semctl(sem_id, NSEMS, GETALL, values);
printf( "%s %-10s: значения семафоров:", name, msg);
for(i=0; i < NSEMS; i++) printf( " %d", values[i]);
putchar('\n');
}
void inisem(){
register i;
for(i=0; i < NSEMS; i++ ) ops[i].sem_flg = 0;
}
/* --------------------------- файл shms.c ----------------------- */
/* Shared memory server */
#include "shm.h"
int npack; /* номер сообщения */
void cleanup(sig){
/* Уничтожить сегмент общей памяти (это нужно делать явно) */
shmctl( shm_id, IPC_RMID, NULL );
/* Уничтожить семафоры */
semctl( sem_id, NSEMS, IPC_RMID, NULL );
if( npack ) printf( "\t** Всего было %d сообщений **\n", npack+1);
exit(0);
}
void main(){
register i; int pid = getpid();
FILE *fout;
sprintf( name, "Server-%03d", pid );
for( i = 1; i <= SIGTERM; i++ )
signal( i, cleanup );
/* Создать разделяемый сегмент */
if((shm_id = shmget( key, sizeof(struct connect),
0644 | IPC_CREAT )) < 0 ){
perror( "shmget" ) ; exit(1);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); cleanup();
}
caddr = (struct connect far *) addr;
/* Создать группу из NSEMS семафоров */
if((sem_id = semget( key, NSEMS, 0644 |IPC_CREAT |IPC_EXCL)) < 0){
if(errno == EEXIST){ printf( "Сервер уже запущен\n");exit(2); }
else{ perror( "semget" ); cleanup(); }
}
/* Загрузить начальные значения семафоров */
semctl( sem_id, NSEMS, SETALL, values );
setbuf(stdout, NULL);
inisem(); printf( "Server is up now. Читай файл MESSAGES.\n");
fout = fopen( "MESSAGES", "w");
for(;;npack++){
printf( "%s: ждет почты\n", name );
semtell("Вход", name);
ops[0].sem_num = EMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS; ops[1].sem_op = CLOSE;
semop( sem_id, ops, 2 /* сразу два семафора */);
printf( "%s: GOT-%02d/%02d от %d \"%s\"\n", name,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
fprintf( fout, "#%03d %02d/%02d от %d \"%s\"\n", npack,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
if( ! strcmp(caddr->message, "-exit" )){
printf( "%s: завершает работу.\n", name );
cleanup();
}
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = OPEN;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = CLOSE;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
semop( sem_id, ops, 3 /* сразу три семафора */);
}
/*NOTREACHED*/
}
/* --------------------------- файл shmc.c ----------------------- */
/* Shared memory client */
#include "shm.h"
void ignsigs(sig){
register i;
for( i = 1; i <= SIGTERM; i++ )
signal( i, ignsigs );
printf( "Клиент игнорирует сигналы,\n\
чтобы не оставлять закрытых семафоров в случае своей смерти.\n" );
}
void main(argc, argv) char **argv; {
int pid = getpid();
int i, ntimes = 60;
if( argc < 2 ){
fprintf( stderr, "Вызов: %s сообщение [числоПовторов]\n", argv[0] );
fprintf( stderr, "сообщение \"-exit\" завершает сервер\n");
fprintf( stderr, "сообщение \"-info\" выдает значения семафоров\n");
exit(1);
}
if( argc > 2 ) ntimes = atoi(argv[2]);
sprintf( name, "Client-%03d", pid);
ignsigs(); srand( pid );
/* Получить доступ к разделяемому сегменту */
if((shm_id = shmget( key, sizeof(struct connect), 0644)) < 0 ){
perror( "shmget" ); exit(2);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); exit(3);
}
caddr = (struct connect far *) addr;
/* Получить доступ к семафорам */
if((sem_id = semget( key, NSEMS, 0644)) < 0 ){
perror( "semget" ); exit(4);
}
setbuf(stdout, NULL);
inisem();
if( !strcmp(argv[1], "-info")){
semtell("Информация", name); exit(0);
}
for( i=0; i < ntimes; i++ ){
printf( "%s: ждет пустого ящика\n", name);
semtell("Вход", name);
ops[0].sem_num = NOTEMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS ; ops[1].sem_op = CLOSE;
if( semop( sem_id, ops, 2 /* сразу два семафора */) < 0)
goto err;
caddr->pid = pid; caddr->msgnum = i; caddr->max = ntimes;
strncpy( caddr->message, argv[1],
sizeof(caddr->message) - 1);
printf( "%s: PUT-%02d \"%s\"\n", name, i, argv[1]);
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = CLOSE;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = OPEN;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
if( semop( sem_id, ops, 3 /* сразу три семафора */) < 0)
goto err;
if( rand()%2 ) sleep(2); /* пауза */
}
shmdt( addr ); /* Отключиться от общего сегмента */
exit(0);
err:
perror("semop");
exit(5);
}
© Copyright А. Богатырев, 1992-95
Си в UNIX
Назад | Содержание | Вперед