Appunti per Scuola e Università
humanisticheUmanistiche
Appunti e tesine di tutte le materie per gli studenti delle scuole medie riguardanti le materie umanistiche: dall'italiano alla storia riguardanti le materie umanistiche: dall'italiano alla storia 
sceintificheScientifiche
Appunti, analisi, compresione per le scuole medie suddivisi per materie scientifiche, per ognuna troverai appunti, dispense, esercitazioni, tesi e riassunti in download.
tecnicheTecniche
Gli appunti, le tesine e riassunti di tecnica amministrativa, ingegneria tecnico, costruzione. Tutti gli appunti di AppuntiMania.com gratis!
Appunti
informatica
CComputerDatabaseInternetJava
Linux unixReti


AppuntiMania.com » Informatica » Appunti di c » Vettori di interrupt o puntatori?

Vettori di interrupt o puntatori?




Visite: 1452Gradito:apreciate stela [ Medio appunti ]
Leggi anche appunti:

I puntatori


I puntatori Una variabile è un'area di memoria alla quale è associato un nome

Due file sono il medesimo file?


Due file sono il medesimo file? La domanda è formulata in modo fuorviante. Il

Le costanti


Le costanti Le costanti, in senso lato, sono dati che il programma non può modificare.
immagine di categoria

Scarica gratis Vettori di interrupt o puntatori?

Vettori di interrupt o puntatori

Degli interrupt e dei loro vettori si parla diffusamente a pag.  e seguenti. Qui l'attenzione si sposta sl fatto che normalmente non tutti i 256 vettori della tavola sono utilizzati: molti non vengono inizializzati al bootstrap e, comunque, vi è un certo numero di interrupt riservati alle applicazioni (ad esempio il gruppo F0h‑FDh). Da ciò deriva che è perfettamente lecito, per qualsiasi programma, installare proprie routine di interrupt che non siano necessariamente gestori di altre già esistenti ed attive. Ma vi è un'altra implicazione, che rende possibili sviluppi interessanti: la possibilità di utilizzare i vettori di interrupt come puntatori immediatamente conoscibili da tutto il sistema (anche da applicazioni diverse da quella che li inizializza).

Si supponga, ad esempio, che un programma abbia la necessità di condividere con uno o più child process (applicazioni da esso stesso lanciate) una quantità di variabili tale da rendere pesante il loro passaggio attraverso la spawnl() o spawnv() (vedere pag. 135): potrebbe rivelarsi conveniente allocare un'area di memoria di dimensioni sufficienti e scriverne l'indirizzo nella tavola dei vettori perché essa sia accessibile a tutte le applicazioni attive nel sistema.

.

void far *common_data;

.

common_data = farmalloc(10000);

setvect(0xF1,(void(interrupt *)())common_data);

.

