Пример 25

/* Пример использования именованных "труб" (pipes) FIFO-файлов
 * для коммуникации независимых процессов
 * (FIFO - first in, first out : первым пришел - первым ушел).
 * По мотивам книги М.Дансмура и Г.Дейвиса.
 */

/* файл P_packet.h --------------------------------------------*/
#include <sys/types.h>
#include <sys/stat.h>   /* S_IFIFO */

/* структура пакета-запроса */
struct packet {
	int pk_pid;     /* идентификатор процесса-отправителя */
	int pk_blk;     /* номер блока, который надо прочитать */
	int pk_code;    /* код запроса */
};

/* request codes (коды запросов) */
#define RQ_READ         0       /* запрос на чтение */
#define CONNECT         1       /* запрос на соединение */
#define SENDPID         2       /* ответ на запрос соединения */
#define DISCONNECT      3       /* разрыв связи */
#define BYE             4       /* завершить сервер */

/* имена FIFO-каналов связи */
#define DNAME           "datapipe"
#define CNAME           "ctrlpipe"

/* размер блока информации */
#define PBUFSIZE 512

/* P_client.c --------------------------------------------------------- */
/*
 *      Процесс-клиент, посылающий запросы к серверу.
 */
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"

int datapipe, ctrlpipe;
int got_sig;
int mypid;      /* идентификатор процесса-клиента */
int spid;       /* идентификатор процесса-сервера */

/* waiting for signal */
#define WAITSIG   while( !got_sig )

void handler(nsig){
	signal( SIGUSR1, handler );
	got_sig ++;
}

void init(){
	extern void die();

	/* Ожидать создания каналов связи */
	while( (datapipe = open( DNAME, O_RDONLY | O_NDELAY )) < 0 );
	while( (ctrlpipe = open( CNAME, O_WRONLY | O_NDELAY )) < 0 );
	mypid = getpid();       /* my process identifier */
	printf( "Client pid=%d started\n", mypid );

	signal( SIGINT,  die);
	signal( SIGQUIT, die);
	signal( SIGTERM, die);

	handler(0);
}

int canRun = 1;

void die(nsig){
	canRun = 0;
}

/* подключиться к серверу, запросив его pid */
connect(){
	struct packet pk;

	pk.pk_pid = mypid;
	pk.pk_code = CONNECT;
	pk.pk_blk = (-1);

	got_sig = 0;
	write( ctrlpipe, &pk, sizeof pk ); /* послать запрос */

	/* ожидать сигнала-"толчка" */
	WAITSIG;

	/* прочитать ответ из канала данных */
	read( datapipe, &pk, sizeof pk );

	/* послать сигнал-подтверждение */
	kill( pk.pk_pid, SIGUSR1 );
	return pk.pk_pid;
}

void disconnect(){
	struct packet pk;

	pk.pk_pid  = mypid;
	pk.pk_code = DISCONNECT;
	pk.pk_blk  = (-1);

	got_sig = 0;
	write( ctrlpipe, &pk, sizeof pk );      /* send request */

	/* wait for reply */
	WAITSIG;

	/* receive reply */
	read( datapipe, &pk, sizeof pk );

	/* confirm */
	kill( pk.pk_pid, SIGUSR1 );

	printf( "Disconnected.\n" );
}

request( ptr, blk, spid )
	char *ptr;
	int blk;
	int spid;
{
	struct packet pk;

	pk.pk_pid = mypid;
	pk.pk_blk = blk;
	pk.pk_code = RQ_READ;

	got_sig = 0;
	write( ctrlpipe, &pk, sizeof pk );
	WAITSIG;
	read( datapipe, ptr, PBUFSIZE );
	kill( spid, SIGUSR1 );
}

bye(){
	struct packet pk;

	pk.pk_pid = mypid;
	pk.pk_code = BYE;
	pk.pk_blk = (-1);

	got_sig = 0;
	write( ctrlpipe, &pk, sizeof pk );      /* send request */
	exit(0);
}

/* client [номер_блока] */
main(argc, argv) char *argv[];
{
	int blk;
	char buffer[ PBUFSIZE ];

	setbuf( stdout, NULL ); /* make unbuffered */
	blk = (argv[1] ? atoi( argv[1] ) : 0);
	init();
	spid = connect();
	printf( "Client pid=%d connected to server pid=%d\n",
			mypid, spid );

	/* запрос блока номер -33 соответствует запросу "завершить
	 * работу сервера"
	 */
	if( blk == -33 )
		bye();

	/* в цикле посылать запросы на чтение блока blk */
	while( canRun ){
		request( buffer, blk, spid );
		printf( "\nBEG-------------------------------------\n" );
		fwrite( buffer, PBUFSIZE, 1, stdout );
		printf( "\nEND-------------------------------------\n" );
	}
	disconnect();   /* отключиться от сервера */
	exit(0);
}

