Пример 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 www.abyss-group.narod.ru
Си в UNIX

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

Hosted by uCoz