Il frammento di codice riportato alloca 10000 byte al puntatore common_data e scrive nella tavola dei vettori l'indirizzo restituito da farmalloc() , come vettore F1h: qualunque child process può accedere al buffer common_data leggendone l'indirizzo nella tavola dei vettori. Il putatore common_data è definito puntatore a dati di tipo void per evidenziare che il buffer può contenere qualsivoglia tipo di dati: è sufficiente referenziarlo con i casts di volta in volta opportuni. Inoltre common_data è definito far, in quanto puntatore a 32 bit (l'ipotesi è di compilare per un modello di memoria 'piccolo'; vedere pag.  ): nei modelli di memoria compact, large e huge esso lo è per default.

I successivi esempi di questo paragrafo presumono, per semplicità, l'uso in compilazione di un modello di memoria 'grande' (compact, large, huge).

La scelta del vettore da utilizzare è problematica: un programma non ha infatti modo di scoprire con assoluta sicurezza se un vettore sia utilizzato da altre applicazioni oppure sia, al contrario, libero . Per evitare di sottrarre ad un programma uno degli interrupt da esso gestiti si può adottare un accorgimento prudenziale, consistente nell'inserire in testa al buffer un'istruzione di salto all'indirizzo originale dell'interrupt.

.

void *common_data;

char *aux_ptr;

.

aux_ptr = (char *)malloc(10000+sizeof(char)+sizeof(void far *));

*aux_ptr = 0xEA;

(void(interrupt *)())*(long *)(aux_ptr+1) = getvect(0xF1);

common_data = (void *)(aux_ptr+sizeof(char)+sizeof(void far *));

setvect(0xF1,(void(interrupt *)())aux_ptr);

.

Il puntatore aux_ptr è definito per comodità: tutte le operazioni illustrate potrebbero essere effettuate tramite il solo common_data, con cast più complessi; inoltre aux_ptr è dichiarato char per sfruttare con incrementi unitari l'aritmetica dei puntatori. La malloc() alloca un buffer la cui ampiezza, rispetto all'esempio precedente, è incrementata di tanti byte quanti sono sufficienti a contenere gli opcodes dell'istruzione di salto . Nel primo byte del buffer è memorizzato il valore EAh, opcode dell'istruzione JMP FAR; nei successivi quattro il vettore originale dell'int F1h: infatti, dal momento che aux_ptr è un puntatore a char, l'espressione aux_ptr+1 punta al secondo byte del buffer, e rappresenta, in particolare, un puntatore a un dato a 32 bit (risultato ottenuto mediante il cast a puntatore a long), la cui indirezione (il dato a 32 bit stesso), forzata a puntatore ad interrupt, è valorizzata con il valore restituito dalla getvect() . Il puntatore common_data è poi inizializzato in modo tale da 'scavalcare' l'istruzione di salto. Prima di restituire il controllo al sistema, il programma ripristina il vettore originale con l'istruzione:

setvect(0xf1,(void(interrupt *)())*(long *)(aux_ptr+1));

Operazioni di cast analoghe sono descritte ed utilizzate a pagina  ; va ancora sottolineato che il child process che acquisisce, ad esempio mediante getvect(), l'indirizzo dell'interrupt prescelto deve incrementarlo di un numero di byte pari a sizeof(char)+sizeof(void far *) per ottenere il reale indirizzo dei dati, corrispondente a common_data

.

void *common_data;

.

common_data = (void *)(((char *)getvect(0xf1))+sizeof(char)+sizeof(void *));

.

Il cast di getvect() a puntatore a character ha lo scopo di forzare incrementi unitari del puntatore sommandovi la dimensione dell'istruzione JMP FAR completa di indirizzo.

Ogni buffer allocato da malloc() è automaticamente rilasciato quando il programma che ha invocato la malloc() termina. Se i dati in esso contenuti devono essere condivisi da applicazioni attive dopo il termine dell'esecuzione del programma, occorre che la memoria necessaria sia loro riservata con altri metodi.

E' valida, allo scopo, la tecnica delle funzioni jolly, utilizzabile dai TSR per lasciare residenti in memoria dati e routine , discussa a pag.  e alla quale si rimanda, precisando però che allocando nel code segment lo spazio per i dati si determina un incremento delle dimensioni del file eseguibile pari al numero di byte riservati.

In alternativa, è possibile creare il buffer con la allocmem() , che utilizza il servizio 48h dell'int 21h (vedere pag.  ): in questo caso si rendono necessarie due precauzioni.

La prima consiste nel forzare il DOS ad allocare la memoria in modo tale da evitare un'eccessiva frammentazione della RAM libera: allo scopo si può invocare la allocmem() dopo avere impostato la strategia di allocazione LastFit (vedere pagina  ); il buffer occupa la porzione 'alta' della memoria convenzionale.

.

unsigned blockseg; /* conterra' l'indirizzo di segmento del buffer */

int strategy; /* usata per salvare la strategia di allocazione */

.

_AX = 0x5800;

asm int 21h; /* individua strategia attuale di allocazione */

strategy = _AX;

_AX = 0x5801;

_BX = 2;

asm int 21h; /* imposta strategia LastFit */

allocmem(1000,&blockseg); /* alloca 1000 paragr. (circa 16000 bytes) */

_AX = 0x5801;

_BX = strategy;

asm int 21h; /* ripristina la strategia di allocazione */

setvect(0xF1,(void(interrupt *)())MK_FP(blockseg,0));

.

La macro MK_FP() (pag.  ) è utilizzata per costruire l'indirizzo far completo del buffer (blockseg ne costituisce la parte segmento; l'offset è zero).

