Internet Windows Android

Protocolul Rpc. Proceduri de la distanță: Apeluri de procedură de la distanță, definiție și caracteristici

Programele care comunică printr-o rețea au nevoie de un mecanism de comunicare. La nivelul inferior, la sosirea pachetelor, un semnal este procesat de programul de procesare a semnalului de rețea. La nivel superior funcționează mecanismul de întâlnire (rendezvous), adoptat în limbajul Ada. NFS folosește un mecanism de apel de procedură la distanță (RPC) în care clientul comunică cu serverul (vezi Figura 1). În conformitate cu acest proces, clientul accesează mai întâi o procedură care trimite o cerere către server. La sosirea unui pachet cu o cerere, serverul apelează procedura de deschidere a acestuia, efectuează serviciul solicitat, trimite un răspuns și controlul este returnat clientului.

Interfața RPC poate fi considerată ca având trei straturi:

Nivelul superior este complet transparent. Un program la acest nivel poate, de exemplu, să apeleze rnusers (), care returnează numărul de utilizatori de pe mașina de la distanță. Nu trebuie să știți despre utilizarea mecanismului RPC deoarece efectuați apelul în program.

Nivelul de mijloc este pentru cele mai comune aplicații. Apelurile RPC la acest nivel sunt gestionate de rutinele registerrpc () și callrpc (): registerrpc () primește cod întunecat la nivelul întregului sistem, iar callrpc () execută un apel de procedură la distanță. Apelul rnusers () este implementat folosind aceste două rutine.

Nivelul inferior este utilizat pentru sarcini mai complexe, schimbând valorile implicite la valorile parametrilor procedurii. La acest nivel, puteți manipula în mod explicit socket-urile utilizate pentru transmiterea mesajelor RPC.

Ca regulă generală, ar trebui să utilizați stratul superior și să evitați utilizarea inutilă a straturilor inferioare.

În ciuda faptului că în acest tutorial luăm în considerare doar interfața în C, apelurile la procedurile de la distanță pot fi făcute din orice limbă. Funcționarea mecanismului RPC pentru organizarea comunicării între procese pe diferite mașini nu diferă de activitatea sa pe aceeași mașină.

RPC (Remote Procedure Call) este o interfață între utilizatori la distanță și programe gazdă specifice care sunt invocate de acești utilizatori. Un serviciu RPC pentru o gazdă oferă de obicei clienților o suită de programe. Fiecare dintre aceste programe, la rândul său, constă dintr-una sau mai multe proceduri de la distanță. De exemplu, un serviciu de sistem de fișiere la distanță NFS care se bazează pe apeluri RPC poate consta doar din două programe: de exemplu, un program interacționează cu interfețe de utilizator de nivel înalt, iar celălalt cu funcții I/O de nivel scăzut.

Fiecare apel RPC implică două părți: clientul activ, care trimite cererea de apel de procedură către server și serverul, care trimite răspunsul către client.

Notă. Rețineți că termenii „client” și „server” în acest caz se referă la o anumită tranzacție. O anumită gazdă sau software (proces sau program) poate acționa atât ca client, cât și ca server. De exemplu, un program care oferă un serviciu de procedură la distanță poate fi în același timp un client pentru un sistem de fișiere din rețea.

RPC este construit pe un model de apel de procedură la distanță similar cu apelurile de procedură locală. Când apelați o procedură locală, împingeți argumente într-o anumită locație de memorie, stivă sau variabile de mediu și transferați controlul procesului la o anumită adresă. După finalizarea lucrării, citiți rezultatele la o anumită adresă și continuați procesul.

În cazul unei proceduri la distanță, principala diferență este că apelul funcției de la distanță este servit de două procese: procesul client și procesul server.

Procesul client trimite un mesaj către server, care include parametrii procedurii apelate și așteaptă un mesaj de răspuns cu rezultatele muncii sale. Când se primește un răspuns, rezultatul este citit și procesul continuă. Pe partea de server, procesul de gestionare a apelurilor este în starea de așteptare, iar când sosește un mesaj, citește parametrii procedurii, îl execută, trimite un răspuns și devine în starea de așteptare pentru următorul apel.

Protocolul RPC nu impune nicio cerință privind conexiunile suplimentare între procese și nu necesită sincronizarea funcțiilor efectuate, adică apelurile pot fi asincrone și neindependente, astfel încât clientul să poată executa alte proceduri în așteptarea unui răspuns. Serverul RPC poate aloca un proces separat sau o mașină virtuală pentru fiecare funcție, prin urmare, fără a aștepta finalizarea solicitărilor anterioare, poate accepta imediat următoarea.

Cu toate acestea, există câteva diferențe importante între apelurile de procedură locale și cele de la distanță:

1. Eroare la procesare. Clientul ar trebui în orice caz să fie anunțat despre erorile care apar la apelarea procedurilor de la distanță pe server sau în rețea.

2. Variabile globale. Deoarece serverul nu are acces la spațiul de adrese al clientului, nu puteți utiliza parametrii ascunși sub formă de variabile globale în apelurile de procedură la distanță.

3. Performanţă. Viteza de execuție a procedurilor de la distanță, de regulă, este cu unul sau două ordine de mărime mai mică decât viteza de execuție a procedurilor locale similare.

4. Autentificare. Deoarece apelurile de procedură la distanță au loc prin rețea, trebuie utilizate mecanisme de autentificare a clientului.

Principii de construire a protocolului.

Protocolul RPC poate folosi mai multe protocoale de transport diferite. Singurele responsabilități ale protocolului RPC sunt de a aplica standardele și de a interpreta transferul de mesaje. Fiabilitatea și fiabilitatea transmiterii mesajelor este asigurată în întregime de stratul de transport.

Cu toate acestea, RPC poate controla alegerea și unele funcții ale protocolului de transport. Ca exemplu de interacțiune dintre RPC și protocolul de transport, luați în considerare procedura de atribuire a unui port RPC unui proces de aplicație prin RPC - Portmapper.

Această funcție atribuie dinamic (la cerere) un anumit port unei conexiuni RPC. Funcţie Portmapper este folosit destul de des deoarece setul de porturi de transport rezervate pentru RPC este limitat, iar numărul de procese care pot rula concomitent este foarte mare. Portmapper, de exemplu, apelat atunci când sunt selectate porturile de comunicare client/server NFS.

Serviciu Portmapper folosește mecanismul de difuzare a mesajelor RPC către un anumit port - III. Pe acest port, clientul transmite o solicitare pentru portul unui anumit serviciu RPC. Serviciu Portmapper procesează mesajul fiscal, determină adresa serviciului RPC local și trimite un răspuns clientului. Serviciul RPC Portmapper poate funcționa atât cu protocoalele TCP, cât și cu UDP.

RPC poate funcționa cu diverse protocoale de transport, dar nu le dublează niciodată funcțiile, adică dacă RPC funcționează peste TCP, RPC pune toate grijile cu privire la fiabilitatea și fiabilitatea conexiunii pe TCP. Cu toate acestea, dacă RPC este instalat peste UDP, acesta poate oferi funcționalitate nativă suplimentară pentru a se asigura că livrarea mesajelor este garantată.

Notă. Aplicațiile pot vizualiza protocolul RPC ca o procedură definită de apelare a funcției prin rețeaua JSR (Jump Subroutine Instruction).

Pentru ca protocolul RPC să funcționeze, trebuie îndeplinite următoarele condiții:

1. Identificarea unică a tuturor procedurilor apelate de la distanță pe o anumită gazdă. Cererile RPC conțin trei câmpuri de identificatori - numărul programului de la distanță (serviciu), numărul versiunii programului la distanță și numărul procedurii de la distanță a programului specificat. Numărul programului este atribuit de producătorul serviciului, numărul procedurii indică funcția specifică a acestui serviciu

2. Identificarea versiunii protocolului RPC. Mesajele RPC conțin un câmp pentru versiunea protocolului RPC. Este folosit pentru a potrivi formatele parametrilor transmisi atunci când clientul lucrează cu diferite versiuni de RPC.

3. Furnizarea de mecanisme de autentificare a clientului la server. Protocolul RPC oferă o procedură de autentificare a clientului în serviciu și, dacă este necesar, cu fiecare cerere sau trimitere a unui răspuns către client. În plus, RPC permite utilizarea diferitelor mecanisme de securitate suplimentare.

RPC poate folosi patru tipuri de mecanisme de autentificare:

AUTH_NULL - nu a fost folosită nicio autentificare

AUTH_UNIX - autentificare standard UNIX

AUTH_SHORT - autentificare UNIX cu propria sa structură de codificare

AUTH_DES - autentificare DES

4. Identificarea mesajelor de răspuns la solicitările corespunzătoare. Mesajele de răspuns RPC conțin ID-ul cererii pe care s-au bazat. Acest identificator poate fi numit identificatorul de tranzacție al apelului RPC. Acest mecanism este util în special atunci când se lucrează în modul asincron și când se execută o secvență de mai multe apeluri RPC.

5. Identificarea erorilor de protocol. Toate erorile de rețea sau server au identificatori unici prin care fiecare dintre participanții la conexiune poate determina cauza defecțiunii.

