2.2.1 Il modello di denominazione
È utile avere un modello di come i nomi sono associati a oggetti specifici. Un progettista di sistema crea uno schema di denominazione, che consiste di tre elementi. Il primo elemento è uno spazio dei nomi, che comprende un alfabeto di simboli insieme a regole di sintassi che specificano quali nomi sono accettabili. Il secondo elemento è un algoritmo di mappatura dei nomi, che associa alcuni (non necessariamente tutti) nomi dello spazio dei nomi con alcuni (di nuovo, non necessariamente tutti) valori in un universo di valori, che è il terzo e ultimo elemento dello schema di denominazione. Un valore può essere un oggetto, o può essere un altro nome dallo spazio dei nomi originale o da uno spazio dei nomi diverso. Una mappatura nome-valore è un esempio di legame, e quando esiste una tale mappatura, si dice che il nome è legato al valore. La figura 2.10 illustra.
Nella maggior parte dei sistemi, tipicamente diversi schemi di denominazione sono in funzione contemporaneamente. Per esempio, un sistema può usare uno schema di denominazione per i nomi delle caselle di posta elettronica, un secondo schema di denominazione per gli host di Internet, un terzo per i file e un quarto per gli indirizzi di memoria virtuale. Quando l’interprete di un programma incontra un nome, deve sapere quale schema di denominazione invocare. L’ambiente che circonda l’uso del nome di solito fornisce abbastanza informazioni per identificare lo schema di denominazione. Per esempio, in un programma applicativo, l’autore di quel programma sa che il programma dovrebbe aspettarsi che i nomi dei file siano interpretati solo dal file system e che i nomi degli host Internet siano interpretati solo da qualche servizio di rete.
L’interprete che incontra il nome esegue l’algoritmo di mappatura dei nomi dello schema di denominazione appropriato. L’algoritmo di mappatura dei nomi risolve il nome, il che significa che scopre e restituisce il valore associato (per questo motivo, l’algoritmo di mappatura dei nomi è anche chiamato resolver). L’algoritmo di name-mapping è solitamente controllato da un parametro aggiuntivo, noto come contesto. Per un dato schema di denominazione, ci possono essere molti contesti diversi, e un singolo nome dello spazio dei nomi può mappare a valori diversi quando il resolver usa contesti diversi. Per esempio, nel discorso ordinario, quando una persona si riferisce ai nomi “tu”, “qui” o “Alice”, il significato di ciascuno di questi nomi dipende dal contesto in cui la persona lo pronuncia. D’altra parte, alcuni schemi di denominazione hanno un solo contesto. Tali schemi di denominazione forniscono quelli che sono chiamati spazi di nomi universali, e hanno la bella proprietà che un nome ha sempre lo stesso significato all’interno di quello schema di denominazione, non importa chi lo usa. Per esempio, negli Stati Uniti, i numeri di sicurezza sociale, che identificano i conti pensionistici e fiscali del governo, costituiscono uno spazio di nomi universale. Quando c’è più di un contesto, l’interprete può dire al resolver quale dovrebbe usare o il resolver può usare un contesto predefinito.
Possiamo riassumere il modello di denominazione definendo la seguente operazione concettuale sui nomi:
valore ← risolvere (nome, contesto)
Quando un interprete incontra un nome in un oggetto, per prima cosa capisce quale schema di denominazione è coinvolto e quindi quale versione di resolve dovrebbe invocare. Poi identifica un contesto appropriato, risolve il nome in quel contesto, e sostituisce il nome con il valore risolto mentre continua l’interpretazione. La variabile context dice a resolve quale contesto usare. Quella variabile contiene un nome noto come riferimento al contesto.
In un processore, i numeri di registro sono nomi. In un processore semplice, l’insieme dei nomi di registro e i registri a cui quei nomi sono legati sono entrambi fissati al momento della progettazione. Nella maggior parte degli altri sistemi che usano i nomi (incluso lo schema di denominazione dei registri di alcuni processori ad alte prestazioni), è possibile creare nuovi legami ed eliminare quelli vecchi, enumerare lo spazio dei nomi per ottenere una lista di legami esistenti e confrontare due nomi. Per questi scopi definiamo altre quattro operazioni concettuali:
status ← bind (nome, valore, contesto)
status ← unbind (nome, contesto)
list ← enumerate (context)
result ← compare (nome1, nome2)
La prima operazione cambia contesto aggiungendo un nuovo binding; il risultato di stato riporta se il cambiamento è riuscito o meno (potrebbe fallire se il nome proposto viola le regole di sintassi dello spazio dei nomi). Dopo una chiamata di successo a bind, resolve restituirà il nuovo valore per name.* La seconda operazione, unbind, rimuove un binding esistente dal contesto, con lo status che riporta nuovamente il successo o il fallimento (forse perché non c’era un binding esistente). Dopo una chiamata di successo a unbind, resolve non restituirà più quel valore per name. Le operazioni di bind e unbind permettono l’uso di nomi per creare connessioni tra oggetti e cambiare quelle connessioni in seguito. Un progettista di un oggetto può, usando un nome per riferirsi ad un oggetto componente, scegliere l’oggetto a cui quel nome è legato in quel momento o in un momento successivo invocando bind, ed eliminare un legame che non è più appropriato invocando unbind, tutto senza modificare l’oggetto che usa il nome. Questa capacità di ritardare e cambiare i binding è un potente strumento usato nella progettazione di quasi tutti i sistemi. Alcune implementazioni di denominazione forniscono un’operazione di enumerazione, che restituisce una lista di tutti i nomi che possono essere risolti nel contesto. Alcune implementazioni di enumerate possono anche restituire una lista di tutti i valori attualmente legati nel contesto. Infine, l’operazione compare riporta (vero o falso) se nome1 è uguale a nome2 oppure no. Il significato di “stesso” è una questione interessante affrontata nella Sezione 2.2.5, e potrebbe richiedere la fornitura di ulteriori argomenti di contesto.
Diversi schemi di denominazione hanno regole diverse sull’unicità delle mappature nome-valore. Alcuni schemi di denominazione hanno una regola che un nome deve corrispondere esattamente a un valore in un dato contesto e un valore deve avere un solo nome, mentre in altri schemi di denominazione un nome può corrispondere a più valori, o un valore può avere più nomi, anche nello stesso contesto. Un altro tipo di regola di unicità è quella di uno spazio dei nomi con identificatore unico, che fornisce un insieme di nomi che non saranno mai riutilizzati per tutta la durata dello spazio dei nomi e, una volta legati, rimarranno sempre legati allo stesso valore. Si dice che un tale nome abbia un legame stabile. Se uno spazio dei nomi con identificatore unico ha anche la regola che un valore può avere un solo nome, i nomi unici diventano utili per tenere traccia degli oggetti per un lungo periodo di tempo, per confrontare i riferimenti per vedere se sono allo stesso oggetto, e per il coordinamento di copie multiple in sistemi in cui gli oggetti sono replicati per prestazioni o affidabilità. Per esempio, il numero di conto cliente della maggior parte dei sistemi di fatturazione costituisce uno spazio di nome identificativo unico. Il numero di conto farà sempre riferimento al conto dello stesso cliente finché esiste, nonostante i cambiamenti nell’indirizzo del cliente, nel numero di telefono o anche nel nome personale. Se il conto di un cliente viene cancellato, il numero di conto di quel cliente non verrà riutilizzato un giorno per un conto di un altro cliente. I campi denominati all’interno del conto, come il saldo dovuto, possono cambiare di volta in volta, ma il legame tra il numero di conto del cliente e il conto stesso è stabile.
L’algoritmo di mappatura dei nomi più un singolo contesto non mappano necessariamente tutti i nomi dello spazio dei nomi in valori. Così, un possibile risultato dell’esecuzione di resolve può essere un risultato non trovato, che resolve può comunicare al chiamante o come valore riservato o come eccezione. D’altra parte, se lo schema di denominazione permette a un nome di mappare diversi valori, un possibile risultato può essere una lista di valori. In questo caso, l’operazione di unbind può richiedere un argomento aggiuntivo che specifichi quale valore scollegare. Infine, alcuni schemi di denominazione forniscono una ricerca inversa, il che significa che un chiamante può fornire un valore come argomento all’algoritmo di mappatura dei nomi, e scoprire quale nome o quali nomi sono legati a quel valore.
La figura 2.10 illustra il modello di denominazione, mostrando uno spazio dei nomi, il corrispondente universo di valori, un algoritmo di mappatura dei nomi, e un contesto che controlla l’algoritmo di mappatura dei nomi.
In pratica, si incontrano tre algoritmi di mappatura dei nomi frequentemente usati:
Table lookup
■
Recursive lookup
■
Multiple lookup
L’implementazione più comune di un contesto è una tabella di coppie {nome, valore}. Quando l’implementazione di un contesto è una tabella, l’algoritmo di mappatura del nome è solo una ricerca del nome in quella tabella. La tabella stessa può essere complessa, coinvolgendo hashing o B-trees, ma l’idea di base è sempre la stessa. Legare un nuovo nome ad un valore consiste nell’aggiungere quella coppia {nome, valore} alla tabella. La figura 2.11 illustra questa comune implementazione del modello di denominazione. C’è una tabella di questo tipo per ogni contesto, e contesti diversi possono contenere diverse associazioni per lo stesso nome.
Esempi del mondo reale sia del modello generale di denominazione che dell’implementazione della table-lookup abbondano:
Un elenco telefonico è un contesto di table-lookup che lega nomi di persone e organizzazioni a numeri di telefono. Come nell’esempio della rete di comunicazione dati, i numeri telefonici sono essi stessi nomi che la compagnia telefonica risolve in apparenze fisiche della linea, usando un algoritmo di mappatura dei nomi che coinvolge i codici di zona, le centrali e i centralini fisici. Gli elenchi telefonici di Boston e di San Francisco sono due contesti dello stesso schema di denominazione; qualsiasi nome particolare può apparire in entrambi gli elenchi telefonici, ma se è così, è probabilmente legato a numeri telefonici diversi.
Piccoli numeri interi nominano i registri di un processore. Il valore è il registro stesso, e la mappatura dal nome al valore è realizzata tramite cablaggio.
Le celle di memoria sono nominate in modo simile con numeri chiamati indirizzi, e la mappatura nome-valore è di nuovo realizzata tramite cablaggio. Il capitolo 5 descrive un meccanismo di ridenominazione degli indirizzi noto come memoria virtuale, che lega blocchi di indirizzi virtuali a blocchi di celle di memoria contigue. Quando un sistema implementa più memorie virtuali, ogni memoria virtuale è un contesto distinto; un dato indirizzo può riferirsi ad una cella di memoria diversa in ogni memoria virtuale. Le celle di memoria possono anche essere condivise tra le memorie virtuali, nel qual caso la stessa cella di memoria può avere lo stesso (o diverso) indirizzo in diverse memorie virtuali, come determinato dai binding.
Un tipico file system di computer usa diversi livelli di nomi e contesti: settori del disco, partizioni del disco, file e directory sono tutti oggetti nominati. Le directory sono esempi di contesti di ricerca di tabelle. Un particolare nome di file può apparire in diverse directory, legato allo stesso file o a file diversi. La sezione 2.5 presenta un caso di studio sulla denominazione nel file system unix.
I computer si connettono alle reti di comunicazione dati in luoghi noti come punti di attacco alla rete. I punti di attacco alla rete sono solitamente denominati con due schemi di denominazione distinti. Il primo, usato all’interno della rete, comporta uno spazio dei nomi che consiste di numeri in un campo di lunghezza fissa. Questi nomi sono legati, a volte in modo permanente e a volte solo brevemente, ai punti fisici di entrata e uscita della rete. Un secondo schema di denominazione, usato dai clienti della rete, mappa uno spazio dei nomi universale di stringhe di caratteri più facile da usare ai nomi del primo spazio dei nomi. La sezione 4.4 è un caso di studio del Domain Name System, che fornisce una denominazione dei punti di attacco facile da usare per Internet.
Un programmatore identifica le variabili di procedura con dei nomi, e ogni attivazione della procedura fornisce un contesto distinto in cui la maggior parte di tali nomi viene risolta. Alcuni nomi, identificati come “statici” o “nomi globali”, possono invece essere risolti in un contesto che è condiviso tra le attivazioni o tra diverse procedure. Quando una procedura viene compilata, alcuni dei nomi originali delle variabili, facili da usare, possono essere sostituiti con identificatori interi che sono più convenienti da manipolare per una macchina, ma il modello di denominazione è ancora valido.
Un Uniform Resource Locator (URL) del World Wide Web è mappato su una specifica pagina web da un algoritmo relativamente complicato che spezza l’URL in diverse parti costituenti e risolve le parti usando diversi schemi di denominazione; il risultato alla fine identifica una particolare pagina web. La sezione 3.2 è un caso di studio di questo schema di denominazione.
Un sistema di fatturazione dei clienti mantiene tipicamente almeno due tipi di nomi per ogni conto cliente. Il numero di conto nomina l’account in uno spazio di nome identificativo unico, ma c’è anche uno spazio di nome distinto di nomi personali che possono anche essere usati per identificare l’account. Entrambi questi nomi sono tipicamente mappati nei record dei conti da un sistema di database, in modo che i conti possano essere recuperati o per numero di conto o per nome personale.
Questi esempi evidenziano anche una distinzione tra “denominazione” e legame. Alcuni contesti, ma non tutti, “nominano” le cose, nel senso che mappano un nome ad un oggetto che è comunemente pensato per avere quel nome. Così, l’elenco telefonico non “nomina” né le persone né le linee telefoniche. Da qualche altra parte ci sono contesti che legano i nomi alle persone e che legano i numeri di telefono a particolari telefoni fisici. L’elenco telefonico lega i nomi delle persone ai nomi dei telefoni.