La seconda precauzione, ancora più importante , sta nell'impedire al DOS di rilasciare, all'uscita dal programma, il buffer allocato con allocmem(): infatti tutti i blocchi assegnati ad un programma non TSR vengono liberati (dal DOS) quando esso termina; in altre parole il DOS rilascia tutte le aree di RAM il cui Memory Control Block reca nel campo PSP (vedere pag.  ) l'indirizzo di segmento del Program Segment Prefix di quel programma. Per evitare tale spiacevole inconveniente è sufficiente modificare il contenuto del campo PSP del MCB dell'area di RAM allocata al buffer:

.

unsigned blockseg; /* conterra' l'indirizzo di segmento del buffer */

.

allocmem(1000,&blockseg); /* alloca 1000 paragr. (circa 16000 bytes) */

*(unsigned far *)MK_FP(blockseg-1,1) = 0xFFFF;

.

Nell'esempio viene assegnato il valore FFFFh al campo PSP del MCB: si tratta di un valore del tutto arbitrario, che può, tra l'altro, essere utilizzato dalle applicazioni interessate, per individuare l'origine dell'area di RAM.

Le considerazioni sin qui espresse relativamente all'utilizzo dei vettori di interrupt come puntatori a dati mantengono la loro validità anche qualora si intenda servirsi dei medesimi come puntatori a funzioni. In effetti, ogni vettore è, per definizione, un puntatore a funzione, in quanto esprime l'indirizzo di una routine eseguibile, ma, dal momento che gli interrupt non sono vere e proprie funzioni C , può essere interessante approfondire appena l'argomento.

Un programma ha la possibilità di mettere a disposizione dell'intero sistema parte del proprio codice. Si tratta, in pratica, di un programma TSR, il quale non installa gestori di interrupt, ma normali funzioni C, che potranno essere eseguite da tutte le applicazioni di volta in volta attive nel sistema, a patto che ne conoscano il prototipo . La chiamata avviene mediante indirezione del puntatore alla funzione , che in questo caso è rappresentato da un vettore di interrupt. Vedere pag. 

Per quanto riguarda l'installazione in RAM delle routine si rimanda a quanto discusso circa i programmi TSR (pag.  ). Si ricordi inoltre che i vettori degli interrupt 00h, 04h, 05h e 06h sono ripristinati dalla keep(), mentre il DOS, da parte sua, ripristina i vettori degli interrupt 23h e 24h prelevandone i valori originali dal PSP del programma.

Circa le funzioni C installabili va invece sottolineato, innanzitutto, che esse devono necessariamente essere dichiarate far, poiché i vettori di interrupt sono indirizzi a 32 bit. Inoltre esse non possono essere invocate con l'istruzione INT , ma solo con l'indirezione del vettore (o meglio, del puntatore con esso valorizzato): ciò evita il ricorso a funzioni e strutture di interfaccia o allo inline assembly, e consente di utilizzare le convenzioni di alto livello di chiamata delle funzioni C (passaggio di copie dei parametri attraverso lo stack, restituzione di un valore, etc.). Attenzione, però: il puntatore utilizzato per referenziare la funzione non deve essere un puntatore a funzione interrupt, dal momento che, come si è detto, le rotuine residenti non sono esse stesse funzioni interrupt, bensì normali funzioni far; se si ottiene il vettore (indirizzo della funzione) mediante getvect(), il valore da questa restituito deve subire un cast:

.

int (far *resFuncPtr)(char *str);

.

resFuncPtr = (int(far *)(char *str))getvect(0x1F);

