UNIVERSIDADE LUTERANA DO BRASIL
CENTRO DE CIÊNCIAS NATURAIS E EXATAS
DEPARTAMENTO DE INFORMÁTICA
AMBIENTE DE MULTIPROCESSAMENTO
PARA UMA REDE DE MÁQUINAS UNIX:
MANUAL DE IMPLEMENTAÇÃO
por
ELGIO SCHLEMER
Prof. Roland Teodorowitsch
Orientador
Canoas, dezembro de 1996
Agradeço à meu s pais, Ern o e Eroni, pelo
apoio
incondicion al
em
tod os
os
momentos; aos meus professo res, em
esp ecial a Roland T eodorowitsch , pela
dedicada orient ação; a Miguel Roberto
Bor ges, por sua incansável col aboração e
cessão de recursos; e acim a de tudo ,
agradeço à Deu s, por ter colocado essas
pessoas em meu caminho.
“Senhor, deste−me tan to! Dá−m e uma
coisa a mais: um coração agradecido”.
(Geor ge Herbert )
SUMÁRIO
LISTA DE FIGURAS
RESUMO
Este trabalho descreve a implementação de um ambiente que transforma
uma rede UNI X em um mult iprocessador virtu al, assim co mo faz o PVM.
Basi cament e, o ambiente é compost o de uma bi bliot eca, l incada com
programas dos usuários e de um processo que control a a execução e
comunicação entre processo s no sistema.
ABSTRACT
Thi s wor k describes th e implementation of a environ ment that turn s a
UNIX network into a vir tual m ultipr ocessor, li ke PVM does.
Basi cally , the environm ent is i ntegrated by a l ibrar y linked with th e
users’ progr ams and a pr ocess that controls the execution and communicatio ns
between process in the system .
1INTRODUÇÃO
Ambientes mul tiprocessado s baseados em UNIX são, do ponto de vista
didático, difí ceis de ent ender em função de sua complexidade e da escassa
lit eratura
em
relação
ao
seu
f uncionamento
interno.
O
Ambiente
impl ementado f ornece fer rament as de estudo bastante práticas, facilitando o
aprendizado.
O AMRU (Ambi ente de Multipro cessamento para uma Rede de máquinas
Uni x)
ut iliza, basicam ente, chamadas de RPC ( Remo te Procedure Call)
disp onívei s n o UNIX e rotinas básicas do C. Fornece todas as rotinas
necessári as à criação de um a máqui na paral ela virt ual, como, por exemplo,
serv iços de “sen d” e “receive” (envio e recep ção de mensagens ent re
processos) e manipulação de pro cessos.
Um ambi ente paralelo virtual é aquele que perm ite progr amar como se
est ivesse uti lizando uma máquina paral ela, quando na verdade estamos
sim ulando−a. O Ambiente de Multiprocessament o para uma Rede d e máqu inas
Uni x (AMRU) , faz esta simul ação em cima de uma rede ro dando UNIX ou
compatível .
Este sistem a fornece r otinas de criação e comunicação de processos,
possibilit ando rodar vár ios processos ao m esmo tempo, dividido s entre as
máquinas da r ede, e controlá−los com o se esti vessem r odando em um
processador local .
Par a isso ser possível, faz−se uso de chamadas RPC ( Remote Procedure
Call ) [SUN 90], pacote de comunicação exi stent e no UNIX, à um processo
9
ant eriorm ente inicializado nas máqui nas da rede (chamado de daemon), e,
através dele, executa−se os serviços necessários.
Estes servi ços podem ser: in serir uma nova máq uina no sistema,
tor nando−a um “pr ocessador virtual”; criar um novo processo em uma
máquina especif icada; enviar um m ensag em para um processo do sistem a ou
receber uma mensag em de um pr ocesso do si stema.
Par a tanto, o capítulo 2 fornece um a visão geral do ambi ente. Em
seguida o capítulo 3 descreve com det alhes a implementação de cada função
do ambi ente. Duas aplicações são discut idas no capít ulo 4. Por fim, o capít ulo
5 apresenta as conclusões.
2PROGRAMAÇÃO PARALELA
Máq uinas paralel as são aquelas que possuem dois ou mais processador es
e são capazes de execut ar pelo menos doi s p rocessos simult aneamen te. Ou
sej a, em determinado momento, duas instruções estão sendo execut adas ao
mesmo tem po. Essas máquinas são caras, e portanto, difícei s de serem
encontradas.
Em
função
di sto,
alternativas
mais
econô micas
for am
desenvolvidas.
Uma destas alter nativas é a util ização de uma r ede de comput adores
com o se fosse uma única máquina com vári os pro cessad ores (figu ra 2. 1). Um
sistema rodando nesta rede seria capaz de mascara−l a, possibilit ando aos
processos “enxergá−la” como um a máqu ina paralela.
Figura 2.1− Ilustração de uma rede
Neste caso, cada máquina é vista como se fosse um p rocessador de uma
máq uina paralela virt ual, e pode−se, portan to, util izá−la como t al. Este é o
pri ncipal objetivo deste trabal ho d e conclusão, desenvolver um ambi ente que
11
exp lore os recursos de uma r ede, no qual pode−se inici ar a execução d e
múltipl os pro cessos e atr avés do qual estes processos possam se com unicar .
Um exemplo das possibilidades do sist ema proposto neste trabalho é
mostrado na figura 2.2. Através de uma série de cham adas à uma bibliot eca de
fun ções,
o
usuário
pode:
agregar
uma
máqui na
da
rede
ao
seu
mult iprocessador virtual ( am_addhost()), iniciar um processo em al guma
máq uina da red e ( am_ startproc()) e enviar (am_send() ) ou receber (
am_receive( )) mensagens.
12
#include "am_lib.c"
main()
{
int aux;
long int tid;
char msg[100];
int m1,m2, p1,p2;
m1=am_init(); /* inicializa o sistema parelelo virtual */
if (m1==−1)
exit(0);
m1=am_addhost("verdi"); /* insere a maquina verdi no sistema */
if (m1==−1)
exit(0);
m2=am_addhost("vivaldi"); /* insere a maquina vivaldi no sistema */
if (m2==−1)
exit(0);
p1=am_startproc(m1,"exemplo2"); /* inicia o processo exemplo2 na
maquina verdi */
if (p1==−1)
exit(0);
p2=am_startproc(m2,"exemplo2"); /* inicia o processo exemplo2 na
maquina vivaldi */
if (p1==−1)
exit(0);
am_send(p1,"Querida Verdi...");
/*envia msg para processo na
am_send(p2,"Querida Vivaldi...");
/*envia msg para processo na
tid=am_receive(−1,msg);
/*recebe uma msg de qualquer
tid=am_receive(−1,msg);
/*recebe uma msg de qualquer
}
maq 1*/
maq 2*/
processo*/
processo*/
Figura 2.2− Programa exemplo1.c
Neste exemplo são criados dois processos “exem plo2”, um executará na
máquina verdi e o outr o na máqu ina vivaldi. En via−se, em seguida,
uma
mensagem para cada um e espera−se pel as respostas.
#include "am_lib.c"
main()
{
long int tid;
char msg[100],nome[30];
gethostname(nome,64); /*obtem o nome desta máquina*/
tid=am_receive(−1,msg); /*recebe mensagem de qualquer processo */
fprintf(stderr,"Recebi mensagem ’%s’ do processo %d\n",msg,tid);
/*imprime a mensagem na tela*/
sprintf(msg,"Eu sou a maquina %s",nome);
am_send(tid,msg); /*envia mensagem*/
}
Figura 2.3− Programa exemplo2.c
Na fi gura 2. 3 t emos o có digo do program a exemplo2. Sua função é
receber mensagem d e qual quer processo e enviar uma resposta.
3IMPLEMENTAÇÃO
O Ambien te Mul tipro cessad o para
uma
Rede
de
máquin as Unix
(AMRU), foi total mente desenvolvido em C sobre o sistem a op eracion al
LINUX (com patível com UNIX) versão 2.2. Foram utilizadas, para tant o, as
máquinas verd i e vi valdi do Laboratóri o de In formát ica da ULBRA.
Foi implem entad a uma biblioteca contendo as f unções necessárias à
util ização do “AMRU” (am_l ib.c) e um programa daemon (amrud.c). Um
pro grama daemon é um processo residente qu e permanece em execução
continua.
3.1Biblioteca am_lib.c
Esta biblioteca contém as fun ções necessárias à manipulação d o
Amb iente Multiprogramado. Deve ser i ncluída na imp lementação qu e use as
caract erísti cas deste si stema.
Estruturas e Variáveis Globais
A bibl ioteca cri a duas estrut uras par a manipular pr ocessos e máquinas
instaladas.
A estr utura AM_L ISTHOS T manipula a lista de máquinas que est ão
disponí veis no sist ema. S ua def inição pode ser vi sta na figur a 2.1.
14
struct AM_LISTHOST{
char nome[20];
int numero;
char status;
};
/* Lista das hosts participantes do sistema
nome= nome da host (ex. verdi)
numero= numero pelo qual sera reconhecida no sistema
status= 0 se campo disponivel, 1 se ocupado por host ativa
*/
Figura 2.1− Estrutura AM_LISTHOST
O registro nome cor responde ao nome da máquina, através da qual ela é
identificada e referenciada (por exemplo: verdi, vivaldi, mozart, etc). O
regi stro numero é o número pel o qual ela é conhecida no si stema. Este
número é incrementado a cada cham ada de am_ addhost(), sendo armazenad o
na variável gl obal am_nummaq. Desta f orma a prim eira máquina a ser
inserida receberá o número zero , a segunda um, e assim sucessivam ente. Por
últi mo, o regi stro stat us fornece informações sobre o reg istro dentro da
est rutura. Se stat us está em zero, si gnifica q ue aquel a posição está di sponível
para ser u sada por uma nova m áquina, se for um, indica a presença de uma
máq uina ativa. Esta estru tura é usada pela v ariável global am_hostli st, que é
um vetor de AM_MAXMA Q posições (d efinido como const ante) .
A estr utura AM_L ISTPROC manipula informações sobre os processos
instalados pelo sistema. Sua definição pode ser vi sta na figur a 2.2.
struct AM_LISTPROC{
long int nproc;
int nmaq;
char status;
char nome[20];
};
/* Lista das processos inicializados por startproc
nproc= numero do processo dado pelo sistema (uma composicao
do numero da host e o id do processo
nmaq= numero da host onde encontra−se este processo
nome= nome do processo.
*/
Figura 3.2− Estrutura AM_LISTPROC
O registro npro c g uarda o número do pro cesso criado. Este número é
gerado pelo próprio sist ema e difere do núm ero de processo (id) usado
15
internamente pelo sistema operacional. O r egistr o nma q co ntém a inf ormação
do n úmero da máquina à qual pertence o r eferi do processo, número este que
coi ncide com o regi stro numero em am_ hostli st. Stat us po de ser zer o ou um.
Zer o indi ca que este registro não está ocupado por nenhu m pr ocesso e um que
exi ste um processo ativo e pronto para ser mani pulado . E, por fim, o campo
nome contém uma str ing designando o nome do pro cesso. A variável global
am_listproc ut iliza esta estrut ura, p ara declarar um v etor de AM_M AXPROC
posiçõ es (definid a como const ante) .
A vari ável am_ servidor indi ca se este processo é o u não o processo
serv idor, setada para zer o caso afirmativo e para um caso contrár io. Torna−se
servid or o p rocesso que executar a função am_init( ) .
Função am_init()
Util izada para inici alizar o p rocesso corren te como ser vidor, in iciali za
as li stas de máqui nas e de processos, seta a variável indicadora de servi dor
para zero e já insere a própria m áquina no sistema se registran do com o
apl icação servido ra junto ao amrud . Se a apl icação n ão executar esta função,
ela será considerada como uma aplicação cliente, não controlará os pr ocesso s
(man ipulação das listas) e não poder á inserir máquinas no sist ema e nem
inicializar p rocessos, pois são caracter ísticas ex clusiv as do processo servido r.
O código dest a função pode ser visto n a figura 3.3.
16
int am_init()
{ char nome[50],envia[100],recebe[25];
int conta;
char *env,*precebe;
int stat;
for (conta=0;conta<AM_MAXMAQ;conta++)
am_listmaq[conta].status=0;
for (conta=0;conta<AM_MAXPROC;conta++)
am_listproc[conta].status=0;
gethostname(nome,64);
am_servidor=0;
conta=am_addhost(nome);
sprintf(envia,"8#%d#",getpid());
env=envia;
precebe=recebe;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
am_listproc[am_numproc].status=1;
am_listproc[am_numproc].nmaq=am_myhost();
am_listproc[am_numproc].nproc=am_mytid();
return(conta);
}
Figura 3.3− Função am_init()
Função am_hostname()
Obt ém o nome da m áquina cuj o número é maq. Recebe u m inteiro com o
número da máquin a, e uma stri ng para armazenar o n ome obtido. O códig o
desta função pode ser vi sto na figur a 3.4 .
É necessário, às vezes conhecer o no me de determi nada máqui na ( para
usar este no me em uma chamada RPC, por exempl o). A list a de máquinas é
controlada e conhecida apenas pelo servidor . Deve−se, portanto, r equisi tar ao
serv idor que co nsult e sua base de dados e retor ne o número desta máquina.
Ent retanto, para se fazer uma chamada de RP C, preci sa−se informar o no me
da máqu ina, e uma vez que a aplicação cliente não tem est a informação
(apenas seu daemon possu i), deve−se deixar que o daemon local execute est a
tarefa que é reali zada da seg uinte forma:
− obtém −se atr avés da função gethostname o nom e da m áquina local ;
17
− prepara−se a string de envio composta de duas partes: o núm ero 6
indi cando que o amrud (daemon) deve execu tar o ser viço 6, seguido do dado
referente ao númer o da máquina para qual desej a−se o bter o nome;
− faz−se uma chamada RPC ao amrud local , passando a string e
recebendo outra string;
− o amr ud retornará um entre três padrões de resposta. S e a string
retornada possuir o caracter ‘1’ em sua prim eira posição, i ndica que o amru d
retornou o nome da máquina requi sitada, nome este que segue após o caracter
‘#’ na stri ng. Se, porém, a stri ng retornada começar com ‘0’, o am rud local
não teve co ndições de lo calizar a máqu ina porque el a não está inseri da n o
sistema, ou seja, não existe. O caracter ‘2’ na prim eira posição ind ica que o
amru d local não teve co ndições de l ocalizar a máquina porque ele não é o
serv idor e não conhece a lista d e máquinas. Nest e caso, segue−se após o
car acter ‘# ’ o nome da máqui na que possui o amr ud servi dor, devendo a
fun ção f azer uma n ova chamada de RPC, desta vez, porém, destinada ao
amru d ser vidor. Nest a chamada, se a st ring retornada não inici ar com ‘1’,
certam ente esta m áquina não existe.
− retor na−se 1, caso a máquina tenha sid o encontrada, ou −1 caso
contrário .
É int eressante sal ientar o fato de t odas as chamadas de RPC serem
através da troca de string s. Optou−se por stri ngs pelo fat o d as chamadas
envolverem inteir os e str ings sim ultaneamente, e intei ros podem facilm ente
ser co nverti dos para str ings e vice−versa.
18
int am_hostname(int maq,char *nome)
/*
Esta funcao retorna o nome da maquina cujo numero e maq
*/
{
char nome2[20],*env,envia[20],recebe[20],*precebe,stat;
/* nome=usada para guardar o nome da host, conta=contador for*/
gethostname(nome,64);
/* obtem o nome da host
no laco a seguir, procurara pela ocorrencia do nome da host na
lista de hosts do sistema retornando o numero desta*/
precebe=recebe;
sprintf(envia,"6#%d#",maq);
env=envia;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if (recebe[0]==’0’)
return(−1);
if (recebe[0]==’1’)
{sprintf(nome,"%s",recebe+2);
return(1);
}
if (recebe[0]==’2’)
{sprintf(envia,"6#%d#",maq);
env=envia;
stat = callrpc ( recebe+2, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if (recebe[0]==’1’)
{sprintf(nome,"%s",recebe+2);
return(1);
}
}
return(−1);
}
Figura 3.4− Função am_hostname()
Função am_myhost()
Utilizada para obt er o número da máquina local . Reto rna o número dest a
ou −1 se ocorreu erro (máquina não está no sistema). Para obter o número da
máquina local, deve−se r equisitar o ser viço 5 ao daem on de sua própria
máquina ( env recebe ‘5’ na sua declaração), pois ela “sabe” o seu número, qu e
lhe é passad o quando o am_addhost() é executado . A função am_strint () é
executada para ret irar um i nteir o da stri ng. Na f igura 3 .5 temos o código da
função am_myhost( ).
19
int am_myhost()
/*
Esta funcao retorna o numero da maquina que o requisitou ou −1 caso
haja algum erro (esta maquina nao tem processo daemon, por exemplo
*/
{
char nome[20],*env="5",recebe[50],*precebe,stat;
/* nome=usada para guardar o nome da host, conta=contador for*/
int maq=0;
gethostname(nome,64);
/* obtem o nome da host
no laco a seguir, procurara pela ocorrencia do nome da host na
lista de hosts do sistema retornando o numero desta*/
precebe=recebe;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if (recebe[0]==’1’)
{am_strint(recebe);
maq=am_strint(recebe);
return(maq);
}
return(−1);
}
Figura 3.5− Função am_myhost()
Função am_mytid()
Esta função retorna o n úmero pelo qual este processo é co nheci do no
sistema (am _pid) podendo ser vista na fi gura 3.6 . Uma vez q ue este número é
uma composição d o númer o da máquina com o i dentif icador do pro cesso,
basta obter estas duas i nformações para gerar o am_pid do si stema.
O número da máquina pode ser obtid o pela fu nção am_ myhost () e o
identificador d o processo pela função do UNIX get pid() . Um deslocam ento à
esq uerda de 12 po sições n o número da máquina, adicionado através de “OU”
lógi co ao i denti ficador retor nado pela get pid() com põe o número do processo
no sist ema (am_pid) .
20
long int am_mytid()
/* Obtem o numero pelo qual o processo e conhecido no sistema
Este numero e uma comnposicao binaria descrita em am_startproc
*/
{long int maq;
pid_t pid;
maq=am_myhost();
/*otem o numero da host */
maq=maq<<12;
/*compoe o numero da maquina */
pid=getpid();
/*obtem o numero do processo (no SO)*/
maq=maq|pid;
return(maq);
}
Figura 3.6− Função am_mytid()
Função am_converte()
Esta fun ção t em como objetiv o receber o número de uma máquin a e o
número de um pr ocesso (seu identifi cador) e retor nar o número am_pid deste
processo. Pode ser vista na figura 3.7.
Par a ger ar o am_ pid ( número pel o qual este processo será conheci do no
sistema) deve−se fazer um deslocamento à esquerda d e 12 posições do número
da máq uina. Par a um númer o de m áquina i gual a dois, um deslocamento a
esq uerda de 12 posições resultará em 8192. Este número deverá ser
adi cionado ( através d a operação ou lógico ) ao i dentif icador de pr ocesso n o
sistema para formar o número am_pi d do processo, que é retor nado pela
função.
long int converte (int maq,long int tid)
{long int aux;
aux=maq<<12;
return(aux|tid);
}
Figura 3.7− Função am_converte()
21
Função am_strint()
Esta função r etira uma parte num érica da string data , conver te−a para
inteiro e devolve à v ariável data o que sobrou . O número i nteiro gerado é
retorn ado à função que o cham ou. Na figur a 3.8 temos seu código .
long int am_strint (char *am_data)
{char *am_aux;
long int am_total=0;
int am_sinal=1;
for (am_aux=am_data;(*am_aux<’0’)||(*am_aux>’9’);am_aux++)
{if (am_aux==NULL) return(−1);
if (*am_aux==’−’) am_sinal=−1;
}
for (;(*am_aux>=’0’)&&(*am_aux<=’9’);am_aux++)
am_total=(am_total*10) + (*am_aux−’0’);
sprintf(am_data,"%s",am_aux);
return(am_total*am_sinal);
}
Figura 3.8− Função am_strint()
Função am_addhost()
Tem o objeti vo de inserir uma nov a máqui na no si stema, r etornando o
seu número (apenas a apl icação servido ra pode executar est a tarefa). Esta
inserção é f eita enviando−se uma mensagem de teste e algumas infor mações
ao amrud já instalado na máquina (a instal ação dest e amr ud deve ser feita
explicit amente) .
Ini cialmen te é feita uma verificação para determ inar se est a é a
apl icação servidor a ou não. Isto é feito at ravés da verificação da var iável
am_ servidor, que deve ser zero (esta variável é reset ada quando am_init() fo r
executado ). Se não f or zer o, si gnifica que esta aplicação n ão é a ser vidora,
por tanto não pod e i nserir máquinas no sistema e a f unção am_ addhost()
retornará −1 sinalizando u m erro.
Caso sej a o servidor, f az−se uma verifi cação na l ista de máquinas para
ver se já não existe esta máquina no sistema, se existi r apenas retorna seu
22
número. Caso não exi sta, prepara−se a mensagem que será enviada ao amru d.
Esta mensag em é composta do número 1, indicando tratar−se do servi ço 1,
seg uido do número que esta máqui na receberá e o nome da máquina servidora.
Desta forma o amrud destino saberá a q uem repo rtar as chamadas de RPC.
Env ia−se então esta stri ng ao amrud devendo este ret ornar 1, caso não haja
err o. Procede−se en tão ao cadastramento dest a nova máqui na na list a de
máquinas do sistema. Após alt erar esta list a, deve−se co municar ao amrud
serv idor est a nova adesão, para q ue ele possa responder à pedidos como
am_ hostname. Isto é realizado sinal izando o serviço n úmero 7, enviando−se o
número e o nome da nova m áquina ao amrud servi dor q ue n o caso encontra−se
nesta máqui na, o que indica q ue a cham ada de RP C deve ser feita à m áquina
reto rnada pela função gethost name(). Estas informaçõ es são em pacotadas em
uma string.
Apó s isto, f inalm ente ret orna−se o número da máq uina in serida, ao
mesmo t empo em que increm enta−se o contador de máquinas.
O códi go com pleto desta função pode ser vi sto na figur a 3.9.
23
int am_addhost(char *maquina)
{
/* Este programa faz uma chamada RPC para o programa AMRUD
em execucao na maquina remota. Se este programa estiver
ativado ele respondera conforme solicitado. Retorna o numero
da host ou −1 se houve erro
*/
int stat,conta;
char recebe[2],buf[30],nome[20];
char *envia,*precebe;
if (am_servidor)
return(−1);
/* Verifica se a maquina ja nao esta inserida no sistema */
for(conta=0;conta<am_nummaq;conta++)
if (strcmp(maquina,am_listmaq[conta].nome)==0)
{am_listmaq[conta].status=1;
return(am_listmaq[conta].numero);
}
precebe=recebe;
envia=buf;
gethostname(nome,64);
sprintf(buf,"1#%d#%s",am_nummaq,nome);
/* envia o nome e o numero de sua host para a host inserida saber
quem a criou */
stat = callrpc ( maquina, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &envia,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina
%s.\n",maquina);
return(−1);
}
else
if (strcmp("1",recebe)==0)
/*procura por uma posicao disponivel na lista de hosts, a posicao
e
considerada disponivel se status for 0
*/
for (conta=0;conta<AM_MAXMAQ;conta++)
if (!am_listmaq[conta].status)
{strcpy(am_listmaq[am_nummaq].nome,maquina);
/* cadastra a nova host inserida na lista de hosts */
am_listmaq[conta].numero=am_nummaq;
am_listmaq[conta].status=1;
sprintf(buf,"7#%d#%s",am_nummaq,maquina);
gethostname(nome,64);
envia=buf;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &envia,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na
maquina %s.\n",nome);
return(−1);
}
return(am_nummaq++);
}
return(−1);
}
Figura 3.9− Função am_addhost()
24
Função am_startproc()
Ini cia um pr ocesso na máquina especifi cada e r etorna o am_pid dele
(figura 3 .10). Somente a aplicação servid ora po de executar esta função.
Por tanto, assi m com o na função am_ addhost(), deve−se pri meiramente testar
se trat a−se da aplicação servidor a através da verif icação da vari ável
am_servid or. Após isto , se trat ar−se da aplicação servi dora, ver ifica−se a
exi stênci a da máquina especi ficada consu ltando −se a l ista de máqu inas. No
caso de não existi r, retorna−se uma mensagem de erro (−1) .
Deve−se então empacot ar a string a ser env iada ao amr ud da máquina
destino. Esta string é composta de duas part es: o caracter ‘4’ ( indicando
trat ar−se d o servi ço 4) e o nome do pr ocesso, separados p elo caracter ‘# ’.
Executa−se então uma chamada RPC à máquina especif icada, enviando−se a
string empacotada.
Apó s a chamada RPC, verifi ca−se o val or reto rnado. Se este for “−1”,
sign ifica qu e não foi possível iniciar o pr ocesso (por motiv os que ser ão visto s
na ex plicação deste serviço) , caso con trário a st ring terá então o núm ero do
pro cesso criado , que no caso é o id entifi cador gerado pelo SO na máquina
local. Faz−se então a conversão para am_pi d através da função am_con verte( ).
Com
o
p rocesso
criado,
seu
am_ pid
ger ado,
faz−se
então
o
cadastram ento deste processo na l ista de processos existente, retornando−se o
número (am_pi d) dest e à função qu e o executou.
25
long int am_startproc(int maq,char *nomep)
{/* Inicia o processo de nome nomep na maquina maq, retorna o numero
do processo criado ou −1 se houve erro. O numero do processo e uma
composicao feita a partir do numero da host com o ip do processo da
seguinte forma: faz−se um shift left do numero da host em 12
posicoes
como exemplo, se for maquina 1, expresso como 0000 0000 0000 0001b,
ao fazer esse shift, fica 0001 0000 0000 0000b, ou seja, 4096 em
decimal, soma−se entao a este numero o id do processo, por exemplo,
processo 240, expresso como 0000 0000 1111 0000b, resoltando em 4336
(0001 0000 1111 0000b). Assim sendo, como o long int e expresso em 4
bytes, usamos o primeiro byte para numero da host (total de 256) e
os
outros tres para processos (4096). Verificou−se que os ips criados
pelo Linux nao ultrapassam este limite.
*/
int stat,numero=−1,conta;
char envia[20],recebe[5],*precebe,*env;
long int proc;
if (am_servidor)
return(−1);
/* procura pela host na list de hosts */
for(conta=0;conta<AM_MAXMAQ;conta++)
if ((maq==am_listmaq[conta].numero)&&(am_listmaq[conta].status))
numero=conta;
if (numero==−1)
{fprintf(stderr,"numero de maquina nao existe\n");
return(−1);
}
sprintf(envia,"4#%s",nomep);
/* Gera string a ser enviada ao Deamon na host, 4 indica servico
de
am_startproc seguido do nome do processo que deve sr criado */
precebe=recebe;
env=envia;
stat = callrpc(am_listmaq[numero].nome,RUSERSPROG,RUSERSVERS,
RUSERSPROC_NUM,xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina %s.\n",
am_listmaq[numero].nome);
return(−1);}
if (strcmp(recebe,"−1")==0)
{printf("Nao foi possivel iniciar processo %s\n",nomep);
return(−1);}
proc=am_strint(recebe);
proc=am_converte(maq,proc);
/*em recebe existe o numero id (do SO) do processo criado, obtem−se
agora o numero do processo no sistema */
for (conta=0;conta<AM_MAXPROC;conta++)
/* Cadastra o processo criado na lista de processos */
if (!am_listproc[conta].status)
{am_listproc[conta].status=1;
strcpy(am_listproc[conta].nome,nomep);
am_listproc[conta].nproc=proc;
am_listproc[conta].nmaq=maq;
return(proc);
}
return(−1);
}
Figura 3.10− Função am_startproc()
26
Função am_send()
Envia um a mensagem msg ao pr ocesso proc, ambos passados como
parâmetros. Seu có digo pode ser visto na figura 3.11 .
Com o este pro cesso pode estar em q ualquer máqui na participant e do
sistema, não é possí vel usar as rot inas padr ões de troca de mensagens ent re
pro cessos já existentes no UNIX. Portanto, cada amru d é responsável pela
buf erização das mensagens de seus filhos, dev endo as mensagens ser
endereçadas ao amrud da máquina à qual o pr ocesso dest ino é filho.
Com eça−se descobrindo qual é a máquina à qual p ertence este processo.
Um deslocament o à d ireita de 12 posições na variável proc fornece o número
da máqu ina e um “E” lógico com 4 095 o identif icador do pr ocesso.
Prepara−se então a string que deverá ser env iada ao amrud. Ela é
com posta d o caracter ‘2 ’, indicando o serviço de send, mai s o am_p id do
pro cesso
que
envia
a
mensagem,
obtido
pel a
função
am_mytid()
o
identificador d o pro cesso destino, e pela mensagem p ropriamente dita. Todos
est es p arâmetros são empacotados na string envia, e separ ados um do outr o
pelo caracter ’#’.
Procede−se então à cham ada de RPC à m áquina onde se encontra o
pro cesso
d estino,
cujo
nom e
foi
ant eriorm ente
ob tido
pel a
função
am_hostname( ) e ver ifica−se o valor r etornado. S e este for “−1”, signifi ca que
não existe o processo destino, caso contrário a mensagem foi enviada com
sucesso e a função retor nará 1.
27
int am_send(long int proc,char *msg)
{
/* Envia uma mensagem para o processo especificado */
int stat,conta,numero=−1;
char envia[100],nome[30];
char recebe[20],*precebe,*env;
numero=proc>>12;
stat=am_hostname(numero,nome);
if (stat==−1)
return(−1);
proc=proc&4095;
/* Obtem o real numero do processo, seu id, o numero pelo qual
ele e conhecido pelo SO de sua maquina */
sprintf(envia,"2#%d#%d#%s",am_mytid(),proc,msg);
/* Gera a string a ser enviada ao Daemon da maquina que possue
este processo. O numero 2 indica que trata−se do servico 2.
Segue−se o numero do processo que executou o servico e o
numero do processo para quem deseja−se enviar a mensagem.
*/
precebe=recebe;
env=envia;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina %s.\n",
nome);
return(−1);}
if (strcmp(recebe,"−1")==0)
{printf("Nao foi possivel achar processo %s\n",proc);
return(−1);
}
return(1);
}
Figura 3.11− Função am_send()
Função am_receive()
Recebe uma m ensagem do processo esp ecificado (proc), retornando seu
número. S e o parâmet ro passado em proc for −1, am_receive receberá
mensagem de qualquer processo. Veja có digo na figur a 3.12 .
Em pri meiro lu gar gera−se a stri ng que será enviada ao amrud local
(isto porque cada am rud gerencia as mensagens dos pr ocessos da sua
máquina). E sta strin g começa com o identi ficad or do servi ço, que no caso é
‘3’ ( receive), segui da d o número ident ificad or d o pro cesso que desej a receber
a mensagem e do am _pid do processo de q uem se deseja receber a mensagem.
28
Env ia−se esta st ring ao amrud local continuamente at é q ue exista uma
mensagem para este processo (espera ocupada) .
Depo is que amrud retorn ar a st ring, procede−se à conversão dos
parâmetros, pois esta st ring contém o nú mero do processo que enviou a
mensagem seg uido d a mensagem enviada.
29
int am_receive(int proc,char *msg)
{
/* Recebe uma mensagem do processo especificado, Se proc for −1,
indica que a mensagem pode ser de qualquer processo
*/
int stat,conta,numero=−1;
char envia[20],recebe[100],nome[20],*precebe,*env;
sprintf(envia,"3#%d#%d#",getpid(),proc);
/* Note que aqui, ao inves de enviar o numero do processo
no sistema, envia−se o seu id apenas. Isso porque quem
fara o tratamento de mensagens e o Daemon desta maquina
(esta propria que executou o am_receive), e este daemon
conhece seus "filhos" pelo seu id.
*/
precebe=recebe;
env=envia;
gethostname(nome,64);
/* O buffer de msgs e controlado pelo Daemon de cada host, cada
Daemon controla as msgs de seus filhos. Por isso esta chamada
de RPC e para a propria host que executou esta funcao.
*/
for (;recebe[0]!=’1’;)
{
stat = callrpc (nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao nesta maquina
\n");
return(−1);
}
}
am_strint(recebe);
numero=am_strint(recebe);
sprintf(msg,"%s",recebe+1);
return(numero);
}
Figura 3.12− Função am_receive()
3.2Programa Daemon (amrud.c)
Este pr ograma deve ser ant eriorm ente inst alado em cada máquina que
part icipará do si stema. Atribui−se o nome d e amr ud a todo o daemon do
sistema rodan do nas m áquinas i nseridas por am_ addhost() e chama−se de
amru d ser vidor o daem on da máqui na ser vidora, ou seja, aquela responsável
pela in stalação do sistem a e qu e executou a função am_init( ).
Cada amrud é r esponsável em atender as chamad as RP C destinadas à sua
máquina, bem como m anter e gerenciar as mensagens destinadas à seus filhos.
30
Estruturas e variáveis globais
Amr ud cria as m esmas duas estr uturas par a manipular processos e
máquinas inst aladas exi stente na bib lioteca AM_ LIB, por ém co m algumas
inform ações diferentes. Sua definição encontra−se na figura 3.13.
Na
est rutura
AM_LISTHOST
não
exi ste
o
campo
st atus
e
na
AM_LISTPROC não existe o campo nom e e nmaq, e existem outros três
campos: msg, orig em e espera.
O campo msg é um vetor de caracteres bi−direcional de AM_MSGMAX
posi ções, cada uma com AM_TAMMSG caracteres. Este campo serve como
buf fer
de
mensagens
recebidas,
podendo,
portanto,
arm azenar
até
AM_MSGMAX mensagens para cada processo, cada mensagem poden do
conter AM_T AMMSG caracteres.
O camp o or igem é um vetor de int eiros de AM_MSGMAX posições.
Nel e é ar mazenado o númer o am_pid de cada processo que env iou um a
mensagem. É mant ida um a relação ent re a p osição do vetor or igem, com a
posi ção do vetor msg, ou seja, o processo de am_pi d origem[0] gravou sua
mensagem no campo msg[0] .
O campo esp era armazena o am_ pid do processo de q uem se esper a uma
men sagem, e também serve para testar a disponibilidade do registro, isto é, se
esp era tiver −3, indica que todo aquel e regist ro não pertence ainda à nen hum
pro cesso, podendo ser usado quand o um serviço de startproc for r equisi tado;
se po ssuir −2 , este registro j á pertence à um pro cesso e este no mom ento não
aguarda mensagens. Se, no entanto possuir −1, o processo espera qualquer
men sagem. Outro v alor i ndica o am_pi d do pr ocesso de quem se ag uarda
mensagem .
31
O campo status tem uma função d iferent e que na AM_LI B. Aq ui el e
serv e apenas para contar o núm ero de men sagen s existen tes no mo mento para
aquel e pro cesso.
32
struct AM_LISTHOST{
char nome[20];
int numero;
}am_listmaq[AM_MAXPROC];
/* Lista das hosts participantes do sistema
nome= nome da host (ex. verdi), −1 registro disponivel
numero= numero pelo qual sera reconhecida no sistema
*/
struct AM_LISTPROC {
long int nproc;
char status;
char msg[AM_MSGMAX][AM_TAMMSG];
int origem[AM_MSGMAX];
int espera;
}am_listproc[AM_MAXPROC];
/* Lista de processos. Difere−se um pouco da lista existente na
biblioteca, pelo fato de aqui nao existir o nome do processo,
nproc e o id dele do SO e ter campos para buferizar as msg.
nproc=numero do processo
msg= buffers de mensagem pendentes, cada processo pode ter ate
AM_MSGMAX mensagens esperando, cada uma de ate AM_TAMMSG
caracteres.
status= 0= sem msg para ele, >0=numero de msg recebidas
espera= −3 campo disponivel, −2 ocupado−processo nao espera
msg, −1 ocupado, processo espera msg de qualquer pro−
cesso, >=0 = numero do processo de quem esta esperando
uma mensagem.
origem=processos que enviaram mensagem, −1=disponivel.
*/
char am_minhahost=0;
/* se =0, este e o servidor, >0 designa o numero desta host */
char am_servername[30];
/* nome da host servidora */
Figura 3.13− Estruturas e Variáveis do amrud
Existem apenas quatro variáveis gl obais: am_minhahost, que armazena o
número da sua pr ópria m áquina; a st ring am_servername, que guar da o no me
da máquina q ue possui o amr ud servidor; am_ listmaq, uma estrutura que
armazena informações sobre as máquinas inseridas e a variável am_listproc
que ar mazena infor mações dos processos inseridos no si stema.
Função am_msgrec()
A fun ção am_ msgrec( ) é responsável pelo trat amento interno de
men sagens. Verif ica inicialmente se existe no buffer alguma mensagem do
pid_ msg para o pid_ req. Se existi r, empacota−a na variável msg passada por
parâmetro e retorna 1. Senão, retor na −1.
33
Prim eiram ente percorre−se a l ista de processos à procura do processo
número pid_req. Tão logo a mesma seja l ocalizada, faz−se um t este co m a
vari ável stat us, po is se esta for zero, não existem m ensag ens par a este
processo e já se pod e retor nar −1.
De posse da po sição no regist ro pert encent e ao processo que r equisitou
ao pedido, procura−se pela mensagem vin da do processo pid_msg no buf fer.
Se pid_msg f or −1 , então pega−se a prim eira mensagem buferizada. Após
gerar a string de r etorno (já empacotada com o identificador d e sucesso, 1, o
identificador do processo que en viou a m ensagem e a mensagem enviada)
dev e−se ajustar os dad os no buffer, ou seja, decrementar a var iável status
(contadora d e mensagens) e lim par o campo de ond e ela foi extraída.
Observe que o n úmero do processo que enviou a mensagem é retornad o
ao processo que a requisit ou, isto porque caso pid_msg tenha sido −1, o
processo pid_req t erá meios de saber de q uem realment e veio a men sagem .
34
int am_msgrec(int am_pidreq,int am_pidmsg,char *am_msg)
{/*Servico de recebimento de mensagens, se existir mensagem de am_pidmsg
para am_pidreq, esta sera armazenada em am_msg e a funcao retorna 1,
senao a funcao retorna −1 (o servico de send e receive e sincrono, mas
a decisao de por o processo para dormir e do am_receive e nao do
Daemon
este ultimo apenas informa ao am_receive pelo retorno do RPC que nao
existe a am_msg requisitada). Se am_pidmsg for −1, indica receber msg
de qualquer processo.
*/
int conta,conta2;
/* procura a posicao do buffer pertencente ao processo que requisitou
o pedido de receive */
for (conta=0;conta<AM_MAXPROC;conta++)
if (am_listproc[conta].nproc==am_pidreq)
{if (am_listproc[conta].status==0)
/* se status e zero, nao ha msg para este processo */
return(−1);
if (am_pidmsg==−1)
/* −1 indica receber de qualquer processo */
for (conta2=0;conta2<AM_MSGMAX;conta2++)
if (am_listproc[conta].origem[conta2]!=−1)
{sprintf(am_msg,"1#%d#%s",am_listproc[conta].
origem[conta2],am_listproc[conta].msg[conta2]);
/*monta a string de retorno, 1 indica sucesso,
segue o numero do processo qeu enviou a msg
(util caso am_pidmsg fosse −1) e em seguida a
mensagem
*/
am_listproc[conta].origem[conta2]=−1;
/*disponibiliza a posicao do buffer */
am_listproc[conta].status−−;
return(1);
}
for (conta2=0;conta2<AM_MSGMAX;conta2++)
if (am_listproc[conta].origem[conta2]==am_pidmsg)
{
sprintf(am_msg,"1#%d#%s",am_pidmsg,am_listproc[conta].
msg[conta2]);
am_listproc[conta].origem[conta2]=−1;
am_listproc[conta].status−−;
return(1);
}
}
return(−1);
}
Figura 3.14− Função am_msgrec()
Função am_msgsend()
Esta função armazena no buf fer u ma mensagem msg do processo origem
para o processo destino. P ara processo origem deve−se f ornecer o v alor de
seu am_pid e para o d estino apenas o valor de seu identi ficador. Isto porque a
manipul ação de mensagens é feit a por cada am rud, sendo q ue ele conhece seus
filhos pelo i dentif icador de processo gerado pelo sistema operacional .
35
Prim eiram ente faz−se uma busca na l ista de máquinas à procu ra do
regi stro pertencente ao processo destino. Após achar este registro, procur a−se
uma posição liv re no buffer ( campo origem e msg) . E cadastr a−se a
men sagem, bufer izando−a em msg, alterando o campo origem para que
conten ha o am_pid do processo origem e incrementando a vari ável status.
int am_msgsend(int origem,int destino,char *am_msg)
{ /* Coloca mensagem no buffer retorna 1 se sucesso, ou −1 se erro
(buffer cheio, por exemplo
*/
int conta,conta2;
for (conta=0;conta<AM_MAXPROC;conta++)
/* procura posicao de buffer do processo destino */
if (am_listproc[conta].nproc==destino)
/* procura no buffer espaco disponivel para armazenar a msg */
for (conta2=0;conta2<AM_MSGMAX;conta2++)
{
if (am_listproc[conta].origem[conta2]==−1)
{/* buferiza a mensagem */
am_listproc[conta].origem[conta2]=origem;
am_listproc[conta].status=am_listproc[conta].status++;
strcpy(am_listproc[conta].msg[conta2],am_msg);
return(1);
}
}
return(−1);
}
Figura 3.15− Função am_msgsend()
Função am_strint()
Função id êntica à am_ strint () da bibl ioteca am_li b. Ret ira u m número
inteir o de uma string.
Função **am_server()
A função am_ server( ) é a função mais impor tante do sistema. É ela que
aten de as chamadas de RPC e executa os serviços requisi tados r etornando um
apontador de caracteres. Oferece oit o serviços b ásicos que serão vistos
indi vidual mente. E les são sel ecionados através de uma sel eção (do t ipo
36
swit ch−case) na qual é testado o primei ro caracter do parâmetro passado n a
variável indat a.
char **am_server (char **indata)
{/* Programa responsavel pelo atendimento das chamadas de RPC,
atraves das strings enviadas pelas chamadas, determina o tipo de
servico requizitado e exetua−o
*/
static char data[AM_TAMMSG];
static char *pd,*rd;
int stat,pid,am_pidreq,am_pidmsg;
rd=*indata;
switch (**indata) {
case ...
}
}
Figura 3.16− Função **am_server()
Serviço am_addhost
Este ser viço só pode ser requi sitado pela apli cação servid ora, e ser ve
para testar se o am rud está devi damente in stalado nesta máqui na, e para
inf ormá−l o que está partici pando do sistem a, bem como passar−lhe o seu
número e o nom e da máquina ser vidora. Se o amru d est iver i nstalado, deve
apenas retorn ar a string “1” à aplicação ser vidora.
case ’1’:
/* Teste, usado pelo addhost */
am_strint(rd);
am_minhahost=(char)am_strint(rd);
sprintf(am_servername,"%s",rd+1);
data[0]=’1’;
data[1]=’\0’;
break;
Figura 3.17− Serviço am_addhost
Serviço am_send
Este serviço obtém, através de duas cham adas à função am_strint (), o
número
dos
processos
envolvi dos
no
serviço,
e
os
envia
à
função
am_msgsend(). Pr epara a string data com a inf ormação “1” para ret orná−la ao
processo ser vidor .
37
case ’2’:
/* Send */
data[0]=’1’;
data[1]=’\0’;
am_strint(rd);
am_pidreq=am_strint(rd);
am_pidmsg=am_strint(rd);
am_msgsend(am_pidreq,am_pidmsg,rd+1);
reak;
Figura 3.18− Serviço am_send
Serviço am_receive
Este serviço, da mesma form a que o send, apenas obtém o n úmero dos
pro cessos envolvi dos e os passa à f unção am_ msgrec( ). Se est a função achar a
mensagem solicitada, a string data já estará format ada para envi o ao pro cesso
serv idor, caso cont rário, será preci so format á−la sinalizando o erro, o que é
feito pela função sprint f .
case ’3’:
/* Receive */
am_strint(rd);
am_pidreq=am_strint(rd);
am_pidmsg=am_strint(rd);
stat=am_msgrec(am_pidreq,am_pidmsg,data);
if (stat==−1)
{data[0]=’0’;
data[1]=’\0’;
}
break;
Figura 3.19− Serviço am_receive
Serviço am_startproc
Este serviço é responsável pela i nicial ização de pr ocessos. P ara isto,
faz−se u so de d uas funções d o UNIX. A primeira é a função fork(), q ue cria
uma n ova cópia do pro cesso qu e a execu tou. Assim, ao executar−se fork(),
tem os duas cópias do processo am rud, uma consid erada pai e a outra fi lho. As
duas cópias segui rão sua execução nor malmen te a partir deste ponto, uma
independente da out ra (se o pai morrer, o f ilho t ambém morre) .
A função fork() retorna o núm ero do pr ocesso filho ao pai e zer o ao
fil ho. Com ist o é possível realizar um t este para d etermi nar q uem é o fi lho e
38
quem é o pai. Se pi d for 0, a negação em pid far á com que a sentença seja
verd adeira, portanto apenas o filho execut ará o comando após o if e apenas o
pai execu tará os com andos do else. O filho executará a cham ada d e sistema
execv( ) .
A função execv() executa o programa cujo nom e é passado por
parâmetro e subst itui a imagem do pr ocesso que o executou pela im agem do
pro cesso que deseja−se executar. Assim sendo, o amrud filho não conti nuará a
executar depois de chamar a fu nção execv() term inado qu ando o processo
criado ter minar. O segundo parâm etro de execv() é os parâmet ros passados à
fun ção que se deseja criar, como se t ivessem sido passados na li nha de
comando.
Apó s ser inicializado o pr ocesso desejado, o mesmo é cadastrado (pelo
amru d pai, j á q ue o fil ho anterio rmente cri ado não chegar á a executar esta
part e do códi go) na lista de processos do amr ud. Esta l ista, como já f oi dit o
ant eriorm ente, guarda apenas dados dos processos filhos de cada amrud. Em
seguida, uma string de retorno é formatada para sinal izar ao pr ocesso servidor
seu sucesso n a i niciali zação. Est a sinalização é o número iden tificador do
processo criado ou ‘−1’ se houve erro.
39
case ’4’:
/* Startproc */
pid=fork();
if (!pid)
{
execv(&rd[2],NULL);exit(0);
}
sprintf(data,"%d",pid);
for (stat=0;stat<AM_MAXPROC;stat++)
if (am_listproc[stat].espera==−3)
{
am_listproc[stat].espera=−2;
am_listproc[stat].nproc=pid;
for (pid=0;pid<AM_MSGMAX;pid++)
am_listproc[stat].origem[pid]=−1;
stat=AM_MAXPROC+1;
}
if (stat==AM_MAXPROC)
{sprintf(data,"%d",−1);
break;
}
break;
Figura 3.20− Serviço am_startproc
Serviço am_myhost
Este ser viço é bastant e simp les e út il, ele apenas reto rna o número da
máquina local .
case ’5’:
/* Myhost */
sprintf(data,"1#%d",am_minhahost);
break;
Figura 3.21− Serviço am_myhost
Serviço am_hostname
Este ser viço fornece, à quem o requi sitou, o nom e da máqui na cuj o
número é p assado por par âmetro . E ntretanto, apenas o am rud servid or m antém
o cadast ro das m áquinas participantes do sistem a, e se o amrud em q uestão
não for o servidor, el e não será capaz de determ inar o nome da máqu ina q ue
se pede.
Deve−se, por tando, fazer um teste para saber se este amrud é o ser vidor ,
ist o é feit o pela var iável am_minhahost que será zer o se este for o servidor.
Caso sej a, fornece−se o nome da máquina requisitada ou “0” se esta não
existir .
40
Por ém, se este não for o amrud servi dor, ele não ter á condições de
efetuar a tar efa p edida, mas também não pode sinalizar que não existe tal
máq uina. Neste caso, então, o amrud sinalizar á com o caracter ‘2’ seguido d o
nome da máquina que contém o am rud ser vidor , d evendo o processo que
requisitou este serviço voltar a fazê−lo, só que dest a vez ao amr ud ser vidor .
case ’6’: /* am_hostname */
am_strint(rd);
pid=am_strint(rd);
sprintf(data,"0");
if (am_minhahost==0)
{for (stat=0;stat<AM_MAXPROC;stat++)
if (am_listmaq[stat].numero==pid)
sprintf(data,"1#%s",am_listmaq[stat].nome);
}
else sprintf(data,"2#%s",am_servername);
break;
Figura 3.22− Serviço am_hostname
Serviço cadastra
Este ser viço é requisi tado apenas pelo pr ocesso servidor toda vez que
uma função am_addhost() é execut ada. T em como obj etivo infor mar ao amrud
serv idor que uma nova máqui na foi inserida, bem como o número e o no me
desta. Se este cadastramento não fosse feito, o amrud ser vidor não teria
condições de resp onder aos servi ços de am_hostname porque o registro das
máq uinas existentes est aria apenas no pr ocesso ser vidor, inatingí vel por
chamadas de RPC.
case ’7’: /* cadastra nova host */
am_strint(rd);
pid=am_strint(rd);
for (stat=0;stat<AM_MAXPROC;stat++)
if (am_listmaq[stat].numero==−1)
{
am_listmaq[stat].numero=pid;
sprintf(am_listmaq[stat].nome,"%s",rd+1);
stat=AM_MAXPROC;
sprintf(data,"%s","1");
}
break;
Figura 3.23− Serviço cadastra
41
Serviço registra
Este serviço é requisi tado apenas pel o processo servi dor uma única vez,
dur ante o am_ init() . Tem como o bjetivo in formar ao amrud servi dor o
identificador do processo servido r, já que o amrud não o conhece por não
trat ar−se de seu fi lho. Se este cadastr amento não fosse feito, o processo
serv idor não poderia receber mensagens, poi s o amrud servidor não teri a n em
ao menos espaço n o buf fer r eservado para el e e respo nderia com erro à
qualquer pedi do de envio de mensagem destinado ao processo servidor .
case ’8’:
/* registra o processo servidor*/
am_strint(rd);
pid=am_strint(rd);
am_listproc[0].espera=−2;
am_listproc[0].nproc=pid;
for (pid=0;pid<AM_MSGMAX;pid++)
am_listproc[0].origem[pid]=−1;
break;
Figura 3.24− Serviço registra
Apó s este servi ço, termi na o comand o switch−case. Por tanto , a st ring
gerada em algum ponto ant erior da execu ção cont ém os valores que devem ser
reto rnados ao pr ocesso que execut ou a chamada. Reto rna−se, por fim, esta
string.
Função main()
A fu nção pri ncipal do amru d faz a iniciali zação das variáveis usadas
pel o amr ud, o regist ro da função am_ server( ) no RPC e a instalação do RP C.
Seu có digo p ode ser visto na figura 3.25.
A função reg isterrpc() [ SCH 96] recebe seis parâmetro s, sendo que três
del es foram man tidos em default por não serem rel evantes neste trab alho
(RUSERSPROG,
RUSERSVERS ,
RUSERSP ROC_NUM).
O
quarto
é
o
42
endereço da f unção que at enderá as ch amadas de RP C, que no caso é a função
am_server( ).
O quint o e o sexto parâmet ros indicam os tipo s de dados que o processo
receber á e qu e envi ará, que no caso é wrapstring para os dois.
Em seguida é chamada a função svc_ run() do pacote RPC. Esta f unção
inst ala o processo am_ server( ) como responsável pelo atendimento das
chamad as e não ret orna mais. Se est a função retornar houve algum erro.
main()
{int aux=0;
for (aux=0;aux<AM_MAXPROC;aux++)
/* inicializa campos da lista de am_listproc */
{am_listproc[aux].espera=−3;
am_listproc[aux].status=0;
am_listmaq[aux].numero=−1;
}
registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, am_server,
xdr_wrapstring, xdr_wrapstring);
svc_run();
fprintf(stderr,"Erro: svc_run retornou!\n");
exit(1);
}
Figura 3.25− Função main()
4EXEMPLOS DE UTILIZAÇÃO
São v ários os exempl os que podem ser u tilizados neste sist ema. Muit os
dos i mplementados em PVM pod em, com facilidade, ser mi grados para est e
amb iente. Não todos, por que cer tamente o PVM é bem mai s completo e, em
alguns pontos, m ais efi ciente que este amb iente. Substi tuí−lo nunca foi a
pret ensão deste t rabalho. Neste capí tulo ser ão ap resent ados dois exemplos:
desempenh o e o clássico filósof os jantadores.
4.1Desempenho de troca de mensagens
Este exemplo consiste de dois processo s cu ja f inalid ade é t estar o temp o
necessári o ao en vio e recebimento de m ensagens no sistema. Chamaremos o
primei ro processo de “desempenho” e o segu ndo de “responde”.
A função do processo desem penho é criar no ambiente o processo
resp onde, envi ar um a m ensagem qualquer a este e receber uma outra de volta,
contabili zando o tempo levado. Isto será f eito n vezes, e, ao final das
iterações, ser á calculado o tem po médio, mínimo e m áximo do envio das
mensagens.
Já o processo “r esponde” terá a ú nica função de resp onder à todas as
men sagens recebidas do d esempenho. O código do responde po de ser vist o n a
figur a 4.1.
Not e que não existe com plexidade algum a neste processo, ele apenas
recebe um a mensagem qualquer e a envia de vol ta ao mesmo processo que a
enviou, faz isso até qu e receba a m ensagem ‘0’.
44
/* Este processo recebe uma mensagem e a envia de volta */
#include "am_lib.c"
main()
{
long int tid;
char msg[100];
tid=am_receive(−1,msg);
for(;msg[1]!=’0’;)
{
fprintf(stderr,"Recebeu msg %s do processo %d\n",msg,tid);
am_send(tid,msg);
tid=am_receive(−1,msg);
}
}
Figura 4.1− Exemplo responde
O processo desempenho é u m pouco mais compl exo e seu código pode
ser vi sto na figur a 4.2 .
Primeir amente inici aliza−se o ambient e através de função am_init( ), que
retornará o númer o da máquina servi dora ou −1 se houve erro (o amrud deve
ser instal ado pr eviamente pel o usuár io). I nicializa−se então o processo
“responde” na máquin a ser vidor a, cujo núm ero será g uardado na vari ável p1.
Dent ro de um laço de NMAX posições (defin ido com constante), faz−se
vári os envios e recebimen tos, sempre guar dando o tem po lev ado ( deve−se ter
cui dado ao defi nir a constante NMAX, pois um número excessivo de i terações
pode causar sobrecarga n a vari ável t otal) .
Par a obter o t empo, faz−se uso da função get timeof day(), declarada na
bibl ioteca
time.h.
E la
alter a
duas
estruturas
t ambém
bibl ioteca, um a é u sada p ara armazenar o tempo atual
defini das
nesta
(struct timeval) e a
outr a não será util izada pela aplicação ( struct timezone). Na estrutu ra
tim eval,
exi stem
do is
registros:
tv_sec
e
tv_usec,
que
armazenam,
resp ectiv amente o tempo em segundos e microssegundos. F oram usadas duas
vari áveis do tipo desta estrut ura para guardar o t empo. Com a prim eira, tpi,
guarda−se o t empo i nicial e com a segunda, tpf, o final .
45
Apó s o término da iteração, dimi nuí−se o valor fi nal em segun dos
(tpf.u_sec) do val or ini cial em segu ndos ( tpi.tv _sec) armazenado este valor no
pró prio
campo
tvf.tv _sec.
Segue−se
o
mesmo
procedimento
com
os
microsseg undos, armazenado o r esultado em tpf .tv_usec. Convert e−se, em
seguida, o valor em segundos para micr ossegu ndos multiplicand o−o por
1.00 0.000 e o somando este valor ao val or em microssegundos tem−se o val or
total d a it eração , tempo de ida e vol ta da mensagem, por isso é necessári o
dividir este valor por dois par a obter o tempo da transmissão.
Acum ula−se o valor obtido na variável total , e verifica−se este t empo
obti do para sab er se corresponde ao tempo mínimo ou máximo até então.
Proced e−se então à uma n ova it eração.
46
/* Processo desempenho */
#include "am_lib.c"
#include <sys/time.h>
#define NMAX 10
main()
{char msg[100];
unsigned char n;
struct timeval tpi, tpf;
struct timezone tzpi, tzpf;
unsigned long tempo, tmin=0,tmax=0, total;
int m1,m2,p1,p2,tid;
m1=am_init();
if (m1==−1)
exit(0);
m1=am_addhost("verdi");
if (m1==−1)
exit(0);
m2=am_addhost("vivaldi");
if (m2==−1)
exit(0);
p1=am_startproc(m2,"responde");
if (p1==−1)
exit(0);
for (n=0;n<NMAX;n++)
{sprintf(msg,"%s","Ola");
gettimeofday(&tpi, &tzpi);
am_send(p1,msg);
am_receive(p1,msg);
gettimeofday(&tpf, &tzpf);
tpf.tv_sec= tpf.tv_sec−tpi.tv_sec;
tpf.tv_usec= tpf.tv_usec−tpi.tv_usec;
tempo=((tpf.tv_sec*1000000)+tpf.tv_usec)/2;
fprintf(stderr,"Tempo da iteracao %2d foi %u\n",n,tempo);
total=total+tempo;
if (tmax==0)
tmax=tmin=tempo;
else
{if (tempo<tmin)
tmin=tempo;
if (tempo>tmax)
tmax=tempo;
}
}
am_send(p1,”000”);
total=total−tmin−tmax;
n=NMAX−2;
total=total/n;
fprintf(stderr,"Tempo medio foi de %u\n",total);
fprintf(stderr,"Tempo minimo foi de %u\n",tmin);
fprintf(stderr,"Tempo maximo foi de %u\n",tmax);
}
Figura 4.2− Exemplo desempenho
No final das it erações, retira−se o tem po máximo e o tempo m ínimo da
vari ável tot al (isso po rque os extremos p odem infl uenci ar negat ivamen te a
média) e divi de−se o t otal por NMAX−2 (já que f oram retir ados o resultad o
de duas iter ações) .
47
Imprim e−se então o resultado médio, míni mo e m áximo .
4.2Filósofos Jantadores
Cinco fil ósofos estão reunidos em um a mesa par a jant ar, dispondo de
cin co garfos. Cada um p recisa de do is garfos par a jantar. Co mo não há garfos
suf icient es (seriam n ecessários 10 gar fos), eles divid em os garfos ent re si, na
man eira mostrada na figu ra 4.3. Desta f orma, o Filóso fo f1 usará os garfos g 1
e g 5, o f ilósof o g2 os gar fos g1 e g2 e assim sucessivamente. Os f ilósof os or a
jan tam, ora pensam e cada filósofo, assim que conseguir pegar os dois garfos,
janta e os devolve à mesa.
Figura 4.3− Ilustração Filósofos Jantadores
Reso lver este prob lema po r paralel ismo, signifi ca criar cinco processos
garf os e cinco processos fi lósofos. Quand o um garf o estiver sendo usado por
um fi lósofo, não poderá ser usado por nenhum outro até qu e este o li bere, e
ist o só ocor rerá quando o fil ósofo estiv er satisf eito. Impl ementar este
alg oritmo requer uma série de cuidados. Um deles é refer ente à possibilidad e
48
do si stema todo entrar em deadlock. Imagine que cada filósofo, assim que for
inicializado, tentará pegar o garfo da sua d ireita, e que todos consigam. Para
que p ossam jan tar, devem também possu ir o garfo à sua esquer da, só que o
garf o à su a esqu erda é também o garfo à direi ta do f ilóso fo anterior e,
por tanto, já está reservado à este, o que fará que o f ilósof o que r equisit ou
este garfo f ique aguardando. Contudo, per cebe−se que todos os fil ósofos estão
agu ardando a li beração do garfo à sua esq uerda, o qu e r esulta que nenhum
fil ósofo jamais conseguirá pegá−lo, caracter izando deadlock. Para resol ver
est e pr oblema, é tomada uma precaução simples: o úl timo filóso fo tentará
peg ar o g arfo à sua esquerda antes do da direita. Assim, o primeiro f ilósof o
será o pr imeiro a jantar, por que somente ele conseguirá pegar o garfo da sua
direita e o da sua esquerda.
O processo que instal a os fi lósofo s e os garf os cujo código pode ser
visto n a figura 4.4 será analisado a seguir .
49
#include "am_lib.c"
main()
{long int m1,m2,tidg[5],tidf[5],cc,aux,f;
char msg[40];
m1=am_init();
if (m1==−1)
exit(0);
m1=am_addhost("verdi");
if (m1==−1)
exit(0);
m2=am_addhost("vivaldi");
if (m2==−1)
exit(0);
for (aux=0;aux<5;aux++)
{tidg[aux]=am_startproc(m1,"garfo");
if (tidg[aux]==−1)
exit(0);
}
for (aux=0;aux<5;aux++)
{tidf[aux]=am_startproc(m2,"filosofo");
if (tidf[aux]==−1)
exit(0);
}
fprintf(stderr,"Ids dos filosofos: ");
for (aux=0;aux<5;aux++)
fprintf(stderr,"%d ",tidf[aux]);
fprintf(stderr,"\nIds dos garfos: ");
for (aux=0;aux<5;aux++)
fprintf(stderr,"%d ",tidg[aux]);
fprintf(stderr,"\n");
for (f=0;f<5;f++)
{sprintf(msg,"0%d",f);
am_send(tidf[f],msg);
if (f==4)
sprintf(msg,"%d",tidg[0]);
else
sprintf(msg,"%d",tidg[f]);
am_send(tidf[f],msg);
if (f==4)
sprintf(msg,"%d",tidg[f]);
else
sprintf(msg,"%d",tidg[f+1]);
am_send(tidf[f],msg);
}
for (f=1;f<=5;f++)
{cc=am_receive(−1,msg);
fprintf(stderr,"\n**** Filosofo %d esta satisfeito\n",cc);
}
}
Figura 4.4− Processo jantar
Ini ciciali za−se o sistema pelo coman do am_init(), at ribuindo−se o valor
da máquina servi dora à v ariável m 1. Verifica−se se houve er ro, saindo do
pro grama em caso af irmati vo (a mensagem de erro já é gerada pelo
am_ addhost()). Insere−se a máqui na verdi e a vivaldi (uma d elas já havia sido
50
inserida p elo comando am_init()) at ribuindo seus valores à vari ável m 1 e m2,
respectivamente.
Cria−se com a função am_ startproc() cinco processos garf o na máqu ina
m1 (verdi) e armazena seus am _pids no vetor tidg, fazendo−se o mesmo com
os processos fil ósofos, porém criando−os na máquina m2 (vi valdi) e
armazenando seus am_p ids no veto r tidf. Im prime−se na tela o valor am_pid
de tod os os processos cr iados, para verif icação do usuário.
Apó s ser em criados os processos, envia−se a cada filósofo o núm ero que
cada um recebeu (segundo figura 4.3, nú meros de 0−4) e o númer o
identificador de seus doi s garfos, para q ue ele possa enviar−lhes mensagens.
Neste ponto é necessário t omar as medi das para ev itar deadlock. Par a t odo
tidf [aux] envia−se os am_pids dos garf os tidg[aux] e tid g[aux+1], exceto para
o f ilósofo tidf [4] (o últim o), onde esta ordem é invertida, poi s dever ia−se
enviar tidg[ 4] e t idg[0] , mas envia−se pr imeiro o tidg[0] e depoi s o ti dg[4] .
Apó s enviar os am_p ids dos garfos, este pr ocesso fica esperando o
retorno dos filósofos di zendo que estão satisfeitos, e ter mina sua execução.
O processo fi losof o tem como objet ivo requi sitar dois gar fos à mesa,
usá−los por um det erminado tempo ( defini da pela constan te T EMPO) e
dev olvê−l os, v oltand o a repeti r est e pr ocesso por n v ezes (defin ido na
constante N) .
51
#include "am_lib.c"
#define
#define
N
2
TEMPO 31000
/* Numero de vezes que jantaram*/
/* Tempo que ficaram com os garfos*/
main()
{int tidp,tidg1,tidg2,cc,data=0,conta1,conta2,nf;
char msg[40];
tidp=am_receive(−1,msg);
nf=str_int(msg);
tidp=am_receive(−1,msg);
tidg1=str_int(msg);
tidp=am_receive(−1,msg);
tidg2=str_int(msg);
sprintf(msg,"%s","1");
am_send(tidp, msg);
for (conta1=1;conta1<=N;conta1++)
{sprintf(msg,"%s","1");
am_send(tidg1,msg);
cc=am_receive(tidg1,msg);
if (*msg==’0’) conta1−−;
else
{fprintf(stderr,"Filosofo %d tem garfo 1 (%d)\n",nf,tidg1);
sprintf(msg,"%s","1");
am_send(tidg2,msg);
cc=am_receive(tidg2,msg);
if (*msg==’0’)
{sprintf(msg,"%s","0");
am_send(tidg1,msg);
fprintf(stderr,"Filosofo %d liberou garfo 1 (%d)\n"
,nf,tidg1);
conta1−−;
}
/* Neste ponto o filosofo ja tem os dois garfos
a sua disposicao */
else
{fprintf(stderr,"Filosofo %d tem garfo 2 (%d)\n",nf,tidg2);
for (conta2=0;conta2<TEMPO;conta2++);
sprintf(msg,"%s","1");
am_send(tidg1,msg);
am_send(tidg2,msg);
fprintf(stderr,"Filosofo %d ja usou os garfos\n",nf);
}
}
}
sprintf(msg,"%s","1");
am_send(tidp, msg);
}
Figura 4.5− Exemplo filosofo.c
Crio u−se uma pequena função n este processo d enomin ada str_int() qu e
apenas converte uma str ing passada por par âmetro para um intei ro, de form a
idêntica à fu nção am_strint () estudad a anterior mente. Esta fun ção se faz
necessári a já que nosso sistema perm ite troca apenas de strings, devendo o
usuário preocupar−se com p ossíveis empacotam entos e desem pacotamentos.
52
O código completo d o processo f ilosof o pode ser visto na figura 4.5 .
Em primeiro l ugar, recebe−se uma mensagem de um processo q ualquer
(so mente o pai i rá enviá−la). Define−se o p rocesso que a enviou como tidp .
Esta mensagem r ecebi da corresponde ao am_ pid do primeiro garf o, sendo
por tanto convert ida para intei ro e atr ibuída à variável tid g1, o mesmo
acontece com a segu nda mensagem r ecebid a pelo processo pai, só que seu
val or será at ribuído à variável tidg2. De posse dos am_pids de seu s dois
garf os, o processo fil osofo responde com a string “1” ao seu pai , i nform ando−
o que está pr onto para trabalhar .
O fil ósofo pode, ent ão, com eçar a requisit ar seus garfos. Dentro de um
laço de N vezes ( número d e vezes que i rá jant ar), ele envia uma m ensagem
com o text o “1” ao pr imeiro garfo, na tentat iva de usá−lo , este, por sua vez
irá retornar u ma mensagem de co nfirmação. A mensagem recebida por
am_ receive() do garfo pode ser “1”, se o g arfo agor a per tence a este filósofo
ou ‘0’ se o garfo não est á dispon ível para este processo. Se esta últim a
sit uação o correr , o pr ocesso filoso fo decr ementa a vari ável contado ra dos
laço s, p ara desconsider ar esta última iteração, e repete o processo. Mas, se o
garf o1 respo ndeu “1”, ou seja, el e está, agor a, disponí vel para este fil ósofo,
tenta−se ent ão peg ar o garfo2.
Par a ob ter o garf o2, o procedimento é idêntico ao utilizado para obter o
garf o1, com a difer ença que se desta vez el e não conseguir pegar este garfo ,
ele envi a um a mensagem “0” ao garfo1, dispensando−o − para evitar deadl ock
− e repete t odo o laço novamente desconsiderando a últ ima it eração.
Caso tenha, finalm ente, disponib ilizado os seus dois garfos, f az−se um
laço de TEMPO vezes par a si mular o temp o gasto na j anta e depois de
53
term inado este laço, envia−se uma mensagem “1” p ara os doi s garfos,
inform ando−o s que não m ais pr ecisa deles.
Depo is que o filósofo já j antou to das as vezes def inidas na constante N,
este en via um a mensagem ao seu p ai, in formand o estar sati sfeito .
#include "am_lib.c"
main()
{long int tidf,tida,chega;
char msg[20];
for(;;)
{tidf=am_receive(−1,msg);
if (*msg==’1’)
{am_send(tidf, msg);
chega=0;
for (;chega!=1;)
{tida=am_receive(−1,msg);
if (tida!=tidf)
am_send(tida,"0");
else chega=1;
}
}
}
}
Figura 4.6− Processo garfo
O últim o pr ocesso envolvido na sol ução deste problema é o p rocesso
garf o. A função dele é aceitar o pedido de requisição do p rimei ro filósofo, e
rejeitar qualquer outro pedido que venha até que o prim eiro o di spense. O
código compl eto deste pr ocesso pode ser vi sto na figur a 4.6.
O processo inicia aceitando m ensagem de qualquer out ro processo,
sab e−se que, pel a maneira co mo este probl ema foi impl ementado, o único que
env iará esta mensagem é o filósofo que o usará. Armazena−se o am_pid do
pro cesso q ue enviou esta mensagem na variável tidf e verifi ca−se se a
men sagem tem em sua p rimeir a posição o caracter ‘1’, indicando um fi lósofo
querendo usar este garfo. O processo gar fo responde, então com a st ring “1”,
env iando a mesma msg recebida, para indicar ao filósofo que agora pertence a
ele.
54
Processo garfo entra agora em um laço até que a vari ável achei seja
dif erente de 1. Dent ro deste laço, ele recebe m ensagem de qualquer pro cesso,
com para o am_pid dest e com o am_pi d d o seu fil ósofo. Se for em i guais, é
sinal que trata−se do fi lósof o, à qual pert ence atualmente, querendo infor mar
que n ão mais o está usando. Deve−se, portanto, setar a variável achei para 1,
indi cando q ue o processo filosofo dispensou este garf o, podendo o mesmo ser
requ isitado por outro filosof o. Mas se não for o mesm o filosofo, garfo en tão
envia a str ing “0” para indicar ao processo que tenta requi sitá−l o que este
garfo já per tence à um o utro processo e não pode, no m omento, atendê−lo .
5CONCLUSÃO
A gama de uso dest e ambient e não é, obviamen te, tão ampla quanto a do
PVM, mas muitos exemplos cri ados em PVM podem, com extr ema faci lidade,
ser mi grados para esta in terface. No entanto, por ser mais sim ples, este
sistema é bem mais fácil de usar que o PVM, p ois não necessita d e algu ns
com andos
bur ocráticos
usados
no
PVM
( pvm_initsend(),
pvm_pkint (),
pvm_upkint (), por exempl o). Seu estudo é extrem ament e simpl es, e facilment e
alunos poderão im plemen tar p rocessos em ambiente mul tiprogramado sem a
necessi dade de se f amiliarizar com a extensa gama de com andos do PVM.
Uma das m aiores v antagens do AMRU est á, no entanto, no fat o de que
ele usa pr imiti vas básicas de comun icações do UNIX e é relat ivamen te
pequeno. O que facilita não só a sua utilização, mas também o entendim ento
da sua estru tura i nterna.
Ist o o t orna, sem dúvida, uma ferr amenta ex tremam ente relevante d o
ponto de vista did ático.
ANEXOS
ANEXO 1 AM_LIB.C
57
/*********************************************************************/
/*
AM_LIB.C
*/
/*
AMBIENTE DE MULTIPROCESSAMENTO PARA UMA REDE DE MÁQUINAS UNIX
*/
/*
Elgio Schlemer 11/12/96
*/
/*
*/
/* Biblioteca Principal
*/
/*********************************************************************/
#include
#include
#include
#include
#include
<stdio.h>
<utmp.h>
<rpc/rpc.h>
<rpcsvc/rusers.h>
<string.h>
#define AM_MAXMAQ
#define AM_MAXPROC
4 /* Numero maximo de hosts comportadas */
20 /* Numero maximo de processos comportados */
/*************************************************************/
/* Definicao de estruturas */
struct AM_LISTHOST{
char nome[20];
int numero;
char status;
};
/* Lista das hosts participantes do sistema
nome= nome da host (ex. verdi)
numero= numero pelo qual sera reconhecida no sistema
status= 0 se campo disponivel, 1 se ocupado por host ativa
*/
struct AM_LISTPROC{
long int nproc;
int nmaq;
char status;
char nome[20];
};
/* Lista das processos inicializados por startproc
nproc= numero do processo dado pelo sistema (uma composicao
do numero da host e o id do processo
nmaq= numero da host onde encontra−se este processo
nome= nome do processo.
*/
/*************************************************************/
int am_nummaq=0,am_numproc=0; /* am_nummaq=contador de host existentes
am_numproc=contador de processos criados*/
struct AM_LISTHOST am_listmaq[AM_MAXMAQ];
struct AM_LISTPROC am_listproc[AM_MAXPROC];
char am_servidor=1;
/* Prototypes das funcoes */
int am_init();
int am_hostname(int, char *);
int am_myhost();
long int am_mytid();
long int am_converte (int ,long int);
int am_addhost(char *);
long int am_startproc(int, char *);
int am_send(long int,char *);
int am_receive(int,char *);
long int am_strint(char *);
/****************************************************************/
long int am_strint (char *am_data)
{char *am_aux;
long int am_total=0;
int am_sinal=1;
for (am_aux=am_data;(*am_aux<’0’)||(*am_aux>’9’);am_aux++)
{if (am_aux==NULL) return(−1);
if (*am_aux==’−’) am_sinal=−1;
}
for (;(*am_aux>=’0’)&&(*am_aux<=’9’);am_aux++)
am_total=(am_total*10) + (*am_aux−’0’);
sprintf(am_data,"%s",am_aux);
return(am_total*am_sinal);
}
/*************************************************************/
int am_init()
{ char nome[50],envia[100],recebe[25];
int conta;
char *env,*precebe;
int stat;
58
for (conta=0;conta<AM_MAXMAQ;conta++)
am_listmaq[conta].status=0;
for (conta=0;conta<AM_MAXPROC;conta++)
am_listproc[conta].status=0;
gethostname(nome,64);
am_servidor=0;
conta=am_addhost(nome);
sprintf(envia,"8#%d#",getpid());
env=envia;
precebe=recebe;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
am_listproc[am_numproc].status=1;
am_listproc[am_numproc].nmaq=am_myhost();
am_listproc[am_numproc].nproc=am_mytid();
return(conta);
}
/*************************************************************/
int am_hostname(int maq,char *nome)
/*
Esta funcao retorna o nome da host cujo numero e maq
*/
{
char nome2[20],*env,envia[20],recebe[20],*precebe,stat;
/* nome=usada para guardar o nome da host, conta=contador for*/
gethostname(nome,64);
/* obtem o nome da host
no laco a seguir, procurara pela ocorrencia do nome da host na
lista de hosts do sistema retornando o numero desta*/
precebe=recebe;
sprintf(envia,"6#%d#",maq);
env=envia;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if (recebe[0]==’0’)
return(−1);
if (recebe[0]==’1’)
{
sprintf(nome,"%s",recebe+2);
return(1);
}
if (recebe[0]==’2’)
{
sprintf(envia,"6#%d#",maq);
env=envia;
stat = callrpc ( recebe+2, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if (recebe[0]==’1’)
{
sprintf(nome,"%s",recebe+2);
return(1);
}
}
return(−1);
}
/*************************************************************/
int am_myhost()
/*
Esta funcao retorna o numero da host que o requisitou ou −1 caso
haja algum erro (esta maquina nao tem processo daemon, por exemplo
*/
{
char nome[20],*env="5",recebe[50],*precebe,stat;
/* nome=usada para guardar o nome da host, conta=contador for*/
int maq=0;
gethostname(nome,64);
/* obtem o nome da host
no laco a seguir, procurara pela ocorrencia do nome da host na
lista de hosts do sistema retornando o numero desta*/
precebe=recebe;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if (recebe[0]==’1’)
{am_strint(recebe);
maq=am_strint(recebe);
return(maq);
}
return(−1);
}
/*************************************************************/
long int am_mytid()
/* Obtem o numero pelo qual o processo e conhecido no sistema
Este numero e uma comnposicao binaria descrita em am_startproc
*/
{long int maq;
pid_t pid;
maq=am_myhost();
/*otem o numero da host */
maq=maq<<12;
/*compoe o numero da maquina */
pid=getpid();
/*obtem o numero do processo (no SO)*/
maq=maq+pid;
return(maq);
}
59/*************************************************************/
long int am_converte (int maq,long int tid)
{long int am_aux;
am_aux=maq<<12;
return(am_aux+tid);
}
/*************************************************************/
int am_addhost(char *maquina)
{
/* Este programa faz uma chamada RPC para o programa AMRUD
em execucao na maquina remota. Se este programa estiver
ativado ele respondera conforme solicitado. Retorna o numero
da host ou −1 se houve erro
*/
int stat,conta;
char recebe[2],buf[30],nome[20];
char *envia,*precebe;
if (am_servidor)
return(−1);
/* Verifica se a maquina ja nao esta inserida no sistema */
for(conta=0;conta<am_nummaq;conta++)
if (strcmp(maquina,am_listmaq[conta].nome)==0)
{am_listmaq[conta].status=1;
return(am_listmaq[conta].numero);
}
precebe=recebe;
envia=buf;
gethostname(nome,64);
sprintf(buf,"1#%d#%s",am_nummaq,nome);
/* envia o nome e o numero de sua host para a host inserida saber quem a
criou */
stat = callrpc ( maquina, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &envia,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina %s.\n",maquina);
return(−1);}
else
if (strcmp("1",recebe)==0)
/* procura por uma posicao disponivel na lista de hosts, a posicao e
considerada disponivel se status for 0
*/
for (conta=0;conta<AM_MAXMAQ;conta++)
if (!am_listmaq[conta].status)
{strcpy(am_listmaq[am_nummaq].nome,maquina);
/* cadastra a nova host inserida na lista de hosts */
am_listmaq[conta].numero=am_nummaq;
am_listmaq[conta].status=1;
sprintf(buf,"7#%d#%s",am_nummaq,maquina);
gethostname(nome,64);
envia=buf;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &envia,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina
%s.\n",nome);
return(−1);}
return(am_nummaq++);
}
return(−1);
}
/*************************************************************/
long int am_startproc(int maq,char *nomep)
{
/* Inicia o processo de nome nomep na maquina maq, retorna o numero
do processo criado ou −1 se houve erro. O numero do processo e uma
composicao feita a partir do numero da host com o ip do processo da
seguinte forma: faz−se um shift left do numero da host em 12 posicoes
como exemplo, se for maquina 1, expresso como 0000 0000 0000 0001b,
ao fazer esse shift, fica 0001 0000 0000 0000b, ou seja, 4096 em deci−
mal, soma−se entao a este numero o id do processo, por exemplo, processo
240, expresso como 0000 0000 1111 0000b, resoltando em 4336
(0001 0000 1111 0000b). Assim sendo, como o long int e expresso em 4
bytes, usamos o primeiro byte para numero da host (total de 256) e os
outros tres para processos (4096). Verificou−se que os ips criados pelo
Linux nao ultrapassam este limite.
*/
int stat,numero=−1,conta;
char envia[20],recebe[5],*precebe,*env;
long int proc;
60
if (am_servidor)
return(−1);
/* procura pela host na list de hosts */
for(conta=0;conta<AM_MAXMAQ;conta++)
if ((maq==am_listmaq[conta].numero)&&(am_listmaq[conta].status))
numero=conta;
if (numero==−1)
{fprintf(stderr,"numero de maquina nao existe\n");
return(−1);
}
sprintf(envia,"4#%s",nomep);
/* Gera string a ser enviada ao Deamon na host, 4 indica servico de
am_startproc seguido do nome do processo que deve sr criado */
precebe=recebe;
env=envia;
stat = callrpc ( am_listmaq[numero].nome, RUSERSPROG, RUSERSVERS,
RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina %s.\n",
am_listmaq[numero].nome);
return(−1);}
if (strcmp(recebe,"−1")==0)
{printf("Nao foi possivel iniciar processo %s\n",nomep);
return(−1);
}
proc=am_strint(recebe);
proc=am_converte(maq,proc);
/* em recebe existe o numero id (do SO) do processo criado, obtem−se agora
o numero do processo no sistema */
for (conta=0;conta<AM_MAXPROC;conta++)
/* Cadastra o processo criado na lista de processos */
if (!am_listproc[conta].status)
{am_listproc[conta].status=1;
strcpy(am_listproc[conta].nome,nomep);
am_listproc[conta].nproc=proc;
am_listproc[conta].nmaq=maq;
return(proc);
}
return(−1);
}
/*************************************************************/
int am_send(long int proc,char *msg)
{
/* Envia uma mensagem para o processo especificado */
int stat,conta,numero=−1;
char envia[100],nome[30];
char recebe[20],*precebe,*env;
numero=proc>>12;
stat=am_hostname(numero,nome);
if (stat==−1)
return(−1);
proc=proc&4095;
/* Obtem o real numero do processo, seu id, o numero pelo qual
ele e conhecido pelo SO de sua maquina */
sprintf(envia,"2#%d#%d#%s",am_mytid(),proc,msg);
/* Gera a string a ser enviada ao Daemon da maquina que possue
este processo. O numero 2 indica que trata−se do servico 2.
Segue−se o numero do processo que executou o servico e o
numero do processo para quem deseja−se enviar a mensagem.
*/
precebe=recebe;
env=envia;
stat = callrpc ( nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao na maquina %s.\n",
nome);
return(−1);}
if (strcmp(recebe,"−1")==0)
{printf("Nao foi possivel achar processo %s\n",proc);
return(−1);
}
return(1);
}
/*************************************************************/
int am_receive(int proc,char *msg)
{
/* Recebe uma mensagem do processo especificado, Se proc for −1,
indica que a mensagem pode ser de qualquer processo
*/
int stat,conta,numero=−1;
char envia[20],recebe[100],nome[20],*precebe,*env;
sprintf(envia,"3#%d#%d#",getpid(),proc);
/* Note que aqui, ao inves de enviar o numero do processo
no sistema, envia−se o seu id apenas. Isso porque quem
fara o tratamento de mensagens e o Daemon desta maquina
(esta propria que executou o am_receive), e este daemon
conhece seus "filhos" pelo seu id.
*/
precebe=recebe;
env=envia;
gethostname(nome,64);
/* O buffer de msgs e controlado pelo Daemon de cada host, cada
Daemon controla as msgs de seus filhos. Por isso esta chamada
de RPC e para a propria host que executou esta funcao.
*/
for (;recebe[0]!=’1’;)
{
stat = callrpc (nome, RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM,
xdr_wrapstring, &env,
xdr_wrapstring, &precebe );
if ( stat != 0 )
{fprintf(stderr,"ERRO: ’amrud’ nao esta execucao nesta maquina \n");
return(−1);
}
}
am_strint(recebe);
numero=am_strint(recebe);
sprintf(msg,"%s",recebe+1);
return(numero);
}
/*************************************************************/
58
ANEXO 2 AMRUD.C
59
60
/*********************************************************************/
/*
AMRUD.C
*/
/*
AMBIENTE DE MULTIPROCESSAMENTO PARA UMA REDE DE MÁQUINAS UNIX
*/
/*
Elgio Schlemer 11/12/96
*/
/*
*/
/* Programa Daemon
*/
/*********************************************************************/
#include <stdio.h>
#include <rpc/rpc.h>
#include <rpcsvc/rusers.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define AM_TAMMSG 50
#define AM_MSGMAX 10
#define AM_MAXPROC 20
/* tamanho das strings de msgs */
/* num max de msgs no buffer para cada proc */
/* Numero maximo de processos neste host */
struct AM_LISTHOST{
char nome[20];
int numero;
}am_listmaq[AM_MAXPROC];
/* Lista das hosts participantes do sistema
nome= nome da host (ex. verdi), −1 registro disponivel
numero= numero pelo qual sera reconhecida no sistema
*/
struct AM_LISTPROC {
long int nproc;
char status;
char msg[AM_MSGMAX][AM_TAMMSG];
int origem[AM_MSGMAX];
int espera;
}am_listproc[AM_MAXPROC];
/* Lista de processos. Difere−se um pouco da lista existente na
biblioteca, pelo fato de aqui nao existir o nome do processo,
nproc e o id dele do SO e ter campos para buferizar as msg.
nproc=numero do processo
msg= buffers de mensagem pendentes, cada processo pode ter ate
AM_MSGMAX mensagens esperando, cada uma de ate AM_TAMMSG caracteres.
status= 0= sem msg para ele, >0=numero de msg recebidas
espera= −3 campo disponivel, −2 ocupado−processo nao espera
msg, −1 ocupado, processo espera msg de qualquer pro−
cesso, >=0 = numero do processo de quem esta esperando
uma mensagem.
origem=processos que enviaram mensagem, −1=disponivel.
*/
char am_minhahost=0;
/* se =0, este e o servidor, >0 designa o numero desta host */
char am_servername[30];
/* nome da host servidora */
/* PROTOTYPES DAS FUNCOES */
int am_msgrec(int, int, char *);
int am_msgsend(int, int, char *);
long int am_strint (char *);
char **am_server (char **);
/****************************************************************/
int am_msgrec(int am_pidreq,int am_pidmsg,char *am_msg)
{/* Servico de recebimento de mensagens, se existir mensagem de am_pidmsg
para am_pidreq, esta sera armazenada em am_msg e a funcao retorna 1, senao
a funcao retorna −1 (o servico de send e receive e sincrono, mas a de−
cisao de por o processo para dormir e do am_receive e nao do Daemon
este ultimo apenas informa ao am_receive pelo retorno do RPC que nao
existe a am_msg requisitada). Se am_pidmsg for −1, indica receber msg de
qualquer processo.
*/
int conta,conta2;
/* procura a posicao do buffer pertencente ao processo que requisitou
o pedido de receive */
for (conta=0;conta<AM_MAXPROC;conta++)
if (am_listproc[conta].nproc==am_pidreq)
{
if (am_listproc[conta].status==0)
/* se status e zero, nao ha msg para este processo */
return(−1);
if (am_pidmsg==−1)
/* −1 indica receber de qualquer processo */
for (conta2=0;conta2<AM_MSGMAX;conta2++)
if (am_listproc[conta].origem[conta2]!=−1)
{sprintf(am_msg,"1#%d#%s",am_listproc[conta].origem[conta2],am_listproc[conta].ms
g[conta2]);
/* monta a string de retorno, 1 indica sucesso, segue o
numero do processo qeu enviou a msg (util caso am_pidmsg
fosse −1) e em seguida a mensagem
*/
am_listproc[conta].origem[conta2]=−1;
/*disponibiliza a posicao do buffer */
am_listproc[conta].status−−;
return(1);
}
for (conta2=0;conta2<AM_MSGMAX;conta2++)
if (am_listproc[conta].origem[conta2]==am_pidmsg)
{
sprintf(am_msg,"1#%d#%s",am_pidmsg,am_listproc[conta].msg[conta2]);
am_listproc[conta].origem[conta2]=−1;
am_listproc[conta].status−−;
return(1);
}
}
return(−1);
}
/****************************************************************/
int am_msgsend(int origem,int destino,char *am_msg)
{ /* Coloca mensagem no buffer retorna 1 se sucesso, ou −1 se erro (buffer cheio,
por exemplo
*/
int conta,conta2;
for (conta=0;conta<AM_MAXPROC;conta++)
/* procura posicao de buffer do processo destino */
if (am_listproc[conta].nproc==destino)
/* procura no buffer espaco disponivel para armazenar a msg */
for (conta2=0;conta2<AM_MSGMAX;conta2++)
{
if (am_listproc[conta].origem[conta2]==−1)
{/* buferiza a mensagem */
am_listproc[conta].origem[conta2]=origem;
am_listproc[conta].status=am_listproc[conta].status++;
strcpy(am_listproc[conta].msg[conta2],am_msg);
return(1);
}
}
return(−1);
}
/****************************************************************/
long int am_strint (char *data)
/* Retira o primeiro numero inteiro encontrado na string data,
retorna−o convertido para inteiro e retira−o da string
*/
{char *aux;
long int total=0;
int sinal=1;
for (aux=data;(*aux<’0’)||(*aux>’9’);aux++)
{if (aux==NULL) return(−1);
if (*aux==’−’) sinal=−1;
}
for (;(*aux>=’0’)&&(*aux<=’9’);aux++)
total=(total*10) + (*aux−’0’);
sprintf(data,"%s",aux);
return(total*sinal);
}
/****************************************************************/
char **am_server (char **indata)
{/* Programa responsavel pelo atendimento das chamadas de RPC,
atraves das strings enviadas pelas chamadas, determina o tipo de
servico requizitado e exetua−o
*/
static char data[AM_TAMMSG];
static char *pd,*rd;
int stat,pid,am_pidreq,am_pidmsg;
rd=*indata;
switch (**indata) {
case ’1’:
/* Teste, usado pelo addhost */
am_strint(rd);
am_minhahost=(char)am_strint(rd);
sprintf(am_servername,"%s",rd+1);
data[0]=’1’;
data[1]=’\0’;
break;
case ’2’:
/* Send */
data[0]=’1’;
data[1]=’\0’;
am_strint(rd);
am_pidreq=am_strint(rd);
am_pidmsg=am_strint(rd);
am_msgsend(am_pidreq,am_pidmsg,rd+1);
break;
case ’3’:
/* Receive */
am_strint(rd);
am_pidreq=am_strint(rd);
am_pidmsg=am_strint(rd);
stat=am_msgrec(am_pidreq,am_pidmsg,data);
if (stat==−1)
{data[0]=’0’;
data[1]=’\0’;
}
break;
case ’4’:
/* Startproc */
pid=fork();
if (!pid)
{
execv(&rd[2],NULL);exit(0);
}
sprintf(data,"%d",pid);
for (stat=0;stat<AM_MAXPROC;stat++)
if (am_listproc[stat].espera==−3)
{
am_listproc[stat].espera=−2;
am_listproc[stat].nproc=pid;
for (pid=0;pid<AM_MSGMAX;pid++)
am_listproc[stat].origem[pid]=−1;
stat=AM_MAXPROC+1;
}
if (stat==AM_MAXPROC)
{sprintf(data,"%d",−1);
break;
}
break;
case ’5’: /* Myhost */
sprintf(data,"1#%d",am_minhahost);
break;
case ’6’: /* am_hostname */
am_strint(rd);
pid=am_strint(rd);
sprintf(data,"0");
if (am_minhahost==0)
{for (stat=0;stat<AM_MAXPROC;stat++)
if (am_listmaq[stat].numero==pid)
sprintf(data,"1#%s",am_listmaq[stat].nome);
}
else sprintf(data,"2#%s",am_servername);
break;
case ’7’: /* cadastra nova host */
am_strint(rd);
pid=am_strint(rd);
for (stat=0;stat<AM_MAXPROC;stat++)
if (am_listmaq[stat].numero==−1)
{
am_listmaq[stat].numero=pid;
sprintf(am_listmaq[stat].nome,"%s",rd+1);
stat=AM_MAXPROC;
sprintf(data,"%s","1");
}
break;
case ’8’:
/* registra o processo servidor*/
am_strint(rd);
pid=am_strint(rd);
am_listproc[0].espera=−2;
am_listproc[0].nproc=pid;
for (pid=0;pid<AM_MSGMAX;pid++)
am_listproc[0].origem[pid]=−1;
break;
default:
data[0]=’2’;
data[1]=’\0’;
}
pd=data;
return(&pd);
}
/****************************************************************/
main()
{int aux=0;
for (aux=0;aux<AM_MAXPROC;aux++)
/* inicializa campos da lista de am_listproc */
{am_listproc[aux].espera=−3;
am_listproc[aux].status=0;
am_listmaq[aux].numero=−1;
}
registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, am_server,
xdr_wrapstring, xdr_wrapstring);
svc_run();
fprintf(stderr,"Erro: svc_run retornou!\n");
exit(1);
}
BIBLIOGRAFIA
[ S CH 96]
S CHL E ME R, E l g i o . Am bi ent e de Mu l t i p r ocessam en t o par a
um a Rede d e Máq ui n as UNI X : T r ab al h o sub m et i d o com o
r equi si t o i n i ci al p ar a a o b te nçã o d o gr au de Bach ar el em
I nf or m át i ca. Canoas: Depar t am ent o de I n for má t ic a d a
UL BRA. 199 6 .
[ S HA 87]
S HAW, My r i l Cl em ent . UNI X I nt er nal s . US A: T AB bo o ks,
1987.
[ S UN 90]
S UN. Net wor k P r og r am m i ng Gu i d e . 1 990.
Download

ambiente de multiprocessamento para uma rede de máquinas unix