Structuri de mesaje de protocol

Când se transferă mesaje RPC printr-un protocol de transport, mai multe mesaje RPC pot fi localizate într-un pachet de transport. Pentru a distinge un mesaj de altul, se folosește un marcator de înregistrare (RM - Record Marker). Fiecare mesaj RPC este „marcat” cu exact un RM.

Un mesaj RPC poate fi compus din mai multe fragmente. Fiecare bucată constă din patru octeți de antet și (de la 0 la 2 ** 31-1) date. Primul bit al antetului indică dacă bucata este ultima, iar restul de 31 de biți indică lungimea pachetului de date.

Structura RPC este descrisă oficial în limbajul de descriere și reprezentare a formatelor de date - XDR cu completări referitoare la descrierea procedurilor. Puteți spune chiar că limbajul de marcare RPC este o extensie a XDR, completată de lucrul cu proceduri.

Structura pachetului RPC arată astfel:

struct rpc_msg (

unsigned int xid;

comutator de unire (msg_type mtype) (

call_body cbody;

răspuns body rbody;

unde xid este identificatorul tranzacției curente, call_body este pachetul de solicitare, reply_body este pachetul de răspuns. Structura cererii arată cam așa:

struct call body (

unsigned int rpcvers;

unsigned int prog;

unsigned int vers;

unsigned int proc;

opac_auth cred;

opaque_auth verf;

/ * parametrii procedurii * /

Structura reply_body poate conține fie o structură transmisă în cazul unei erori (caz în care conține codul de eroare), fie o structură pentru procesarea cu succes a cererii (caz în care conține datele returnate).

Interfață de programare la nivel înalt.

Utilizarea subrutinelor într-un program este modalitatea tradițională de a structura o sarcină, de a o face mai clară. Cele mai frecvent utilizate subrutine sunt colectate în biblioteci, unde pot fi folosite de diverse programe. În acest caz, vorbim despre un apel local (local), adică atât apelantul, cât și obiectele apelate lucrează în cadrul aceluiași program pe același computer.

În cazul unei invocări de la distanță, un proces care rulează pe un computer începe procesul de pe computerul de la distanță (adică rulează de fapt codul de procedură pe computerul de la distanță). Evident, un apel de procedură la distanță diferă semnificativ de unul local tradițional, dar din punctul de vedere al unui programator, practic nu există astfel de diferențe, adică arhitectura unui apel de procedură la distanță vă permite să simulați un apel local.

Totuși, dacă, în cazul unui apel local, programul transmite parametri procedurii apelate și primește rezultatul muncii sale prin stiva sau zonele de memorie partajată, atunci în cazul unui apel la distanță, transferul parametrilor se transformă în o transmitere a unei cereri prin rețea, iar rezultatul muncii este în răspunsul primit.

Această abordare este o posibilă bază pentru crearea de aplicații distribuite și, deși multe sisteme moderne nu folosesc acest mecanism, conceptele și termenii de bază rămân în multe cazuri. Când descriem mecanismul RPC, ne vom referi în mod tradițional la procesul de apelare drept client și procesul de la distanță care implementează procedura ca server.

Un apel de procedură de la distanță include următorii pași:

1. Programul client efectuează un apel local către o procedură numită stub. În acest caz, clientul „pare” că, apelând stub-ul, efectuează de fapt un apel către procedura serverului. Într-adevăr, clientul trece parametrii necesari stub-ului și returnează rezultatul. Cu toate acestea, nu este chiar așa cum își imaginează clientul. Sarcina stub-ului este să accepte argumente pentru procedura de la distanță, eventual să le convertească într-un format standard și să formeze o cerere de rețea. Împachetarea argumentelor și efectuarea unei cereri de rețea se numește marshalling.

2. Solicitarea de rețea este trimisă prin rețea către sistemul de la distanță. Pentru a face acest lucru, stub-ul folosește apelurile corespunzătoare, de exemplu, cele discutate în secțiunile anterioare. Rețineți că în acest caz pot fi utilizate diverse protocoale de transport și nu numai familia TCP/IP.

3. Pe gazda de la distanță, totul se întâmplă în ordine inversă. Stub-ul serverului așteaptă o solicitare și, la primire, preia parametrii — argumentele apelului de procedură. Extragerea (unmarshalling) poate implica transformări necesare (de exemplu, schimbarea ordinii octeților).

4. Stub-ul efectuează un apel către procedura serverului real căruia îi este adresată cererea clientului, trecându-i argumentele primite prin rețea.

5. După finalizarea procedurii, controlul revine în stub-ul serverului, transmițându-i parametrii necesari. Ca un ciot de client; stub-ul serverului convertește valorile returnate de procedură pentru a forma un mesaj de răspuns al rețelei care este trimis prin rețea către sistemul de la care a venit cererea.

6. Sistemul de operare transmite mesajul primit stub-ului clientului, care, după transformarea necesară, transmite valorile (care sunt valorile returnate de procedura de la distanță) clientului, care interpretează acest lucru ca o întoarcere normală. din procedura.

Astfel, din punctul de vedere al clientului, efectuează un apel de procedură la distanță, așa cum ar fi pentru unul local. Același lucru se poate spune despre server: procedura este apelată într-un mod standard, un obiect (server stub) apelează procedura locală și primește valorile returnate de acesta. Clientul tratează stub-ul ca pe o procedură de server apelabilă, iar serverul își interpretează propriul stub ca client.

Astfel, stub-urile constituie nucleul sistemului RPC, responsabil pentru toate aspectele generării și transmiterii mesajelor între client și serverul de la distanță (procedură), deși atât clientul, cât și serverul presupun că apelurile sunt efectuate local. Acesta este conceptul de bază al RPC - pentru a ascunde complet natura distribuită (de rețea) a comunicării în codul stub. Avantajele acestei abordări sunt evidente: atât clientul, cât și serverul sunt independente de implementarea rețelei, ambele operează într-o mașină virtuală distribuită, iar apelurile de procedură au o interfață standard.

Trecerea parametrilor

Transmiterea parametrilor valorii este simplă. În acest caz, stub-ul clientului plasează valoarea parametrului în cererea de rețea, eventual efectuând conversii la forma standard (de exemplu, modificarea ordinii octeților). Situația cu trecerea de pointeri este mult mai complicată, când parametrul este adresa datelor, și nu valoarea acestora. Transmiterea unei adrese într-o cerere nu are sens, deoarece procedura de la distanță este executată într-un spațiu de adrese complet diferit. Cea mai simplă soluție RPC este de a împiedica clienții să treacă parametri altfel decât prin valoare, deși acest lucru impune cu siguranță restricții serioase.

Legare

Înainte ca un client să poată apela o procedură la distanță, acesta trebuie să se lege de sistemul de la distanță care găzduiește serverul necesar. Astfel, sarcina de conectare este împărțită în două:

Găsirea gazdei la distanță cu serverul necesar

Găsirea procesului de server necesar pe o anumită gazdă

Pentru a găsi o gazdă pot fi folosite diverse abordări. O posibilă opțiune este crearea unui fel de director centralizat, în care gazdele își anunță serverele și unde clientul, dacă dorește, poate alege adresa de gazdă și de procedură potrivită pentru el.

Fiecare procedură RPC este identificată în mod unic printr-un număr de program și de procedură. Numărul programului definește un grup de proceduri de la distanță, fiecare dintre ele având propriul număr. Fiecărui program i se atribuie, de asemenea, un număr de versiune, astfel încât atunci când se fac modificări minore programului (de exemplu, când se adaugă o procedură), să nu fie nevoie să-i schimbi numărul. De obicei, mai multe proceduri similare funcțional sunt implementate într-un singur modul de program, care, atunci când este lansat, devine serverul acestor proceduri și care este identificat prin numărul programului.

Astfel, atunci când un client dorește să apeleze o procedură de la distanță, trebuie să cunoască programul, versiunea și numerele de procedură care oferă serviciul necesar.

Pentru a trece cererea, clientul trebuie să cunoască și adresa de rețea a gazdei și numărul portului asociat programului server care oferă procedurile necesare. Acest lucru se face folosind demonul portmap (IM) (numit rpcbind (IM) pe unele sisteme). Daemonul rulează pe gazda care furnizează serviciul de procedură la distanță și utilizează un număr de port binecunoscut. Când un proces de server se inițializează, acesta își înregistrează rutinele și numerele de port în portmap (IM). Acum, când clientul trebuie să cunoască numărul portului pentru a apela o anumită procedură, acesta trimite o cerere către serverul portmap (IM), care, la rândul său, fie returnează numărul portului, fie redirecționează cererea direct către serverul RPC și returnează un răspuns la client după executarea acestuia. În orice caz, dacă procedura necesară există, clientul primește numărul portului procedurii de la serverul portmap (IM), iar cererile ulterioare pot fi făcute direct pe acest port.

Gestionarea excepțiilor

Gestionarea excepțiilor la apelarea procedurilor locale nu este deosebit de problematică. UNIX gestionează erorile de proces, cum ar fi împărțirea la zero, accesul la memorie invalid și așa mai departe. Apelarea unei proceduri de la distanță crește probabilitatea unor situații de eroare. Adăugate erorilor server și stub sunt erori legate, de exemplu, de primirea unui mesaj de rețea eronat.

De exemplu, când se utilizează UDP ca protocol de transport, mesajele sunt retransmise după un timeout specificat. O eroare este returnată clientului dacă, după un anumit număr de încercări, nu a fost primit un răspuns de la server. În cazul în care se utilizează protocolul TCP, o eroare este returnată clientului dacă serverul a terminat conexiunea TCP.

Semantica apelurilor

Apelarea unei proceduri locale duce fără ambiguitate la executarea acesteia, după care controlul revine la programul principal. Situația este diferită atunci când apelați o procedură de la distanță. Este imposibil de stabilit când exact va fi executată procedura, dacă va fi efectuată deloc și, dacă da, de câte ori? De exemplu, dacă cererea este primită de sistemul de la distanță după ce programul serverului se termină anormal, procedura nu va fi executată deloc. Dacă clientul, când nu primește un răspuns după o anumită perioadă de timp (timeout), retrimite cererea, atunci poate apărea o situație când răspunsul este deja transmis prin rețea și cererea repetată este din nou acceptată pentru procesare prin procedura de la distanță. În acest caz, procedura va fi efectuată de mai multe ori.

Astfel, execuția unei proceduri la distanță poate fi caracterizată prin următoarea semantică:

- O singură dată. Acest comportament (în unele cazuri cel mai de dorit) este dificil de aplicat din cauza posibilelor blocări ale serverului.

- Timp maxim. Aceasta înseamnă că procedura fie nu a fost efectuată deloc, fie a fost efectuată o singură dată. O declarație similară poate fi făcută atunci când se primește o eroare în loc de un răspuns normal.

- Cel puțin o dată. Procedura a fost probabil efectuată o dată, dar mai multe sunt posibile. Pentru funcționarea normală într-o astfel de situație, procedura de la distanță trebuie să aibă proprietatea de idempotenta (din engleza idemponent). Această proprietate este deținută de o procedură, a cărei execuție repetată nu provoacă modificări cumulate. De exemplu, citirea unui fișier este idempotent, dar adăugarea de text într-un fișier nu este.

Prezentarea datelor

Când clientul și serverul rulează pe același sistem pe același computer, nu există probleme de incompatibilitate a datelor. Datele binare sunt reprezentate în același mod atât pentru client, cât și pentru server. În cazul unui apel de la distanță, problema este complicată de faptul că clientul și serverul pot rula pe sisteme cu arhitecturi diferite cu reprezentări de date diferite (de exemplu, reprezentarea în virgulă mobilă, ordinea octetilor etc.)

Majoritatea implementărilor RPC definesc o reprezentare standard a datelor la care trebuie convertite toate valorile transmise în cereri și răspunsuri.

De exemplu, formatul pentru reprezentarea datelor în RPC de la Sun Microsystems este următorul:

Ordinea octetilor - Cel mai semnificativ - Ultimul

Reprezentare în virgulă mobilă - IEEE

Reprezentarea caracterelor - ASCII

Din punct de vedere al funcționalității, sistemul RPC este intermediar între nivelul de aplicație și cel de transport. Conform modelului OSI, această prevedere corespunde straturilor de prezentare și sesiune. Astfel, RPC este teoretic independent de implementarea rețelei, în special de protocoalele de rețea din stratul de transport.

Implementările software ale sistemului, de regulă, acceptă unul sau două protocoale. De exemplu, sistemul RPC al Sun Microsystems acceptă mesageria folosind protocoalele TCP și UDP. Alegerea unuia sau altuia protocol depinde de cerințele aplicației. Alegerea UDP este justificată pentru aplicații cu următoarele caracteristici:

Procedurile numite sunt idempotente

Mărimea argumentelor transmise și rezultatul returnat este mai mică decât dimensiunea pachetului UDP - 8 KB.

Serverul oferă lucru cu câteva sute de clienți. Deoarece, atunci când lucrează cu protocoale TCP, serverul este forțat să mențină o conexiune cu fiecare dintre clienții activi, aceasta ocupă o parte semnificativă a resurselor sale. UDP consumă mai puțin resurse în acest sens.

Pe de altă parte, TCP asigură operarea eficientă a aplicațiilor cu următoarele caracteristici:

Aplicația necesită un protocol de transfer de încredere

Procedurile numite nu sunt identice

Argumentele sau rezultatul returnat este mai mare de 8KB

Alegerea protocolului rămâne de obicei la client, iar sistemul organizează formarea și transmiterea mesajelor în moduri diferite. Deci, atunci când se utilizează protocolul TCP, pentru care datele transmise sunt un flux de octeți, este necesar să se separe mesajele unul de celălalt. Acest lucru se poate face, de exemplu, utilizând protocolul de etichetare a înregistrărilor RFC1057 „RPC: Remote Procedure Call Protocol specification version 2”, care precede fiecare mesaj cu un număr întreg de 32 de biți care specifică dimensiunea mesajului în octeți.

Situația este diferită cu semantica apelului. De exemplu, dacă RPC este efectuat folosind un protocol de transport nefiabil (UDP), sistemul retransmite mesajul la intervale scurte (timeout). Dacă aplicația client nu primește un răspuns, atunci este sigur să spunem că procedura a fost executată de zero sau de mai multe ori. Dacă s-a primit un răspuns, cererea poate concluziona că procedura a fost executată cel puțin o dată. Când se utilizează protocolul de transport de încredere (TCP), dacă se primește un răspuns, se poate spune că procedura a fost executată o singură dată. Dacă nu se primește niciun răspuns, este imposibil să spunem cu siguranță că procedura nu a fost efectuată3.

Cum functioneaza?

În esență, sistemul RPC real este încorporat în programul client și programul server. Este îmbucurător că atunci când dezvoltați aplicații distribuite, nu trebuie să vă aprofundați în detaliile protocolului RPC sau procesării mesajelor programului. Sistemul presupune existența unui mediu de dezvoltare adecvat, care facilitează foarte mult viața creatorilor de aplicații software. Unul dintre punctele cheie în RPC este că dezvoltarea unei aplicații distribuite începe cu definirea unei interfețe obiect - o descriere formală a funcțiilor serverului, scrisă într-un limbaj special. Stub-urile client și server sunt apoi generate automat din această interfață. Singurul lucru care trebuie făcut după aceasta este să scrieți codul de procedură real.

Ca exemplu, luați în considerare RPC de la Sun Microsystems. Sistemul este format din trei părți principale:

Rpcgen (1) este un compilator RPC care generează stub-uri client și server ca programe C bazate pe descrierea interfeței procedurii de la distanță.

Biblioteca XDR (eXternal Data Representation), care conține funcții pentru conversia diferitelor tipuri de date într-o formă independentă de mașină, permițând schimbul de informații între sisteme eterogene.

O bibliotecă de module care asigură funcționarea sistemului în ansamblu.

Să ne uităm la un exemplu de aplicație de bază de înregistrare a evenimentelor distribuite. Clientul, la pornire, apelează procedura de la distanță pentru a scrie un mesaj în fișierul jurnal al computerului la distanță.

Pentru a face acest lucru, va trebui să creați cel puțin trei fișiere: specificația interfețelor procedurilor de la distanță log.x (în limbajul de descriere a interfeței), textul propriu-zis al procedurilor de la distanță log.c și textul procedurilor de la distanță log.c. programul principal al clientului main () - client.c (în limbajul C).

Compilatorul rpcgen (l) generează trei fișiere pe baza specificației log.x: clientul C și stub-urile serverului (log clnt.c și log svc.c) și fișierul de definiție log.h utilizat de ambele stub-uri.

Deci, să ne uităm la codul sursă al programelor.

Acest fișier specifică parametrii de înregistrare ai procedurii de la distanță — numere de program, versiune și proceduri — și definește interfața de apelare — argumente de intrare și valori returnate. Astfel, este definită procedura RLOG, care ia ca argument un șir (care va fi scris în jurnal), iar valoarea returnată, implicit, indică succesul sau eșecul operației ordonate.

programul LOG_PROG (

versiunea LOG_VER (

int RLOG (șir) = 1;

) = 0x31234567;

Compilatorul rpcgen (l) generează un fișier antet log.h unde, în special, sunt definite procedurile:

log.h

* Vă rugăm să nu editați acest fișier.

* A fost generat folosind rpcgen.

#ifndef _LOG_H_RPCGEN

#define _LOG_H_RPCGEN

#include

/ * Numărul programului * /

#define LOG_PROG ((nesemnat lung) (0x31234567))

#define LOG_VER ((nesemnat lung) (1)) / * Numărul versiunii * /

#define RLOG ((nesemnat lung) (1)) / * Număr de rutină * /

extern int * rlog_l ();

/ * Procedura internă - nu va trebui să o folosim * / extern int log_prog_l_freeresult ();

#endif / *! _LOG_H_RPCGEN * /

Să aruncăm o privire mai atentă la acest fișier. Compilatorul traduce numele RLOG definit în descriptorul de interfață în rlog_1, înlocuind caracterele majuscule cu minuscule și adăugând numărul versiunii programului cu un caracter de subliniere. Tipul de returnare s-a schimbat de la int la int *. Aceasta este regula - RPC vă permite să trimiteți și să primiți doar adresele parametrilor declarați la descrierea interfeței. Aceeași regulă se aplică șirului transmis ca argument. Deși acest lucru nu rezultă din fișierul print.h, de fapt, adresa liniei este transmisă și ca argument funcției rlog_l ().

În plus față de fișierul antet, compilatorul rpcgen (l) generează module client stub și server stub. În esență, textul acestor fișiere conține tot codul de apel de la distanță.

Server stub este programul principal care se ocupă de toată interacțiunea rețelei cu clientul (mai precis, cu stub-ul său). Pentru a efectua operația, stub-ul serverului efectuează un apel local la funcție, al cărui text trebuie scris:

log.c

#include

#include

#include

#include „log.h”

int * rlog_1 (char ** arg)

/ * Valoarea returnată trebuie definită ca static * /

rezultat static int;

int fd; / * Descriptor de fișier jurnal * /

/ * 0 deschideți fișierul jurnal (creați-l dacă nu există), în caz de eșec, returnați rezultatul codului de eroare == 1. * /

dacă ((fd = deschis (""./ server .log",

O_CREAT | O_RDWR | O_APPEND))< 0) return (&result);

len = strlen (* arg);

dacă (scrieți (fd, * arg, strlen (* arg))! = len)

returnare (& rezultat); / * Returnează rezultatul - rezultatul adresei * /

Stub-ul clientului preia un argument transmis procedurii de la distanță, face transformările necesare, emite o cerere către serverul portmap (1M), comunică cu serverul procedurii de la distanță și, în final, transmite valoarea returnată clientului. Pentru client, un apel de procedură la distanță este un apel stub și nu este diferit de un apel local obișnuit.

client.c

#include

#include „log.h”

main (int argc, char * argv)

char * server, * mystring, * clnttime;

dacă (argc! = 2) (

fprintf (stderr, „Format apel: % s Adresa_gazdă \ n”,

/ * Obțineți descriptorul clientului. În caz de eșec, vă vom informa despre

imposibilitatea stabilirii conexiunii cu serverul */

dacă ((с1 = clnt_create (server,

LOG_PROG, LOG_VER, „udp”)) == NULL) (

clnt_pcreateerror (server);

/ * Alocați un buffer pentru șir * /

mystring = (char *) malloc (100);

/ * Stabiliți ora evenimentului * /

bintime = timp ((time_t *) NULL);

clnttime = ctime (& bintime);

sprintf (mystring, "% s - Clientul a pornit", clnttime);

/ * Să trimitem un mesaj pentru jurnal - ora la care clientul a început să lucreze. În caz de eșec, vom raporta o eroare * /

if ((rezultat = rlog_l (& mystring, cl)) == NULL) (

fprintf (stderr, „eroare2 \ n”);

clnt_perror (cl, server);

/ * În caz de defecțiune pe computerul de la distanță, vom raporta o eroare * /

dacă (* rezultat! = 0)

fprintf (stderr, „Eroare la scrierea în jurnal \ n”);

/ * 0 eliberează descriptorul * /

cint distruge (cl);

Stub-ul client log_clnt.c este compilat cu modulul client.c pentru a obține executabilul client.

cc -o rlog client.c log_clnt.c -Insl

Stub-ul serverului log_svc.c și rutina log.c sunt compilate pentru a obține executabilul serverului.

cc -o logger log_svc.c log.c -Insl

Acum, pe un server gazdă.nowhere.ru, trebuie să începeți procesul de server:

Apoi, când clientul rlog este pornit pe o altă mașină, serverul va adăuga intrarea corespunzătoare în fișierul jurnal.

Schema de funcționare a RPC în acest caz este prezentată în Fig. 1. Modulele interacționează după cum urmează:

1. Când începe procesul de server, acesta creează un socket UDP și leagă orice port local la acel socket. Serverul apelează apoi funcția de bibliotecă svc_register (3N) pentru a înregistra numere de program și numere de versiune. Pentru a face acest lucru, funcția apelează procesul portmap (IM) și transmite valorile necesare. Serverul portmap (IM) este de obicei pornit în timpul inițializării sistemului și se leagă la un port binecunoscut. Portmap (3N) știe acum numărul portului pentru programul și versiunea noastră. Serverul așteaptă să primească cererea. Rețineți că toate acțiunile descrise sunt efectuate de un server stub creat de compilatorul rpcgen (IM).

2. Când pornește rlog, primul lucru pe care îl face este să apeleze funcția de bibliotecă clnt_create (3N), dându-i adresa sistemului la distanță, numerele de program și versiune și protocolul de transport. Funcția face o solicitare către serverul portmap (IM) al sistemului de la distanță server.nowhere.m și obține numărul portului de la distanță pentru serverul de jurnal.

3. Clientul apelează rutina rlog_1 () definită în stub-ul clientului și transferă controlul către stub. Aceasta, la rândul său, formează cererea (conversia argumentelor în format XDR) sub forma unui pachet UDP și o redirecționează către portul de la distanță primit de la serverul portmap (IM). Apoi așteaptă un răspuns ceva timp și, dacă nu este primit, retrimite cererea. În circumstanțe favorabile, cererea este acceptată de serverul de înregistrare (modul server stub). Stub-ul identifică ce funcție a fost apelată (prin numărul procedurii) și apelează funcția rlog_1 () a modulului log.c. După ce controlul revine înapoi la stub, stub convertește valoarea returnată de funcția rlog_1 () în format XDR și formează răspunsul și sub forma unui pachet UDP. După primirea răspunsului, stub-ul clientului extrage valoarea returnată, o transformă și o returnează gazdei client.


Orice modificare a sistemului de operare Windows, începând cu versiunea XP, include o componentă de serviciu desemnată RPC. Majoritatea utilizatorilor obișnuiți nu știu ce este, în plus, nu știu pentru ce este acest serviciu și cum funcționează. În acest sens, se propune luarea în considerare a unor aspecte de bază legate de componenta în sine, principiile de funcționare a acesteia și domeniul de utilizare fără a descrie termeni tehnici inutile și complexi. Să ne oprim separat asupra posibilelor erori de service și metodelor de eliminare rapidă a acestora.

Proceduri de la distanță (apel de procedură de la distanță): ce este?

Aparent, mulți utilizatori, pe baza numelui acestei componente de serviciu, au concluzionat deja despre ce este vorba. Într-adevăr, procedurile de la distanță (apelarea procedurilor de la distanță) implică unele acțiuni atunci când sunt executate nu pe computerul local, ci pe cel la distanță (cel mai adesea pe server).

Adică, cererea se formează pe un terminal, apoi se transferă la altul, unde este executată, după care un răspuns (raport) asupra execuției este returnat primului computer. Dar aceasta este doar o explicație primitivă. De fapt, totul este mult mai complicat, deoarece aici trebuie să țineți cont de protocoalele de transfer de date (UDP, TCP, HTTP) și de multe alte mecanisme.

Pentru ce este acest serviciu?

În ciuda scopului său principal, RPC RPC poate fi utilizat pe un computer în loc de pe computere diferite. Cel mai simplu exemplu este apelarea unei funcții a unui program dintr-o altă aplicație. Mulți muzicieni care lucrează cu studiouri virtuale și secvențiere sunt conștienți de faptul că fiecare astfel de aplicație are propriul modul de editare sau procesare audio, care nu îndeplinește întotdeauna cerințele utilizatorului. Și orice studio vă permite să conectați în schimb orice alt program extern.

De exemplu, în setările sequencerului FL Studio, puteți specifica o altă aplicație (de exemplu, Adobe Audition), care va fi folosită implicit pentru editarea fișierelor de sunet (eșantioane) în mediul principal al programului. În același timp, conectarea Adobe Audition la FL Studio se va realiza nu prin gazde virtuale precum VST, RTAS sau DX, ci direct prin utilizarea serviciului de apel de procedură la distanță. Este de la sine înțeles că acest exemplu nu este singurul, deoarece domeniul de aplicare al componentei descrise este mult mai larg.

De foarte multe ori acest serviciu este asociat și cu distribuția sarcinii de calcul pe terminale, între care se stabilește o conexiune interactivă. În același timp, dacă sarcina resurselor de calcul a mai multor computere este distribuită uniform, performanța maximă poate fi atinsă numai dacă se fac schimb de cantități mici de date și un răspuns rapid între componente.

Eșecul apelului procedurii de la distanță: care este cauza?

Din păcate, din cauza unei astfel de cereri, apariția defecțiunilor și erorilor asociate cu acest serviciu este destul de comună.

Ca rezultat, devine imposibil nu numai să utilizați componenta în sine. Uneori, nici măcar nu este posibil să accesați unele dintre setările sistemului, iar Windows XP pur și simplu se blochează, după care poate fi destul de problematic să îl restabiliți la o stare normală de funcționare. O altă problemă este instrumentul de reparare online DISM inclus cu sistemul de operare.

Cu încălcări în activitatea sa este asociată apariția erorii 1726, care afectează direct funcționarea componentelor serviciului RPC.

Principalele motive pentru astfel de defecțiuni sunt numite invocarea unui instrument de verificare a sistemului sau a unui instrument de recuperare atunci când procesul DISM este activ sau nu poate ieși grațios (de exemplu, când instrumentele DISM și SFC sunt pornite de la două console de comandă în același timp); când serviciul rulează în paralel cu deservirea componentelor RPC; când serviciul este blocat de software antivirus.

Prin urmare, dacă RPC eșuează pe Windows 7 și versiuni ulterioare, primul lucru de făcut este să părăsiți DISM, să reporniți computerul și să reporniți serviciul. Dacă acest lucru nu ajută, puteți încerca să treceți în modul sigur și să dezactivați complet protecția antivirus pe durata recuperării. Ne vom opri asupra măsurilor suplimentare care ajută la remedierea oricărei defecțiuni în timpul unui apel de procedură la distanță și în orice modificare a Windows. Între timp, să ne uităm la problemele legate de dezactivarea acestei componente de sistem (din păcate, mulți utilizatori care nu cunosc esența problemei încearcă să facă exact astfel de lucruri).

Pot dezactiva serviciul RPC?

Deci, să vedem cât de realist este să dezactivezi RPC-ul. Procedurile de la distanță, bazate pe recomandările dezvoltatorilor, nu trebuie să fie dezactivate sub nicio circumstanță. Este important! În principiu, ea însăși nu va permite acest lucru. Există, desigur, unele soluții care implică utilizarea de software suplimentar, dar din motive evidente, numele unor astfel de aplicații nu sunt date, deoarece dacă sunt utilizate incorect, întregul sistem poate deveni inutilizabil.

Consecințele dezactivării proceselor RPC

Chiar dacă utilizatorul reușește să dezactiveze cumva procedurile de la distanță (apeluri de proceduri la distanță), consecințele, din păcate, pot fi foarte imprevizibile. După cum sa menționat deja, Windows XP poate să nu mai funcționeze, iar într-un sistem de operare cu un rang mai înalt, ca urmare, poate apărea un număr mare de defecțiuni ale sistemului, care nu pot fi eliminate, chiar dacă numai din cauza lipsei de acces la setările critice ale Windows. și parametrii, în plus, chiar și în modul sigur sau la pornirea de pe suporturi amovibile. Cu toate acestea, puteți remedia o blocare atunci când apelați RPC-uri pe Windows 10 sau versiuni anterioare ale sistemului de operare. Metoda nu este cea mai ușoară, așa că trebuie să fii foarte atent când o folosești.

Dezactivați localizatorul de acces la distanță

Deci, serviciul RPC principal nu poate fi dezactivat. Dar poate că are sens să dezactivezi unele dintre componentele sale însoțitoare? Da, într-adevăr, dacă accesați secțiunea de servicii de sistem și componentele acestora (services.msc), puteți găsi așa-numitul locator RPC.

Dar poate fi dezactivat fără teamă de consecințe catastrofale. După ce ați introdus editarea parametrilor săi, trebuie să opriți componenta și să setați tipul de pornire la dezactivat. Programele care pot folosi proceduri de la distanță vor efectua oricum un apel de procedură de la distanță (fără ajutorul acesteia).

Dacă din anumite motive parametrii setați nu funcționează, puteți utiliza discul de instalare Windows, atunci când porniți de pe acesta, apelați linia de comandă și introduceți următoarele:

  • cd X: \ i386 (X este litera unității amovibile);
  • extinde explorer.ex_% TEMP% \ explorer.exe;
  • extinde svchost.ex_% TEMP% \ svchost.exe.

După o repornire, se apelează „Task Manager” și se termină apoi combinația copy% TEMP% \ explorer.exe% SYSTEMROOT% / y este scrisă în linia de comandă, după care absolut toate procesele svchost sunt terminate în „Task Administrator". Acum ar trebui să fiți deosebit de atenți, deoarece după terminarea proceselor în numai șaizeci de secunde în consola de comandă trebuie să aveți timp să scrieți comanda copy% TEMP% \ svchost.exe% systemroot% \ system32 / y.

Dacă un utilizator, de exemplu, în modul normal sau sigur, are acces la registrul de sistem, în editorul (regedit) din ramura HKCC, găsiți parametrul CSConfigFlags și atribuiți-i o valoare zero.

Depanarea accidentului 1726

În cele din urmă, depanarea erorii 1726 se face și prin registry. Dar în acest caz, în ramura HKLM, trebuie să găsiți directorul RpcSs, iar în dreapta, editați valoarea parametrului Start.

Trebuie schimbat de la patru, de obicei setat implicit, la două, apoi reporniți sistemul.

Postfaţă

Asta este tot ce există pentru apelurile RPC. Proceduri de la distanță, principiile de funcționare a acestei componente într-o versiune extinsă pot fi descrise pentru o perioadă foarte lungă de timp, dar accentul în materialul prezentat a fost pus pe o cunoaștere generală a serviciului și unele metode de eliminare a erorilor și defecțiunilor pe care le poate cauza într-un sistem informatic. Utilizatorii obișnuiți vor trebui să aibă răbdare și să fie foarte atenți, deoarece o acțiune greșită în registru poate duce la o prăbușire completă a sistemului de operare.

Vă rugăm să rețineți că aceste tipuri de blocări nu sunt abordate prin alte mijloace, cum ar fi optimizatorii și reglatorii de setări ale sistemului de operare Windows. Cu toată dorința, nu este prevăzută nici linia de comandă, nici, cu atât mai mult, intervenția în registru la nivelul cheilor de editare în astfel de pachete software.

Cursul 4

4.1 Conceptul de apel de procedură la distanță

Ideea din spatele apelării procedurilor de la distanță (Apel de procedură de la distanță - RPC) constă în extinderea mecanismului binecunoscut și bine înțeles de transfer al controlului și datelor în cadrul unui program care rulează pe o singură mașină pentru a transfera controlul și datele într-o rețea. Facilitățile de apel de procedură de la distanță sunt concepute pentru a facilita organizarea calculului distribuit. Cea mai eficientă utilizare a RPC se realizează în acele aplicații în care există o comunicare interactivă între componentele de la distanță cu timpi de răspuns scurti și transfer de date relativ redus. Astfel de aplicații sunt numite RPC-oriented.

Trăsăturile caracteristice ale unui apel la proceduri locale sunt: ​​asimetria, adică una dintre părțile care interacționează este inițiatorul; sincronicitatea, adică executarea procedurii de apelare când se oprește din momentul emiterii cererii și se reia numai după revenirea din procedura apelată.

Implementarea apelurilor de la distanță este semnificativ mai complexă decât implementarea apelurilor de procedură locală. Pentru început, întrucât apelantul și procedura apelată sunt executate pe mașini diferite, au spații de adrese diferite, iar acest lucru creează probleme la transmiterea parametrilor și a rezultatelor, mai ales dacă mașinile nu sunt identice. Deoarece RPC nu poate conta pe memoria partajată, aceasta înseamnă că parametrii RPC nu trebuie să conțină pointeri către locații de memorie non-stiva și că valorile parametrilor trebuie copiate de pe un computer pe altul. Următoarea diferență între RPC și un apel local este că folosește în mod necesar sistemul de comunicații de bază, dar acest lucru nu ar trebui să fie văzut în mod explicit nici în definirea procedurilor, nici în procedurile în sine. Depărtarea introduce probleme suplimentare. Execuția programului apelant și a procedurii locale apelate pe aceeași mașină se face într-un singur proces. Dar există cel puțin două procese implicate în implementarea RPC - unul pe fiecare mașină. Dacă una dintre ele se blochează, pot apărea următoarele situații: dacă procedura de apelare se blochează, procedurile apelate de la distanță vor deveni „orfane”, iar dacă procedurile de la distanță se încheie anormal, apelanții vor deveni „părinți defavorizați” ai apelanților, care vor așteptați în zadar un răspuns de la procedurile de la distanță.

În plus, există o serie de probleme asociate cu eterogenitatea limbajelor de programare și a mediilor de operare: structurile de date și structurile de apel de procedură acceptate în orice limbaj de programare nu sunt acceptate în același mod în toate celelalte limbi.


Acestea și câteva alte probleme sunt rezolvate de tehnologia RPC răspândită care stă la baza multor sisteme de operare distribuite.

Operații RPC de bază

Pentru a înțelege cum funcționează RPC, luați în considerare mai întâi executarea unui apel de procedură locală pe o mașină convențională care rulează autonom. De exemplu, să fie un apel de sistem

count = citit (fd, buf, nbytes);

unde fd este un număr întreg;

buf - matrice de caractere;

nbytes este un număr întreg.

Pentru a efectua apelul, procedura de apelare împinge parametrii pe stivă în ordine inversă. După ce apelul de citire este executat, împinge valoarea returnată într-un registru, mută adresa de retur și readuce controlul la procedura de apelare, care scoate parametrii din stivă, readucendu-l la starea inițială. Rețineți că în C, parametrii pot fi apelați fie prin referință (după nume), fie prin valoare (după valoare). În ceea ce privește procedura apelată, parametrii de valoare sunt variabile locale inițializabile. Procedura apelată le poate modifica fără a afecta valoarea originalelor acestor variabile în procedura de apelare.

Dacă un pointer către o variabilă este transmis unei proceduri apelate, atunci o modificare a valorii acestei variabile de către procedura apelată are ca rezultat o modificare a valorii acestei variabile și pentru procedura apelată. Acest fapt este esențial pentru RPC.

Există și un alt mecanism de transmitere a parametrilor, care nu este folosit în limbajul C. Se numește call-by-copy / restore și constă în necesitatea ca programul apelant să copieze variabilele în stivă ca valori, apoi să le copieze înapoi. după ce apelul se face peste valorile inițiale ale procedurii de apelare.

Este la latitudinea designerilor de limbaj să decidă ce mecanism de trecere a parametrilor să folosească. Uneori depinde de tipul de date transferate. În C, de exemplu, întregul și alte date scalare sunt întotdeauna transmise după valoare, iar tablourile sunt întotdeauna transmise prin referință.

Ideea din spatele RPC este de a face ca un apel de procedură la distanță să arate cât mai aproape posibil de un apel de procedură locală. Cu alte cuvinte, faceți RPC transparent: apelantul nu trebuie să știe că procedura apelată este pe o altă mașină și invers.

RPC realizează transparență în felul următor. Când procedura apelată este într-adevăr la distanță, în loc de procedura locală, o altă versiune a procedurii numită client stub (stub) este plasată în bibliotecă. La fel ca procedura originală, stub-ul este invocat folosind secvența de apelare și apare o întrerupere la accesarea nucleului. Numai că, spre deosebire de procedura inițială, nu pune parametri în registre și nu cere date nucleului, în schimb generează un mesaj de trimis către nucleul mașinii de la distanță.

Etapele execuției RPC

Interacțiunea componentelor software în timpul executării unui apel de procedură la distanță este ilustrată în Figura 2.

Figura 2. Apel de procedură de la distanță

După ce stub-ul clientului a fost apelat de programul client, prima sa sarcină este să umple buffer-ul cu mesajul trimis. Pe unele sisteme, stub-ul clientului are un singur buffer, cu lungime fixă, care este umplut de la început de fiecare dată când sosește o nouă solicitare. Pe alte sisteme, un buffer de mesaje este un grup de buffere pentru câmpuri individuale de mesaje, unele dintre aceste buffere sunt deja pline. Această metodă este potrivită în special atunci când pachetul este formatat cu un număr mare de câmpuri, dar valorile multora dintre aceste câmpuri nu se modifică de la apel la apel.

Parametrii trebuie apoi convertiți în formatul corespunzător și inserați în bufferul de mesaje. În acest moment, mesajul este gata pentru a fi trimis, astfel încât se efectuează o întrerupere a apelului de nucleu.

Când nucleul capătă controlul, schimbă contextele, salvează registrele procesorului și o hartă de memorie (descriptori de pagină) și stabilește o nouă hartă de memorie care va fi folosită pentru a rula în modul kernel. Deoarece nucleul și contextele utilizatorului sunt diferite, nucleul trebuie să copieze mesajul exact în propriul spațiu de adresă, astfel încât să-l poată accesa, să-și amintească adresa de destinație (și eventual alte câmpuri de antet) și trebuie să-l transmită, de asemenea, în rețea. interfata. Acest lucru completează munca din partea clientului. Cronometrul de transmisie este activat, iar nucleul poate fie interoga ciclic pentru un răspuns, fie poate transmite controlul către planificator, care alege un alt proces de executat. În primul caz, execuția interogării este accelerată, dar nu există multiprogramare.

Pe partea de server, biții de intrare sunt plasați de hardware-ul receptor fie într-un buffer încorporat, fie în RAM. Când toate informațiile sunt primite, este generată o întrerupere. Managerul de întrerupere verifică corectitudinea pachetelor de date și determină ce stub trebuie transmis. Dacă niciunul dintre stub-uri nu așteaptă acest pachet, handlerul trebuie fie să-l plaseze într-un buffer, fie să-l arunce cu totul. Dacă există un stub în așteptare, mesajul este copiat în acesta. În final, se efectuează o comutare de context, în urma căreia se restabilesc registrele și harta memoriei, luând valorile pe care le aveau în momentul în care stub-ul a efectuat apelul de recepție.

Acum, ștuțul serverului începe să funcționeze. Dezambalează parametrii și îi împinge în mod corespunzător pe stivă. Când totul este gata, se efectuează un apel pe server. După finalizarea procedurii, serverul trimite rezultatele către client. Pentru aceasta, toți pașii descriși mai sus sunt executați, doar în ordine inversă.

Figura 3 prezintă secvența comenzilor care trebuie executate pentru fiecare apel RPC.

Figura 3. Etapele procedurii RPC

Ideea din spatele apelării procedurilor de la distanță (Apel de procedură de la distanță - RPC) constă în extinderea mecanismului binecunoscut și bine înțeles de transfer al controlului și datelor în cadrul unui program care rulează pe o singură mașină pentru a transfera controlul și datele într-o rețea. Facilitățile de apel de procedură de la distanță sunt concepute pentru a facilita organizarea calculului distribuit.

Cea mai mare eficiență a utilizării RPC se realizează în acele aplicații în care există comunicare interactivă între componentele de la distanță cu timp scurt de răspunsși cantitate relativ mică de date transmise.Astfel de aplicații sunt numite RPC-oriented.

Apelurile de procedură locală se caracterizează prin:

    Asimetrie, adică una dintre părțile care interacționează este inițiatorul;

    sincronicitate, adică executarea procedurii de apelare este suspendată din momentul emiterii cererii și reluată numai după revenirea procedurii apelate.

Implementarea apelurilor de la distanță este semnificativ mai complexă decât implementarea apelurilor de procedură locală.

1. Să începem cu faptul că, deoarece procedurile de apelare și apelare sunt executate pe mașini diferite, acestea au spații de adrese diferite iar asta creează probleme în transferul parametrilor și rezultatelor, mai ales dacă mașinile nu sunt identice.

Deoarece RPC nu poate conta pe memoria partajată, aceasta înseamnă că Parametrii RPC nu trebuie să conțină pointeri către locații de memorie non-stivă si ce valorile parametrilor trebuie copiate de pe un computer pe altul.

2. Următoarea diferență între RPC și un apel local este că acesta folosește în mod necesar sistemul de comunicare subiacent totusi aceasta nu ar trebui să fie clar vizibil nici în definiția procedurilor, nici în procedurile în sine .

Depărtarea introduce probleme suplimentare. Executarea programului de apelare și a procedurii locale apelate pe aceeași mașină implementate în cadrulun singur proces... Dar implicate în implementarea RPCcel puțin două procese - câte unul în fiecare mașină... Dacă una dintre ele se prăbușește, pot apărea următoarele situații:

    dacă procedura de apelare se blochează, procedurile apelate de la distanță devin „orfane” și

    întreruperea anormală a procedurilor de la distanță va face apelanții „părinți dezavantajați” și va aștepta un răspuns de la procedurile de la distanță fără niciun rezultat.

În plus, există o serie de probleme asociate cu eterogenitatea limbajelor de programare și a mediilor de operare : Structurile de date și structurile de apel de procedură acceptate în orice limbaj de programare nu sunt acceptate în același mod în toate celelalte limbaje.

Acestea și câteva alte probleme sunt rezolvate de tehnologia RPC răspândită care stă la baza multor sisteme de operare distribuite.

Ideea din spatele RPC este de a face ca un apel de procedură la distanță să arate cât mai aproape posibil de un apel de procedură locală. Cu alte cuvinte, faceți RPC transparent: apelantul nu trebuie să știe că procedura apelată este pe o altă mașină și invers.

RPC realizează transparență în felul următor. Când procedura apelată este într-adevăr la distanță, în loc de procedura locală, o altă versiune a procedurii numită client stub (stub) este plasată în bibliotecă. La fel ca procedura originală, stub-ul este apelat folosind secvența de apelare (ca în Figura 3.1), iar nucleul este întrerupt. Numai că, spre deosebire de procedura inițială, nu pune parametri în registre și nu cere date nucleului, în schimb generează un mesaj de trimis către nucleul mașinii de la distanță.

Orez... 3.2. Apelul procedurii de la distanță

Remote Procedure Call (RPC) Conceptul Remote Procedure Call

Ideea din spatele Remote Procedure Call (RPC) este de a extinde mecanismul bine-cunoscut și bine înțeles pentru transferul de control și date într-un program care rulează pe o singură mașină pentru a transfera controlul și datele printr-o rețea. Facilitățile de apel de procedură de la distanță sunt concepute pentru a facilita organizarea calculului distribuit. Cea mai eficientă utilizare a RPC se realizează în acele aplicații în care există o comunicare interactivă între componentele de la distanță cu timpi de răspuns scurti și transfer de date relativ redus. Astfel de aplicații sunt numite RPC-oriented.

Apelurile de procedură locală se caracterizează prin:

Asimetria, adică una dintre părțile care interacționează este inițiatorul; Sincronitatea, adică executarea procedurii de apelare când aceasta se oprește din momentul emiterii cererii și se reia numai după revenirea din procedura apelată.

Implementarea apelurilor de la distanță este semnificativ mai complexă decât implementarea apelurilor de procedură locală. Pentru început, întrucât apelantul și procedura apelată sunt executate pe mașini diferite, au spații de adrese diferite, iar acest lucru creează probleme la transmiterea parametrilor și a rezultatelor, mai ales dacă mașinile nu sunt identice. Deoarece RPC nu poate conta pe memoria partajată, aceasta înseamnă că parametrii RPC nu trebuie să conțină pointeri către locații de memorie non-stiva și că valorile parametrilor trebuie copiate de pe un computer pe altul. Următoarea diferență între RPC și un apel local este că folosește în mod necesar sistemul de comunicații de bază, dar acest lucru nu ar trebui să fie văzut în mod explicit nici în definirea procedurilor, nici în procedurile în sine. Depărtarea introduce probleme suplimentare. Execuția programului apelant și a procedurii locale apelate pe aceeași mașină se face într-un singur proces. Dar există cel puțin două procese implicate în implementarea RPC - unul pe fiecare mașină. Dacă una dintre ele se blochează, pot apărea următoarele situații: dacă procedura de apelare se blochează, procedurile apelate de la distanță vor deveni „orfane”, iar dacă procedurile de la distanță se încheie anormal, apelanții vor deveni „părinți defavorizați” ai apelanților, care vor așteptați în zadar un răspuns de la procedurile de la distanță.

În plus, există o serie de probleme asociate cu eterogenitatea limbajelor de programare și a mediilor de operare: structurile de date și structurile de apel de procedură acceptate în orice limbaj de programare nu sunt acceptate în același mod în toate celelalte limbi.

Acestea și câteva alte probleme sunt rezolvate de tehnologia RPC răspândită care stă la baza multor sisteme de operare distribuite.

Operații RPC de bază

Pentru a înțelege cum funcționează RPC, luați în considerare mai întâi executarea unui apel de procedură locală pe o mașină convențională care rulează autonom. De exemplu, să fie un apel de sistem

Count = citit (fd, buf, nbytes);

unde fd este un număr întreg,
buf este o matrice de caractere,
nbytes este un număr întreg.

Pentru a efectua apelul, procedura de apelare împinge parametrii pe stivă în ordine inversă (Figura 3.1). După ce apelul de citire este executat, împinge valoarea returnată într-un registru, mută adresa de retur și readuce controlul la procedura de apelare, care scoate parametrii din stivă, readucendu-l la starea inițială. Rețineți că în C, parametrii pot fi apelați fie prin referință (după nume), fie prin valoare (după valoare). În ceea ce privește procedura apelată, parametrii de valoare sunt variabile locale inițializabile. Procedura apelată le poate modifica fără a afecta valoarea originalelor acestor variabile în procedura de apelare.

Dacă un pointer către o variabilă este transmis unei proceduri apelate, atunci o modificare a valorii acestei variabile de către procedura apelată are ca rezultat o modificare a valorii acestei variabile și pentru procedura apelată. Acest fapt este esențial pentru RPC.

Există și un alt mecanism de transmitere a parametrilor, care nu este folosit în limbajul C. Se numește call-by-copy / restore și constă în necesitatea ca programul apelant să copieze variabilele în stivă ca valori, apoi să le copieze înapoi. după ce apelul se face peste valorile inițiale ale procedurii de apelare.

Este la latitudinea designerilor de limbaj să decidă ce mecanism de trecere a parametrilor să folosească. Uneori depinde de tipul de date transferate. În C, de exemplu, întregul și alte date scalare sunt întotdeauna transmise după valoare, iar tablourile sunt întotdeauna transmise prin referință.

Orez. 3.1. a) Stiva de dinaintea apelului de citire este efectuată;
b) Stiva în timpul executării procedurii;
c) Stiva după revenirea la programul de apelare

Ideea din spatele RPC este de a face ca un apel de procedură la distanță să arate cât mai aproape posibil de un apel de procedură locală. Cu alte cuvinte, faceți RPC transparent: apelantul nu trebuie să știe că procedura apelată este pe o altă mașină și invers.

RPC realizează transparență în felul următor. Când procedura apelată este într-adevăr la distanță, în loc de procedura locală, o altă versiune a procedurii numită client stub (stub) este plasată în bibliotecă. La fel ca procedura originală, stub-ul este apelat folosind secvența de apelare (ca în Figura 3.1), iar nucleul este întrerupt. Numai că, spre deosebire de procedura inițială, nu pune parametri în registre și nu cere date nucleului, în schimb generează un mesaj de trimis către nucleul mașinii de la distanță.

Etapele execuției RPC

Interacțiunea componentelor software în timpul executării unui apel de procedură la distanță este ilustrată în Figura 3.2. După ce stub-ul clientului a fost apelat de programul client, prima sa sarcină este să umple buffer-ul cu mesajul trimis. Pe unele sisteme, stub-ul clientului are un singur buffer, cu lungime fixă, care este umplut de la început de fiecare dată când sosește o nouă solicitare. Pe alte sisteme, un buffer de mesaje este un grup de buffere pentru câmpuri individuale de mesaje, dintre care unele sunt deja pline. Această metodă este potrivită în special atunci când pachetul este formatat cu un număr mare de câmpuri, dar valorile multora dintre aceste câmpuri nu se modifică de la apel la apel.

Parametrii trebuie apoi convertiți în formatul corespunzător și inserați în bufferul de mesaje. În acest moment, mesajul este gata pentru a fi trimis, astfel încât se efectuează o întrerupere la apelul la nucleu.

Orez. 3.2. Apelul procedurii de la distanță

Când nucleul capătă controlul, schimbă contextele, salvează registrele procesorului și o hartă de memorie (descriptori de pagină) și stabilește o nouă hartă de memorie care va fi folosită pentru a rula în modul kernel. Deoarece nucleul și contextele utilizatorului sunt diferite, nucleul trebuie să copieze mesajul exact în propriul spațiu de adresă, astfel încât să-l poată accesa, să-și amintească adresa de destinație (și eventual alte câmpuri de antet) și trebuie să-l transmită, de asemenea, în rețea. interfata. Acest lucru completează munca din partea clientului. Cronometrul de transmisie este activat, iar nucleul poate fie interoga ciclic pentru un răspuns, fie poate transmite controlul către planificator, care alege un alt proces de executat. În primul caz, execuția interogării este accelerată, dar nu există multiprogramare.

Pe partea de server, biții de intrare sunt plasați de hardware-ul receptor fie într-un buffer încorporat, fie în RAM. Când toate informațiile sunt primite, este generată o întrerupere. Managerul de întrerupere verifică corectitudinea pachetelor de date și determină ce stub trebuie să i se transmită. Dacă niciunul dintre stub-uri nu așteaptă acest pachet, handler-ul trebuie fie să-l tamponeze, fie să-l arunce cu totul. Dacă există un stub în așteptare, mesajul este copiat în acesta. În final, se efectuează o comutare de context, în urma căreia se restabilesc registrele și harta memoriei, luând valorile pe care le aveau în momentul în care stub-ul a efectuat apelul de recepție.

Acum, ștuțul serverului începe să funcționeze. Dezambalează parametrii și îi împinge în mod corespunzător pe stivă. Când totul este gata, se efectuează un apel pe server. După finalizarea procedurii, serverul trimite rezultatele către client. Pentru aceasta, toți pașii descriși mai sus sunt executați, doar în ordine inversă.

Figura 3.3 arată secvența comenzilor care trebuie executate pentru fiecare apel RPC, iar Figura 3.4 arată cât de mult din timpul total de execuție RPC este cheltuit executând fiecare dintre cei 14 pași descriși. Cercetarea a fost efectuată pe o stație de lucru multiprocesor DEC Firefly și, deși prezența a cinci procesoare a afectat în mod necesar rezultatele măsurătorilor, histograma prezentată în figură oferă o imagine de ansamblu asupra procesului de execuție RPC.

Orez. 3.3. Etapele procedurii RPC

Orez. 3.4. Alocarea timpului între 14 etape de execuție RPC

1. Call stub

2. Pregătiți tamponul

3. Parametrii pachetului

4. Completați câmpul titlu

5. Calculați suma de control din mesaj

6. Întreruperea la miez

7. Coada de pachete pentru execuție

8. Trimiterea unui mesaj către controler prin QBUS

9. Timp de transmisie Ethernet

10. Obțineți un pachet de la controler

11. Procedura de manipulare a întreruperii

12. Calcularea sumei de control

13. Schimbarea contextului în spațiul utilizatorului

14. Executarea unui server stub

Legătura dinamică

Luați în considerare întrebarea cum specifică clientul locația serverului. O metodă pentru a rezolva această problemă este utilizarea directă a adresei de rețea a serverului în programul client. Dezavantajul acestei abordări este inflexibilitatea sa extremă: atunci când serverul este mutat, sau când numărul de servere este crescut, sau când interfața este schimbată, în toate acestea și în multe alte cazuri, este necesar să se recompileze toate programele care au folosit un atribuirea grea a adresei serverului. Pentru a evita toate aceste probleme, unele sisteme distribuite folosesc ceea ce se numește legătură dinamică.

Punctul de plecare pentru legarea dinamică este definiția formală (specificația) a serverului. Specificația conține numele serverului de fișiere, numărul versiunii și o listă de proceduri de service furnizate de acest server pentru clienți (Figura 3.5). Pentru fiecare procedură, este dată o descriere a parametrilor săi, indicând dacă acest parametru este de intrare sau de ieșire în raport cu serverul. Unii parametri pot fi atât de intrare, cât și de ieșire - de exemplu, o matrice care este trimisă de client către server, modificată acolo și apoi returnată înapoi clientului (operație de copiere/restaurare).

Orez. 3.5. Specificația serverului RPC

Specificația formală a serverului este utilizată ca intrare într-un program generator de stub care creează atât client, cât și server. Apoi sunt plasate în bibliotecile corespunzătoare. Când programul utilizator (client) apelează orice procedură definită în specificația serverului, procedura stub corespunzătoare este asociată cu binarul programului. De asemenea, atunci când un server este compilat, cioturile de server sunt asociate cu acesta.

Când serverul pornește, primul lucru pe care îl face este să-și transmită interfața serverului unui program special numit binder.Acest proces, cunoscut sub numele de proces de înregistrare a serverului, implică ca serverul să-și trimită numele, numărul versiunii, identificatorul unic și descriptorul locația serverului. Descriptorul este independent de sistem și poate fi o adresă IP, Ethernet, X.500 sau o altă adresă și poate conține și alte informații, cum ar fi autentificarea.

Când clientul apelează una dintre procedurile de la distanță pentru prima dată, de exemplu, citire, stub-ul clientului vede că nu este încă conectat la server și trimite un mesaj programului de legare prin care îi cere să importe interfața versiunii corecte. a serverului necesar. Dacă un astfel de server există, atunci Binder transmite un handle și un identificator unic stub-ului clientului.

Stub-ul clientului folosește un descriptor ca adresă atunci când trimite un mesaj de solicitare. Mesajul conține parametri și un identificator unic pe care nucleul serverului îl folosește pentru a redirecționa mesajul primit către serverul corect, dacă există mai mulți dintre aceștia pe această mașină.

Această metodă de import/export de interfețe este foarte flexibilă. De exemplu, pot exista mai multe servere care acceptă aceeași interfață, iar clienții sunt distribuiți aleatoriu între servere. Ca parte a acestei metode, devine posibilă interogarea periodică a serverelor, analizarea performanței acestora și, în caz de defecțiune, oprirea automată, ceea ce crește toleranța generală la erori a sistemului. Această metodă poate suporta și autentificarea clientului. De exemplu, un server poate determina că poate fi utilizat numai de clienți dintr-o anumită listă.

Cu toate acestea, legătura dinamică are dezavantaje, cum ar fi supraîncărcarea suplimentară (timp) de export și import de interfețe. Amploarea acestor costuri poate fi semnificativă, deoarece multe procese client există pentru o perioadă scurtă de timp, iar la fiecare început al procesului, procedura de import a interfeței trebuie efectuată din nou. În plus, în sistemele mari distribuite, programul de liant poate deveni un blocaj, iar crearea mai multor programe cu un scop similar crește, de asemenea, suprasarcina de creare și sincronizare a proceselor.

Semantica RPC în caz de eșecuri

În mod ideal, RPC ar trebui să funcționeze corect în cazul unei defecțiuni. Luați în considerare următoarele clase de eșecuri:

Clientul nu poate determina locația serverului, de exemplu, dacă serverul necesar eșuează sau pentru că programul client a fost compilat cu mult timp în urmă și a folosit o versiune veche a interfeței serverului. În acest caz, un mesaj care conține un cod de eroare este primit ca răspuns la solicitarea clientului. Solicitare client-server pierdută. Cea mai simplă soluție este să repeți cererea după un anumit timp. Mesaj de răspuns pierdut de la server la client. Această opțiune este mai complicată decât cea anterioară, deoarece unele proceduri nu sunt idempotente. O procedură idempotentă este o procedură a cărei cerere de execuție poate fi repetată de mai multe ori fără a modifica rezultatul. Un exemplu de astfel de procedură este citirea unui fișier. Însă procedura de retragere a unei anumite sume dintr-un cont bancar nu este idempotentă, iar dacă răspunsul se pierde, o solicitare repetată poate schimba semnificativ starea contului clientului. Una dintre soluțiile posibile este aducerea tuturor procedurilor la formă idempotentă. Cu toate acestea, în practică, acest lucru nu este întotdeauna posibil, așa că poate fi utilizată o altă metodă - numerotarea secvențială a tuturor solicitărilor de către nucleul clientului. Nucleul serverului își amintește numărul celei mai recente solicitări de la fiecare dintre clienți și, la primirea fiecărei cereri, analizează dacă această solicitare este primară sau repetată. Serverul s-a prăbușit după ce a primit cererea. Proprietatea idempotității este de asemenea importantă aici, dar, din păcate, abordarea de numerotare a interogărilor nu poate fi aplicată. În acest caz, contează când a avut loc defecțiunea - înainte sau după operație. Dar nucleul clientului nu poate recunoaște aceste situații, știe doar că răspunsul a expirat. Există trei abordări ale acestei probleme: Așteptați până când serverul repornește și încercați din nou. Această abordare asigură că RPC a fost finalizat cel puțin o dată și, eventual, mai multe. Raportați imediat eroarea la aplicație. Această abordare asigură că RPC a fost executat cel mult o dată. A treia abordare nu garantează nimic. Când serverul eșuează, nu există suport pentru client. RPC poate sau nu poate fi executat deloc sau de multe ori. În orice caz, această metodă este foarte ușor de implementat.

Niciuna dintre aceste abordări nu este foarte atractivă. Iar varianta ideala care ar garanta exact o executie RPC, in cazul general, nu poate fi implementata din motive de principiu. Să presupunem, de exemplu, că o operație de la distanță imprimă un text, care include încărcarea tamponului imprimantei și setarea unui bit într-un registru de control al imprimantei, în urma căruia imprimanta pornește. O prăbușire a serverului poate avea loc fie cu o microsecundă înainte, fie cu o microsecundă după setarea bitului de control. Momentul eșecului determină în întregime procedura de recuperare, dar clientul nu poate ști despre momentul eșecului. Pe scurt, posibilitatea unui accident de server schimbă radical natura RPC și reflectă în mod clar diferența dintre un sistem centralizat și un sistem distribuit. În primul caz, o blocare a serverului duce la o blocare a clientului, iar recuperarea este imposibilă. În al doilea caz, este posibil și necesar să se efectueze acțiunile de restabilire a sistemului.

Clientul s-a blocat după trimiterea cererii. În acest caz, calculele sunt efectuate pe rezultatele la care nimeni nu se așteaptă. Astfel de calcule se numesc „orfani”. Prezența orfanilor poate cauza diverse probleme: supraîncărcarea timpului CPU, blocarea resurselor, înlocuirea răspunsului la cererea curentă cu răspunsul la o solicitare care a fost emisă de mașina client chiar înainte ca sistemul să fie repornit.

Ce să faci cu orfanii? Să luăm în considerare 4 soluții posibile.

Distrugere. Înainte ca stub-ul clientului să trimită un mesaj RPC, acesta face o notă în jurnal care anunță ce va face acum. Jurnalul este stocat pe disc sau pe altă memorie tolerantă la erori. După accident, sistemul repornește, jurnalul este analizat și orfanii sunt eliminați. Dezavantajele acestei abordări includ, în primul rând, costurile crescute asociate cu scrierea despre fiecare RPC pe disc și, în al doilea rând, posibila ineficiență din cauza apariției orfanilor de a doua generație generați de apelurile RPC emise de orfanii de prima generație. Reîncarnare. În acest caz, toate problemele sunt rezolvate fără a utiliza scrierea pe disc. Metoda constă în împărțirea timpului în perioade numerotate secvenţial. Când clientul repornește, difuzează către toate mașinile pentru a începe o nouă perioadă. La primirea acestui mesaj, toate calculele de la distanță sunt terminate. Desigur, dacă rețeaua este segmentată, atunci unii orfani pot supraviețui. Reîncarnarea soft este similară cu cazul precedent, cu excepția faptului că nu toate calculele la distanță sunt găsite și distruse, ci doar cele ale clientului care se repornește. Data expirării. Fiecărei cereri i se atribuie un interval de timp standard T, în care trebuie îndeplinită. Dacă cererea nu este finalizată în timpul alocat, atunci se alocă un cuantum suplimentar. Deși acest lucru necesită muncă suplimentară, dacă, după o blocare a clientului, serverul așteaptă un interval T înainte de repornirea clientului, atunci toți orfanii sunt în mod necesar distruși.

În practică, niciuna dintre aceste abordări nu este de dorit; în plus, distrugerea orfanilor poate agrava situația. De exemplu, să presupunem că un orfan a blocat unul sau mai multe fișiere de bază de date. Dacă orfanul este distrus brusc, atunci aceste încuietori vor rămâne, în plus, orfanii distruși pot rămâne în picioare în diverse cozi de sistem, în viitor pot provoca execuția de noi procese etc.