.

Per completezza, si deve poi osservare che inserire una JMP FAR in testa alla funzione a scopo di sicurezza è più problematico di quanto non lo sia l'inserimento in testa ad un buffer . Infine, l'utilizzo di funzioni di libreria all'interno delle funzioni installabili deve uniformarsi a quanto esposto a pag.  con riferimento ai programmi TSR.




 Forse gli unici casi in cui esiste un buon grado di sicurezza sono quelli in cui il vettore punta ad un indirizzo nel quale è davvero improbabile (se non impossibile) che si trovi codice eseguibile (ad esempio un vettore pari a  è sicuramente inutilizzato).

 In tutto 5: uno per l'opcode dell'istruzione JMP FAR e quattro per il vettore a 32 bit.

 Il cast a puntatore a interrupt evita che il compilatore, assegnando tale tipo di dato a una locazione che dovrebbe contenere un long, segnali un conflitto tra i tipi di dato.

 Se la funzione contenente i dati è definita, nel sorgente, prima di ogni altra, il programma può terminare come un TSR e lasciare residente in memoria quella soltanto (oltre, naturalmente, allo startup code). Dal momento che la funzione è in realtà un'area di 'parcheggio' per dati, il programma non è un vero TSR, in quanto nessuna sua parte rimane attiva in memoria. Si tratta, ancora una volta, di un trucco

 Direi, anzi, assolutamente fondamentale.

 Anche i gestori scritti in linguaggio C devono comunque mantenere una rigorosa coerenza con le particolari regole di interfacciamento con il sistema seguite dagli interrupt (vedere, al proposito, pag. 

 Se non lo conoscessero, come potrebbero passare i parametri eventualmente richiesti ed interpretare correttamente il valore restituito?

 Si può usare l'istruzione INT solo se si tratta di gestori di interrupt.

 Il compilatore genera in testa alla funzione, in modo automatico e trasparente al programmatore, le istruzioni assembler per la gestione dello stack (le solite PUSH BP e MOV BP,SP seguite, se nella funzione sono definite variabili locali, dall'istruzione per il decremento di SP). Un'istruzione JMP FAR seguita dai 4 byte di indirizzo si collocherebbe inevitabilmente dopo dette istruzioni: sarebbe il disastro. Vi è una sola scappatoia semplice, peraltro onerosa e restrittiva dal punto di vista logico: dichiarare tutte le funzioni installabili prive di parametri e di variabili locali, e compilare senza standard stack frame. Chi volesse invece eccedere negli stratagemmi potrebbe riservare i 5 byte in testa alla funzione e poi inizializzare, con una procedura runtime, i primi byte della funzione con gli opcode necessari alla FAR JMP e alla gestione dello stack nell'ordine necessario, scrivendoli all'indirizzo puntato dal vettore, come se si avesse a che fare con un buffer piuttosto che con una funzione. Come nel caso del buffer, deve poi incrementare il puntatore alla funzione (non il vettore, ma il puntatore con esso inizializzato) di 5 perché esso punti effettivamente all'inizio del codice eseguibile. Occorre però una buona conoscenza delle modalità di gestione dello stack e può tornare utile qualche occhiatina al sorgente assembler prodotto dal compilatore (opzione ‑S). Forse, dopo tutto, è preferibile correre qualche rischio: se si lavora con attenzione, la probabilità che il vettore prescelto sia gia utilizzato è, in concreto, molto piccola.

Scarica gratis Vettori di interrupt o puntatori?
Appunti su:



Scarica 100% gratis e , tesine, riassunti



Registrati ora

Password dimenticata?
  • Appunti superiori
  • In questa sezione troverai sunti esame, dispense, appunti universitari, esercitazioni e tesi, suddivisi per le principali facoltà.
  • Università
  • Appunti, dispense, esercitazioni, riassunti direttamente dalla tua aula Universitaria
  • all'Informatica
  • Introduzione all'Informatica, Information and Comunication Tecnology, componenti del computer, software, hardware ...