/* P_server.c ---------------------------------------------------------*/
/*
 *      Процесс-сервер, принимающий запросы и выполняющий их.
 */

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"

int datapipe, ctrlpipe, datafile, got_sig;
char *dataname = "/etc/passwd";

/* waiting for signal */
#define WAITSIG   while( !got_sig )

void handler(nsig){
	signal( SIGUSR1, handler );     /* reset trap */
	got_sig++;
}

/* завершение работы сервера: уничтожить каналы связи */
void die(nsig){
	unlink( CNAME ); unlink( DNAME ); exit(0);
	/* Если эти файлы были открыты клиентами,
	 * то клиенты не умрут, хотя имена файлов и будут удалены!
	 */
}

main(){
	struct packet pk;
	struct packet sendpk;

	/* сделать стандартный вывод небуферизованным каналом */
	setbuf( stdout, NULL );         /* make unbuffered */

	/* создать каналы связи */
	mknod( DNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
	mknod( CNAME, S_IFIFO | 0666, 0 ); /* create FIFO */

	/* по этим сигналам будет вызываться функция die() */
	signal( SIGINT, die );
	signal( SIGQUIT, die );
	signal( SIGTERM, die );

	/* Открыть управляющий канал связи. O_NDELAY означает,
	 * что файл открывается для "чтения без ожидания",
	 * т.е. если канал пуст (нет заявок), то системный вызов
	 * read() не будет "спать", дожидаясь появления информации,
	 * а просто вернет 0 (прочитано 0 байт).
	 * Этот флаг применим также к чтению с терминала.
	 */
	ctrlpipe = open( CNAME, O_RDONLY | O_NDELAY );
	if( ctrlpipe < 0 ){
		printf( "Can't open %s\n", CNAME );
		die(0);
	}
	datafile = open( dataname, O_RDONLY );
	if( datafile < 0 ){
		printf( "Can't open %s\n", dataname );
		die(0);
	}

	/* заранее формируем пакет для ответов */
	sendpk.pk_code = SENDPID;
	sendpk.pk_pid = getpid();       /* server's pid */
	sendpk.pk_blk = (-1);

	printf( "Server pid=%d\n", getpid());

	handler(0);
	for(;;){
		int n;
		static long i = 0L;

		/* active spin loop */
		printf( "%20ld\r", i++ );

		/* опрашивать канал насчет поступления запросов */
		while((n = read( ctrlpipe, &pk, sizeof(pk))) > 0 ){
			putchar( '\n' );
			if( n != sizeof pk ){
				printf( "Wrong packet size\n" );
				continue;
			}
			/* обработать прочитанный запрос */
			process( &pk, &sendpk );
		}
	}
	die(0);
}

process( pkp, spkp )
	struct packet *pkp, *spkp;
{
	char pbuf[ PBUFSIZE ];
	/* Запись в FIFO-файл будет произведена только если
	 * он уже открыт для чтения
	 */
	datapipe = open( DNAME, O_WRONLY | O_NDELAY );

	printf( "REQUEST TYPE_%d from pid=%d blk=%d\n",
		pkp->pk_code, pkp->pk_pid, pkp->pk_blk );

	switch( pkp -> pk_code ){
	case CONNECT:   /* ответить своим идентификатором процесса */
		write( datapipe, spkp, sizeof( struct packet ));
		break;
	case RQ_READ:   /* ответить блоком информации из файла */
		/* read block # pk_blk */
		lseek( datafile, pkp -> pk_blk * (long)PBUFSIZE, 0 );
		read(  datafile, pbuf, PBUFSIZE );
		write( datapipe, pbuf, PBUFSIZE );
		break;
	case DISCONNECT: /* подтвердить отключение */
		printf( "Client pid=%d finished\n", pkp -> pk_pid );
		write ( datapipe, spkp, sizeof( struct packet ));
		break;
	case BYE:       /* завершиться */
		printf( "Server terminated.\n" );
		kill( pkp-> pk_pid, SIGKILL );
		die(0);
	default:
		printf( "Unknown packet type %d\n", pkp -> pk_code );
		break;
	}
	close( datapipe );

	/* "подтолкнуть" отправителя сигналом */
	got_sig = 0;
	kill( pkp -> pk_pid , SIGUSR1 );

	printf( "Waiting for reply...  " );
	/* ждать сигнала-подтверждения от клиента */
	WAITSIG;

	printf( "server continued\n" );
}

© Copyright А. Богатырев, 1992-95
Си в UNIX

Назад | Содержание | Вперед