Chamadas Remotas de
Procedimentos (RPC)
• Chamada Remota de Procedimento (RPC)
ou Chamada de Função ou Chamada de
Subrotina
• Método de transferência de controle de
parte de um processo para outra parte
• Procedimentos => permite a divisão do
programas em vários pedaços
Motivação para RPC
• Desvantagens do Socket:
– Abordagem orientada a Comunicação
– A aplicação não recebe atenção exclusiva
• Chamadas de Procedimentos Convencionais
podem representar send e receive
O Conceito de Procedimentos
main
proc1
proc5
proc2
proc3
proc4
proc6
proc7
proc8
- Um programa convencional consiste de um ou mais procedimentos, geralmente
organi-zados em uma hierarquia de chamadas.
- Uma seta de um procedimento n para um procedimento m significa uma chamada de n
para m
RPC: Programa Distribuído
- A divisão ocorre entre o programa principal e o procedimento 4.
- Um protocolo de comunicação é necessário para implementar a chamada remota.
main
proc1
proc5
proc2
proc3
proc4
proc6
proc7
proc8
RPC: Modelo de Execução
P ro grama P rin cip al P ro ced imen to A
n a máq u ina 1 n a máqu in a 2
(cliente) (servido r)
ch amad a remota
p roc. A
S aída
R espo sta
• O processo cliente fica bloqueado durante a
execução do procedimento remoto!
RPC
•
Objetivo: Tornar mais fácil a implementação de Aplicações Distribuídas
•
Esconde o código de chamadas a rede em procedimentos chamados
stubs
– Stubs -> procedimentos que contêm o código de chamadas a rede.
– Com stubs o RPC protege os programas de aplicação (cliente e
servidor) de preocupações com detalhes como sockets.
– O RPC inclui uma especificação para formato padrão dos dados
(visando interoperabilidade), e nos stubs é que acontece a conversão
dos dados
• No RPC da Sun o padrão para a representação dos dados é o
XDR (eXternal Data Representation Standard)
– Os stubs são gerados automaticamente por algum compilador.
Exemplo: O RPCGen da Sun
Passos de uma Chamada Remota
de Procedimentos
transporte de mensagens via rede
Cliente
Empacota
Parâmetros
Desempacota
Resultado
Máquina do Cliente
Stub do Cliente
KERNEL
Servidor
Empacota
Resultados
Desempacota
Parâmetros
Máquina do Servidor
Stub do Servidor
KERNEL
transporte de mensagens via rede
Chamadas Remotas de
Procedimentos (RPC)
• Idéia do modelo é estender o conceito de chamada de
procedimento convencional para ambientes distribuídos.
– a ênfase é em distribuição e não em concorrência!
– objetivo é simplificar a programação distribuída,
tornando-a semelhante à programação convencional!
• Remote Procedure Call (RPC): subrotina chamada pode
ser função ou procedimento
• Procedimentos => permitem a divisão do programas em
vários pedaços que podem executar em máquinas
arbitrárias.
RPC - Implementação
• O código das chamadas a rede é escondido em
procedimentos chamados stubs
– Stubs
-> procedimentos que contêm o código de
chamadas a rede.
– Com stubs o RPC protege os programas de aplicação
(cliente e servidor) de preocupações com detalhes como
sockets.
• Cabe aos stubs a passagem de parâmetros entre
procedimentos.
– Máquinas diferentes podem usar representações
diferentes de dados como inteiros, caracteres, etc.
– O que fazer com dados complexos, como listas, etc?
Um exemplo de RPC: Sun-RPC
• Sistema originalmente criado para máquinas Sun.
– oferecido atualmente em diversos sistemas operacionais!
• A arquitetura definida inclui:
– uma linguagem para definição das interfaces (cabeçalhos de
procedimentos, etc);
– a ferramenta RPCGEN, que gera os stubs cliente e servidor
automaticamente;
– uma biblioteca RPC, que pode ser usada diretamente na
construção de programas que não usem o PRCGEN;
– o protocolo de comunicação entre os stubs.
Sun-RPC - Tradução de dados
• Tradução entre formatos de dados:
utilização de uma representação padrão,
XDR (eXternal Data Representation
Standard).
• A conversão é especificada para um
conjunto pré-definido de tipos de dados.
formato
origem
formato
padrão
formato
destino
RPC - Mapeamento Dinâmico de
Portas (Portmapper)
• Mapeia serviços a Portas
• Em geral está na porta 111
Programa RPC
(servidor)
RPC
port mapper
Registra (programa,porta)
Socket usado
atualmente por
este programa
Socket em porta
bem conhecida
usada
RPC - Mapeamento Dinâmico de
Portas
Portmapper
daemon
svc_register
clnt_create
(1) registro
(2)
(3)
(4)
Sistema
Remoto
Sistema
Local
Passos envolvidos em uma chamada RPC
rpcgen - Funcionamento
prog.x
rpcgen
biblioteca
RPC
cc
cc
rprog.c
prog_proc.c
prog_clnt.c
prog.h
prog_svc.c
rprog
prog_svc
procedimentos servidores
cliente
especificação RPC
stub servidor
stub cliente
programa servidor
programa cliente
rpcgen
• Exemplo de arquivo de especificação:
/* date.x
especificação de serviços remotos de data e hora */
program DATE_PROG{
version DATE_VERS{
long BIN_DATE(void) = 1;
string STR_DATE(long) = 2;
} = 1;
} = 0x31234567;
Passo 1: Construir uma Aplicaçao Convencional
main
Passo 2: Dividir o programa em duas partes
insere
remove
busca
inicializar
main
proxima
Banco
de
Dados
Chamadas a
Procedimentos Remotos
Cliente
Programa Remoto
Passo 3: Criar uma Especificação Rpcgen
/* rbd.x
especificação rpc para um programa de banco de dados
que oferece os procedimentos INSERE, REMOVE e BUSCA
*/
struct example {
/* estrutura não usada, declarada para ilustrar como rpcgen */
int exfield1;
/* constrói rotinas XDR para converter estruturas */
char exfield2;
};
program RBDPROG{
/* nome do programa remoto */
version RDBVERS{
/* declaração da versão */
int INICIALIZAR(void) = 1;
/* primeiro procedimento deste programa */
int INSERE(string) = 2;
/* segundo procedimento deste programa */
int REMOVE(string) = 3;
/* terceiro procedimento deste programa */
int BUSCA(string) = 4;
/* quarto procedimento deste programa */
} = 1;
/* definição da versão do programa */
Passo 4: Rodar o Rpcgen
• rpcgen rbd.x
rpcgen
rdb_clnt.c
rdb.h
rdb_svc.c
rdb_xdr.c
Rpcgen
Arquivo .h
/* rbd.h */
struct example {
int exfield1;
char exfield2;
};
typedef sruct example example;
bool_t xdr_example();
#define RBDPROG (u_long) 0x30090949)
#define RDBVERS ((u_long) 1)
#define INICIALIZAR ((u_long) 1)
extern int *inicializar_1();
#define INSERE ((u_long) 2)
extern int *insere_1();
#define REMOVE ((u_long) 3)
extern int *remove_1();
#define BUSCA ((u_long) 4)
extern int *busca_1();
Rpcgen
Arquivo de Conversão XDR
/* rbd_xdr.c */
#include <rpc/rpc.h>
#include “rbd.h”
bool_t
xdr_example(xdrs, objp)
XDR *xdrs;
example *objp;
{
if (!xdr_int(xdrs, &objp->exfield1)) {
return(FALSE);
}
if (!xdr_char(xdrs, &objp->exfield2) {
return(FALSE);
}
return(TRUE);
}
Rpcgen
Stub do Cliente
/* rbd_clnt.c */ #include <rpc/rpc.h> #include “rbd.h” int * inicializar_1(argp, clnt) void *argp; CLIENT *clnt; {static int res;
bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, INICIALIZAR, xdr_void, argp, xdr_int, &res, TIMEOUT) != RPC_SUCCESS)
return (NULL); return (&res); } int *insere_1(argp, clnt) char **argp; CLIENT *clnt; {
static int res;
bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, INSERE, xdr_wrapstring, argp, xdr_int, &res, TIMEOUT) != RPC_SUCCESS)
return (NULL); return (&res); } int * remove_1(argp, clnt) char **argp; CLIENT *clnt; {
static int res;
bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, REMOVE, xdr_wrapstring, argp, xdr_int, &res, TIMEOUT) != RPC_SUCCESS)
return (NULL); return (&res); } int *busca_1(argp, clnt) char **argp; CLIENT *clnt; {
static int res;
bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, BUSCA, xdr_wrapstring, argp, xdr_int, &res, TIMEOUT) != RPC_SUCCESS)
return (NULL); return (&res); }
Rpcgen
Stub do Servidor
/* rbd_svc.c */#include <rpc/rpc.h> #include “rbd.h”
static void rbdprog_1();
main() { SVCXPRT *transp; (void)pmap_unset(RBDPROG, RBDVERS); transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) {
(void) fprintf(“Não pode criar serviço udp\n”); exit(1);
}
if (!svc_register(transp, RBCPROG, RBDVERS, rbdprog_1, IPPROTO_UDP)) {
(void) fprintf(“Não pode registrar tal prog.\n”); exit(1);
}
transp = svctcp_create(RPC_ANYSOCK, 0, 0); if (transp == NULL) {
(void) fprintf(“Não pode criar serviço TCP\n”); exit(1);
}
if (!svc_register(transp, RBCPROG, RBDVERS, rbdprog_1, IPPROTO_TCP)) {
(void) fprintf(“Não pode registrar tal prog.\n”); exit(1); } svc_run(); (void)fprintf(“SVC_RUn retornado \n”); exit(1); }
static void rbdprog_1(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { union { char *insere_1_arg; char *remove_1_arg; char *busca_1_arg; } argument; char *result;
bool_t (*xdr_argument) (), (*xdr_result)(); char *(*local)();
switch (rqstp->rq_proc) { case NULLPROC:
( void)svc_sendreply(transp, xdr_void,(char *) NULL); return;
case INICIALIZAR: xdr_argument = xdr_void; xdr_result = xdr_int;
local = (char *(*)())inicializar_1; break;
case INSERE:
xdr_argument = xdr_wrapstring; xdr_result = xdr_int; local = (char *(*)()) insere_1; break;
Rpcgen
Continuação Stub do Servidor
case REMOVE:xdr_argument = xdr_wrapstring; xdr_result = xdr_int; local = (char *(*)())remove_1; break;
case BUSCA:
xdr_argument = xdr_wrapstring; xdr_result = xdr_int; local = (char *(*)()) busca_1; break; default: svcerr_noproc(transp); return; } bzero((char*)&argument, sizeof(argument)); if (!svc_getargs(transp, xdr_argument, &argument)) {
svcerr_decode(transp); return;
}
result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp,
xdr_result, result )) { svcerr_systemerr(transp); }
if (!svc_freeargs(transp, xdr_argument, &argument)) { (void)fprintf(“Problema nos argumentos\n”); exit(1);
} }
Passo 5: Escrever procedimentos de Interface com o Stub
/* rbd_cif.c - inicializar, insere, remove, busca */
#include <rpc/rpc.h>
#include “rbd.h”
extern CLIENT *handle; /* handle para procedimento
remoto */
int inicializar()
{
return *inicializar_1(handle);
}
int insere(item)
char *item;
{
char **arg;
arg = &item;
return *insere_1(arg, handle);
}
• Rotinas de Interface do Cliente
int remove(item)
char *item;
{
char **arg;
arg = &item;
return *remove_1(arg, handle);
}
int busca(item)
char *item;
{
char **arg;
arg = &item;
return *busca_1(arg, handle);
}
Rotinas de Interface do Servidor
/* rbd_sif.c - inicializar_1, insere_1, remove_1, busca_1
*/
#include <rpc/rpc.h>
#include “rbd.h”
static int retcode;
int *inicializar_1()
{
retcode = inicializar();
return &retcode;
}
int *insere_1(i)
char **i;
{
retcode = insere(*i);
return &retcode;
}
int *remove_1(i)
char **i;
{
retcode = remove(*i);
return &retcode;
}
int *busca_1(i)
char **i;
{
retcode = busca(*i);
return &retcode;
}
Passo 6: Compilar e Linkar o Programa Cliente
• cc -c rbd_cif.c ---> rbd_cif.o
• cc -c rbd.c ---> rdb.o
• cc -o rbd rbd.o rbd_clnt.o rbd_xdr.o
rbd_cif.o
/* rbd.c - main, proxima*/ #include <rpc/rpc.h> #include “rbd.h”
#define RMACHINE “localhost” /* nome da máquina remota */ CLIENT *handle; /* handle para um procedimento remoto */ int main(argc, argv)
int argc; char *argv[]; {
char palavra[MAXWORD+1]; char cmd;
int plvlen; /* tamanho da palavra */
/* Seta a conexão para uma chamada remota de procedimento */ handle = clnt_create(RMACHINE, RBDPROG, RBDVERS, “tcp”); if (handle == 0) {
printf(“Não pode conectar programa remoto/n”); exit(1);
} while (1) {
plvlen = proxima(&cmd, palavra); if (plvlen < 0)
exit(0);
switch (cmd) { case ‘I’: /*inicialize */
inicializar(); printf(“BD inicializado \n”); break;
case ‘i’: /*insere */ insere(palavra);
printf(“%s Inserida \n”, palavra); break;
case ‘b’: /*busca */ if (busca(palavra))
printf(“%s foi encontrada \n”, palavra); else printf(“%s não existe \n”, palavra); break;
case ‘r’: /*remove */ if (remove(palavra))
printf(“%s removida \n”, palavra); else
printf(“%s não encontrada”\n”, palavra); break;
case ‘q’: /*quit */
printf(“programa encerrado \n”); exit(0);
default: /* entrada ilegal */ printf(“Comando inválido \n”); break; } } }
Programa Cliente
int proxima(cmd, palavra) char *cmd, *palavra; { int i, ch; ch = getc(stdin); while (ch == ‘ ‘) ch = getc(stdin); if (ch == EOF) return -1; *cmd = (char) ch; ch = getc(stdin); while (ch = ‘ ‘) ch = getc(stdin); if (ch == EOF) return -1; if (ch == ‘\n’) return 0; i = 0; while ((ch != ‘ ‘) && (ch != ‘\n’)) { if (++i > MAXWORD) {
printf(“Erro, palavra muito longa \n”); exit(1); } *palavra++ = ch; ch = getc(stdin); } return i; }
Continuação Programa Cliente
Passo 7: Compilar e Linkar o Programa Servidor
• cc -c rbd_sif.c ---> rbd_sif.o
• cc -c rbd_srp.c --->
rdb_srp.o
• cc -o rbddaemon rbd_svc.o rbd_xdr.o
rbd_sif.o rbd_srp.o
/* rbd_srp.c - inicializar, insere, remove, busca*/ #include <rpc/rpc.h>
#include “rbd.h”
/* Procedimentos remotos do servidor e dados globais */
char bd[BDSIZE][MAXWORD+1] /* armazena o dicionário de palavras */ int npalavras = 0; /* número de palavras no dicionário */ int inicializar() { npalavras = 0; return 1; } int insere(palavra) char *palavra; { strcpy(bd[npalavras], palavra); npalavras++; return npalavras; } int remove(palavra) char *palavra; { int i;
for (i=0; i<npalavras; i++)
if (strcmp(palavra, bd[i]) == 0) { npalavras--; strcpy(bd[i], bd[npalavras]); return 1; } return 0; } int busca(palavra) char *palavra; { int i;
for (i=0; i<npalavras; i++)
if (strcmp(palavra, bd[i]) == 0 ) return 1;
return 0; }