View Full Version : [Linux] le syscall
sinceramente non sapevo dove postare una domanda del genere, in scienza e tecnica non mi sembrava troppo appropriata in quanto poi se si tira in ballo il codice dopo aver discusso a livello concettuale mi sembrava fuori luogo, spero di aver azzeccato il luogo.
Non conoscendo la filosofia di Linux ma sapendo che pure questo SO ha le sue syscall, mi chidevo come funzionano.
Premetto che sto studiando minix e per quanto concerne le chiamate di sistema almeno l'approcio iniziale dovrebbe essere il medesimo.
Il sistema minix consta di un certo numero di livelli partendo dal più basso abbiamo: il kernel il clock task ed il system task.
Salendo i livelli troviamo i diver, i server ed infine i processi utente.
Bene, dopo questa premessa mi chiedo: quando un programma a livello utente chiama un servizio tipico del kernel, chiede al livello inferiore un servizio e poi questo al suo livello inferiore sino ad arrivare al kernel ?
Questo modo di comportarsi mi ricorda molto il modello ISO/OSI relativo alle reti.
cdimauro
19-05-2009, 20:36
Sì, il concetto è quello: si sale (o scende, a seconda dei punti di vista :D) finché il componente di un certo strato non è in grado di soddisfare la richiesta dell'applicazione.
Nei sistemi dotati di microkernel e in cui vengono utilizzate primitive di comunicazione di tipo messagge passing o similiari, è possibile che ci siano dei buffer che "raccolgono" batch di richieste, in modo da spedirne un blocco al livello superiore e minimizzare, ove possibile, l'overhead dovuto a tale passaggio (esempio: passare da user-mode a kernel-mode è costoso per una CPU, per quanto possa essere agevolata da apposite API).
ah grazie.
Stavo meditando ancora sui livelli :D
Quindi ad esempio, se un processo utente chiama la fork() che risulta essere una system call senza parametri, questa viene rediretta tramite passaggi verso il kernel che duplicherà il processo chiamante(il padre).
Mi viene da pensare che se il processo passasse direttamente la fork() così com'è direttamente al kernel, forse il kernel non avrebbe i riferimenti per capire chi deve duplicare allora, questa benedetta fork() scendendo i vari livelli, viene trasformata da opportune funzioni presenti in determinate librerie e che fungono da interfaccia verso il kerne, e la trasformazione consiste nell'aggiungere ulteriori informazioni per il kernel come credo ad esempio: il PID, il messaggio che rappresenta la fork() etc...
Ma funziona così ? :stordita:
cdimauro
22-05-2009, 07:32
ah grazie.
Stavo meditando ancora sui livelli :D
Quindi ad esempio, se un processo utente chiama la fork() che risulta essere una system call senza parametri, questa viene rediretta tramite passaggi verso il kernel che duplicherà il processo chiamante(il padre).
Sì.
Mi viene da pensare che se il processo passasse direttamente la fork() così com'è direttamente al kernel, forse il kernel non avrebbe i riferimenti per capire chi deve duplicare allora, questa benedetta fork() scendendo i vari livelli, viene trasformata da opportune funzioni presenti in determinate librerie e che fungono da interfaccia verso il kerne, e la trasformazione consiste nell'aggiungere ulteriori informazioni per il kernel come credo ad esempio: il PID, il messaggio che rappresenta la fork() etc...
Ma funziona così ? :stordita:
Non esattamente. Il kernel conosce già in partenza il PID del processo, per cui non ha bisogno che quest'informazione venga "aggiunta" nei vari passaggi di livello.
Per la fork() penso che il PID sia l'unica informazione di cui ha bisogno per duplicare il processo, perché questo numero individua una struttura in kernel space in cui sono conservati tutti i dati necessari per capire quali risorse utilizza un processo e, quindi, in che modo poterle duplicare (se serve) per crearne un clone.
Grazie ancora.
Nel caso della fork() quindi, i vari livelli servono per tenere lontani i software di livello user dal PID ad esempio ?
In questo modo, grazie ai livelli, nascondi la complessità ai livelli superiori almeno, questa è l'utilità pratica che ho colto io dei livelli. Con i livelli io sotto, a livello kernel, posso fare quello che voglio e il tuo programma, ma anche tutte le migliaia di programmi già scritti e compilati che ci sono in giro, richiamando semplicemente la fork() non devono essere necessariamente ricompilati e continueranno a funzionare.
Al contrario, se chiamavi direttamente la sys_call come fa il kernel, magari dovendo specificare diversi parametri, se a livello kernel ne aggiungo o tolgo uno di detti parametri obbligherei tutti a ricompilare.
Chissà se ho colto!
cdimauro
22-05-2009, 20:29
Grazie ancora.
Nel caso della fork() quindi, i vari livelli servono per tenere lontani i software di livello user dal PID ad esempio ?
No, il PID e altre informazioni possono tranquillamente essere noti a tutti i livelli. Dipende tutto dal "grado" di astrazione che vogliamo imprimere al nostro s.o..
In questo modo, grazie ai livelli, nascondi la complessità ai livelli superiori almeno, questa è l'utilità pratica che ho colto io dei livelli. Con i livelli io sotto, a livello kernel, posso fare quello che voglio e il tuo programma, ma anche tutte le migliaia di programmi già scritti e compilati che ci sono in giro, richiamando semplicemente la fork() non devono essere necessariamente ricompilati e continueranno a funzionare.
Al contrario, se chiamavi direttamente la sys_call come fa il kernel, magari dovendo specificare diversi parametri, se a livello kernel ne aggiungo o tolgo uno di detti parametri obbligherei tutti a ricompilare.
Chissà se ho colto!
Chiaro, e infatti si astrae sia per due motivi: nascondere dettagli (man mano che si sale di livello di astrazione) e si "pone una barriera", un filtro, una porta che dir si voglia, che regoli il passaggio da un livello a un altro.
A tal proposito alla recente PyCon3 c'è stato un talk (http://www.pycon.it/conference/talks/lo-zen-e-larte-della-manutenzione-delle-astrazioni) di Alex Martelli sull'argomento che è stato illuminante. Ti consiglio di leggerlo perché ne vale veramente la pena (e, meglio ancora, guardarti il video quando sarà disponibile, fra qualche mese).
domanda apparentemente banale :)
Sto implementando una nuova syscall in minix e siccome è la prima volta che sbircio all'interno di un SO, il fatto che minix come del resto altri SO sia costruito a livelli significa che:
- scrivo il programma A che necessita del programma sottostante B e che a sua volta necessita del livello sottostante C etc....
Così dicendo è quindi una scelta implementativa e cioè, nessuno mi vieta di by-passare tutti i livelli sottostanti e parlare direttamente col kernel sempre che si sappia dove mettere le mani.
Perchè sto discorso ?
Perchè solo passando dallo studio concettuale alla pratica ci si accorge che i livelli non sono altro che concetti nel senso che, nessuno mi vieta di implementare la mia funzione direttamente nel kernel e far parlare una applicazione di livello user direttamente co livello kernel, giusto ?
Ovviamente così facendo manderei a ramengo tutta la filosofia sulla quale si erge l'idea minix!
In definitiva le istruzioni che si possono trovare in giro circa l'implementazione di una nuova syscall in minix non sono altro che le volontà di Tanenbaum e cioè: se vuoi che minix continui a funzionare nel modo da me stabilito devi:
- dichiarare nella directory xyz la tua nuova syscall
- implementare la tal funzione nella directory abcd
- ricompilare e richiamare la syscall
Questo significa sposare la filosofia di un SO ?
Scusate la lungaggine ma grazie infinite a chi mi leggerà
cdimauro
02-06-2009, 06:09
Sostanzialmente sì, e proprio per questo NON aggiungerei API al microkernel: proprio perché è "micro", sarebbe meglio aggiungerle negli strati software che girano in user mode. :D
grazie per la risposta :)
Ti risulta che quando compili il kernel questo ha già indirizzi fissi in memoria dove piazzarci le varie tabelle per le varie gestioni degli interrupt ad esempio e le eccezioni ?
accumulo un'altra domanda
push eax
mov eax, 0+4(esp)
mov (old_eip), eax
mov eax, 4+4(esp)
mov (old_cs), eax
mov eax, 8+4(esp)
mov (old_eflags), eax
come mai non viene memorizzato il contenuto dello stack direttamente nelle variabili ma si passa prima dal registro eax ?
Cioè anzichè:
mov eax, 0+4(esp)
mov (old_eip), eax
non si scrive:
mov (old_eip), 0+4(esp)
cdimauro
04-06-2009, 20:09
grazie per la risposta :)
Ti risulta che quando compili il kernel questo ha già indirizzi fissi in memoria dove piazzarci le varie tabelle per le varie gestioni degli interrupt ad esempio e le eccezioni ?
Alcuni kernel possono memorizzare delle strutture a indirizzi fissi, ma in questo modo si legano all'architettura.
In generale è sempre meglio non fare assunzioni su dove mettere certe strutture, anche perché le architetture moderne permettono di rilocare praticamente qualunque dato.
accumulo un'altra domanda
push eax
mov eax, 0+4(esp)
mov (old_eip), eax
mov eax, 4+4(esp)
mov (old_cs), eax
mov eax, 8+4(esp)
mov (old_eflags), eax
come mai non viene memorizzato il contenuto dello stack direttamente nelle variabili ma si passa prima dal registro eax ?
Cioè anzichè:
mov eax, 0+4(esp)
mov (old_eip), eax
non si scrive:
mov (old_eip), 0+4(esp)
Perché non hai un Motorola 68000, ma un ben più semplice 80386. :p
Perché non hai un Motorola 68000, ma un ben più semplice 80386. :p
che significa ?
cdimauro
04-06-2009, 20:40
Significa che un 68000+ è in grado di eseguire una copia fra due locazioni di memoria con una sola istruzione MOVE, mentre un 386+ no, perché la sua MOV può avere soltanto un indirizzo di memoria per i suoi due operandi, e l'altro deve necessariamente essere un registro.
Significa che un 68000+ è in grado di eseguire una copia fra due locazioni di memoria con una sola istruzione MOVE, mentre un 386+ no, perché la sua MOV può avere soltanto un indirizzo di memoria per i suoi due operandi, e l'altro deve necessariamente essere un registro.
ah, grazie
tomminno
04-06-2009, 22:11
Significa che un 68000+ è in grado di eseguire una copia fra due locazioni di memoria con una sola istruzione MOVE, mentre un 386+ no, perché la sua MOV può avere soltanto un indirizzo di memoria per i suoi due operandi, e l'altro deve necessariamente essere un registro.
Sbaglio o le istruzioni memoria-memoria sono da considerarsi come la peste?
E sono istruzioni che un'architettura RISC non supporterà mai?
cdimauro
04-06-2009, 23:07
Sbaglio o le istruzioni memoria-memoria sono da considerarsi come la peste?
Dipende dall'architettura: sui CISC sono, invece, quelle più ambite. :cool:
E sono istruzioni che un'architettura RISC non supporterà mai?
I RISC non supportano nemmeno le istruzioni memoria<->registro (le uniche consentite sono le LOAD e le STORE): figuriamoci quelle memoria<->memoria. :p
Sbaglio o le istruzioni memoria-memoria sono da considerarsi come la peste? motivare é fatica vedo.
cdimauro
05-06-2009, 07:04
Non essere così duro: potrebbe essere un legittimo dubbio. Non si nasce esperti di architetture degli elaboratori...
tomminno
05-06-2009, 12:49
motivare é fatica vedo.
La domanda era retorica.
x86 rimane pur sempre un'architettura CISC, che avrebbe anche consetito l'inserimento di tale (architetturalmente parlando) bestialità.
In questo caso x86 batte il 68000 ma dopotutto uno è vivo e vegeto l'altro è morto.
cdimauro
05-06-2009, 13:01
La domanda era retorica.
x86 rimane pur sempre un'architettura CISC, che avrebbe anche consetito l'inserimento di tale (architetturalmente parlando) bestialità.
Io la considero una funzionalità eccellente, non una bestialità: http://www.appuntidigitali.it/3838/motorola-68000-la-rivoluzione-a-32-bit/
E parlo da programmatore assembly 680x0 e 80x86 di lunga data.
A questo punto, visto che non sei a digiuno sulle architetture degli elaboratori, una spiegazione del perché sarebbe una bestialità non sarebbe male.
In questo caso x86 batte il 68000
In semplicità dell'ISA da una parte (perché implementare un decoder che consente di specificare una qualunque modalità di indirizzamento per entrambi gli operandi è più complicato), ma ha un design pessimo dall'altra, con una tabella degli opcode organizzata in maniera pseudocasuale e l'uso dei prefissi che complica ulteriormente la decodifica delle istruzioni.
ma dopotutto uno è vivo e vegeto l'altro è morto.
Il che non vuol dire proprio nulla.
cdimauro
07-06-2009, 08:01
Up. :O
mi ci è voluto un pochino a mettermi nei panni del kernel, ragionavo sempre da sviluppatore di applicazioni :muro:
Difatti quando un processo faceva una send, mi domandavo come potesse scrivere nelle strutture dell'altro pocesso poi mi sono detto: alt! è il kernel che gioca con i due processi e allora tutto è diventato leggermente più chiaro: minix non è poi così brutto come lo si vuole dipingere :)
tomminno
08-06-2009, 12:52
A questo punto, visto che non sei a digiuno sulle architetture degli elaboratori, una spiegazione del perché sarebbe una bestialità non sarebbe male.
Semplicemente motivi di efficienza e semplicità architetturale tanto cari alla filosofia RISC.
Operazioni memoria memoria comportano un supporto da parte di entrambi gli operandi di tutte le possibili combinazioni di indirizzamento, ovvero tanti opcode da gestire.
La cosa è evidenziata anche nell'articolo da te citato:
Purtroppo questo è stato anche il suo tallone d’Achille, perché a una grande facilità di fruttamento da parte dei programmatori s’è contrapposta un’architettura interna molto “pesante” e “complicata”
Poi è chiaro l'x86 rimane una delle peggiori architetture mai concepite da mente umana, ma tant'è il peggio si accompagna al peggio [Wintel] :D
cdimauro
08-06-2009, 13:30
Semplicemente motivi di efficienza e semplicità architetturale tanto cari alla filosofia RISC.
Sì, ma qui stiamo parlando di CISC, che hanno una filosofia completamente diversa e che... alla fine è sopravvissuta.
Operazioni memoria memoria comportano un supporto da parte di entrambi gli operandi di tutte le possibili combinazioni di indirizzamento, ovvero tanti opcode da gestire.
Vero, ma bisogna vedere in che modo sono strutturati gli opcode.
Se prendiamo il modello x86, che NON ha operazioni memoria-memoria (se escludiamo i casi particolari rappresentati dalle speciali istruzioni che lavorano su "stringhe", che però non hanno modalità d'indirizzamento, in quanto è codificato internamente), vediamo che l'organizzazione è decisamente più incasinata e il decoder si complica non poco stando dietro alla quasi casualità della mappatura degli opcode e, soprattutto, alla presenza di un numero variabile prefissi.
La cosa è evidenziata anche nell'articolo da te citato:
Il motivo lo capirai meglio quando parlerò del Motorola 68020. L'architettura 68000 di per sé è abbastanza semplice da decodificare, ma purtroppo Motorola con lo 020 ha introdotto delle modalità d'indirizzamento mostrosamente complicate che, infatti, erano in grado di mettere in ginocchio anche il 68060 (primo processore superscalare per questa famiglia, le cui prestazioni erano molto elevate in caso di istruzioni semplici come quelle del 68000 appunto).
Poi è chiaro l'x86 rimane una delle peggiori architetture mai concepite da mente umana, ma tant'è il peggio si accompagna al peggio [Wintel] :D
Con un amighista sfegatato sfondi una porta aperta. :p
possiamo asserire che in definitiva minix tutto quello che fa è:
- gestire i processi in modo che a turno tutti usino la CPU
- mettere a dormire i processi nel momento in cui questi faoono I/O
- prendere i messaggi di un processo mittente e portali al processo destinatario che sia driver, server etc....
In definitiva scrive nei PCB dei processi e legge e scrive negli stack dei vari processi in quanto solo il kernel può farlo :fagiano:
cdimauro
14-06-2009, 20:13
Sostanzialmente sì.
grazie cdmauro :)
parlando della fork() il cui scopo è quello di duplicare processi:
while (TRUE){
type.....prompt();
read...command(command, paramaters);
if(fork() != 0){
waitpid(-1, &status, 0);
} else {
execve(command, parameters, 0)
}
}
questo programma che dovrebbe essere la shell di unix, se eseguito passando come argomento un comando + parametri si comporta nel modo seguente:
una volta incontrata la fork() questa ritorna un PID=0 e quindi viene eseguito il ramo dell'else che mandando in esecuzione la execve(...) fa iniziare una duplicazione completa in memoria del processo chiamante.
In memoria ora si hanno due processi identici però:
il processo "padre" ora ha PID != 0 e quindi rimane in attesa sulla waitpid(...)
domande:
- il processo figlio rimane con PID = 0 ?
E quindi si comporta ora come se fosse un padre ?
Come si potrebbe far generare al processo figlio un ulteriore processo figlio ?
Spero di essere stato chiaro
cdimauro
22-06-2009, 12:56
Più o meno. Immagino che PID sia una variabile locale che dovrebbe contenere il PID del processo figlio, ma non la vedo nel sorgente che hai postato.
Più o meno. Immagino che PID sia una variabile locale che dovrebbe contenere il PID del processo figlio, ma non la vedo nel sorgente che hai postato.
purtroppo ho solo quel frammento di codice. Cmq si, PID viene ritornato dalla fork()
cdimauro
22-06-2009, 13:09
Allora posso soltanto aggiungere che ogni processo (padre, figlio / figli) ha un proprio PID (mai uguale a zero).
Allora posso soltanto aggiungere che ogni processo (padre, figlio / figli) ha un proprio PID (mai uguale a zero).
però all'inizio e cioè quando esiste solo il processo padre hai PID = 0 e poi via via il padre diventa != 0 e il figlio varrà sempre zero, giusto ?
cdimauro
22-06-2009, 13:37
Allora ti riferisci necessariamente alla variabile che conterrà il PID.
Quindi il codice sarà qualcosa del tipo:
PID = fork();
if(PID != 0){
waitpid(-1, &status, 0);
}
else {
execve(command, parameters, 0)
}
e quindi hai ragione.
grazie :)
ho preso del codice postato da un utente e dopo compilazione l'ho lanciato più volte
#include <stdio.h>
#include <sys/types.h>
void main()
{
pid_t pid;
printf (“Prima della fork: PID = %d\n”, getpid());
pid = fork();
if (pid==0) /* PROCESSO FIGLIO*/
{
printf (“FIGLIO: PID = %d\n”, getpid());
exit(0);
}
else /* PROCESSO PADRE */
{
printf (“PADRE: PID = %d\n”, getpid());
printf (“PADRE: PID DEL FIGLIO = %d\n”, pid);
exit(0);
}
}
digitando ps mi viene mostrato il PID della shell sh che è 92 mentre il PPID di questo programma mandandolo in esecuzione è 92 e il suo PDI è 219.
Quel 92 mi fa capire che il programma che io ho chiamato test è figlio di sh e quindi quando la shell manda in esecuzione test duplica se stessa.
Lanciando test più volte si nota il seguente output:
primo caso
Prima della fork: PID = 219
FIGLIO: PID = 220
PADRE: PID = 219
PADRE: PID DEL FIGLIO = 220
secondo caso
Prima della fork: PID = 227
PADRE: PID = 227
PADRE: PID DEL FIGLIO = 228
FIGLIO: PID = 228
sembrerebbe che si ha una duplicazione della shell e che quindi test diviene una copia della shell ma a sua volta una volta in esecuzione test, questo viene duplicato immediatamente nel primo caso e successivamente nel secondo caso: è dovuto allo scheduler questo effetto ?
cdimauro
23-06-2009, 12:42
Sì, è corretto.
una domanda di assembly
L1: .data 4 0 ! dichiara la variabile L1 di 4 byte inizializzata a zero
move al, (L1) ! copia il contenuto di L1 nel registro al della CPU
mov (L1), ah ! copia l'indirizzo contenuto nel registro ah nella variabile L1 ???
mov eax, L1 ! copia in eax l'indirizzo in memoria occupato da L1 ?????
le righe in rosso non mi sono molto chiare
grazie
malocchio
15-07-2009, 16:52
una domanda di assembly
L1: .data 4 0 ! dichiara la variabile L1 di 4 byte inizializzata a zero
move al, (L1) ! copia il contenuto di L1 nel registro al della CPU
mov (L1), ah ! copia l'indirizzo contenuto nel registro ah nella variabile L1 ???
mov eax, L1 ! copia in eax l'indirizzo in memoria occupato da L1 ?????
le righe in rosso non mi sono molto chiare
grazie
Mi sembra strano che tu non abbia capito le ultime due righe, visto che hai capito la seconda!
L1 è un "nome" a cui l'assembler (o il linker? non ricordo) sostituisce un numero, che è un indirizzo di memoria.
Supponiamo che ad L1 sia associato il numero 0xABC
Se tu provi a sostituire
0xABC: .data 4 0 ! riserva 4 byte a partire dall'indirizzo 0xABC
move al, (0xABC) ! copia il contenuto della cella di indirizzo 0xABC in AL
mov (0xABC), ah ! scrive nella cella d'indirizzo 0xABC il contenuto di AH
mov eax, 0xABC ! copia in EAX 0xABC
Non è troppo corretto dire che la prima riga dichiara una variabile. Piuttosto riserva uno spazio di memoria di 4 byte (azzerati). L1 è un nome che tu programmatore usi per riferirti all'indirizzo di memoria associato ad esso (lo spazio).
Le parentesi tonde in assembly funzionano come l'asterisco in C.
Forse l'ultima riga può essere poco intuitiva. Pensa subito a cos'è L1. Un nome. Il nome viene trasformato nella fase di assembling (si dice così?) in un numero (che è un indirizzo di memoria), ben definito. Quindi potendo andare a guardare il codice assemblato tu vedrai l'ultima istruzione proprio come "scrivi in EAX 0xABC", una costante numerica, che è l'indirizzo di memoria che l'assembler durante il suo lavoro ha deciso di associare ad L1.
Il discorso gira un po', spero di averti aiutato
Le parentesi tonde in assembly funzionano come l'asterisco in C.
grazie per la risposta!
scritto così:
mov al, (L1)
mi è chiaro in quanto come tu mi dici è come se scrivessi
al = *L1
tipo, se L1 è all'indirizzo di memoria 100 e tale indirizzo contiene 20, dopo la mov al, (L1) il registro al conterrà 20
scritto così però:
mov (L1), ah
mi dice molto meno però dovrebbe essere ancora:
*L1 = qualcosa
ma questo qualcosa potrebbe essere di fatto o un indirizzo o un valore ?
esempio:
mov (L1), ah
se ah contiene 28, a questo punto l'istruzione qui sopra assegna all'indirizzo di memoria 100 relativo a L1 28 ?
E se scrivessi invece:
mov L1, ah
cambierebbe l'indirizzo di L1 da 100 a 28 ?
scusa ma ho un pò di confusione
grazie :)
malocchio
16-07-2009, 08:54
grazie per la risposta!
scritto così:
mov al, (L1)
mi è chiaro in quanto come tu mi dici è come se scrivessi
al = *L1
tipo, se L1 è all'indirizzo di memoria 100 e tale indirizzo contiene 20, dopo la mov al, (L1) il registro al conterrà 20
Tutto giusto, attenzione però che L1 non è ALL'INDIRIZZO di memoria, in realtà è L'INDIRIZZO di memoria, L1 diventa proprio un numero!
Ti correggo pignolamente perché sbagliare anche una preposizione potrebbe portare confusione... :)
scritto così però:
mov (L1), ah
mi dice molto meno però dovrebbe essere ancora:
*L1 = qualcosa
ma questo qualcosa potrebbe essere di fatto o un indirizzo o un valore ?
MM, allora in realtà mi rendo conto che l'asterisco del C può portare ad incomprensioni: nel C di solito lo usi per accedere alla cella di memoria il cui indirizzo è contenuto in un'altra cella (la variabile puntatore), mentre qui accedi all'indirizzo L1. Ho introdotto l'asterisco perché serve per accedere all'indirizzo specificato, ma in questo caso nell'assembly l'indirizzo è un numero costante, nel C (DI SOLITO) è una variabile puntatore.
Attenzione però che essenzialmente non c'è differenza tra le parentesi tonde in assembly e l'asterisco in C, solo che qui stiamo parlando di due casi diversi.
Infatti:
mov ah, (bx)
qui vediamo un uso corretto delle parentesi tonde, in cui però l'indirizzo non è specificato come costante numerica (l'L1 di prima), ma è contenuto nel registro BX (gli indirizzi vengono memorizzati in registri a 16/32/64 bit a seconda dell'architettura, ma questo adesso non ci interessa molto), durante l'esecuzione del programma. Anche qui però non possiamo parlare di "puntatore" come lo intendiamo solitamente nel C, perché in C il puntatore è una VARIABILE IN MEMORIA, che contiene l'indirizzo di un'altra variabile. Qui, per quanto ne sappiamo, l'indirizzo è in BX. Quindi è un po' diversa.
esempio:
mov (L1), ah
se ah contiene 28, a questo punto l'istruzione qui sopra assegna all'indirizzo di memoria 100 relativo a L1 28 ?
Sì, considerando l'indirizzo 100 come esempio succede proprio così. In C è traducibile nell'istruzione
*(100) = ventotto con l'unica differenza che ventotto è una variabile, AH è un registro.
In realtà in C non vedrai mai quest'istruzione, perché non puoi decidere a tempo di compilazione un indirizzo specifico a cui vuoi accedere (se non in casi estremi; di solito non puoi perché il sistema operativo non ti permette di accedere a celle di memoria che non ti "appartengono", come processo in esecuzione).
Le variabili globali in un programma C sono associate ciascuna ad un'etichetta come L1. Infatti, durante il processo di compilazione, esse sono tradotte in indirizzi!
E se scrivessi invece:
mov L1, ah
cambierebbe l'indirizzo di L1 da 100 a 28 ?
scusa ma ho un pò di confusione
grazie :)
Assolutamente No! Pensa ancora "L1 è un numero". Rendiamo più leggibile l'istruzione scrivendola in un linguaggio di prog degno di tal nome:
100 = ventottoNo, a questo punto vedi che non ha senso assegnare alla costante 100 il valore 28..
E' anche traducibile in (utile se hai un minimo di esperienza nei puntatori, e penso che tu ce l'abbia)
&L1 = ventottoQuando usi l'ampersend ti riferisci all'indirizzo in cui è contenuta una variabile, no? Ben non vedrai mai a sinistra di un uguale l'operatore &
Se non ti è totalmente chiara una qualsiasi cosa chiedi assolutamente! ;)
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.