Exec (Unix): differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Olisait (discussione | contributi)
Funzionalità collegamenti suggeriti: 2 collegamenti inseriti.
 
(42 versioni intermedie di 22 utenti non mostrate)
Riga 1:
{{Titolo minuscolo}}
{{S|informatica}}
{{S|linguaggi di programmazione}}
{{F|informatica|data=giugno 2007}}
 
'''Execexec''' è una [[Funzione (informatica)|funzione]] di sistema presente nella [[libreria standard del C]] del [[linguaggioGNU|progetto GNU]], la [[GNU C Library]].
 
== Informazioni di carattere generale ==
 
Exec sostituisce i segmenti codice e dati del processo correntemente in esecuzione nello stato di utente con quelli di un altro programma contenuto in un file eseguibile specificato.
 
Agendo solo su processi in stato di utente la exec non interviene sul segmento di sistema e sui file utilizzati dal processo che la invoca.
 
Durante la chiamata ad exec e al successivo caricamento nella Ram del nuovo codice compilato il processo mantiene lo stesso [[PID (Unix)|pid]].
 
La funzione exec deve passare dei parametri al nuovo programma che viene eseguito. Essi vengono letti dal programma tramite il meccanismo di passaggio usuale argc, argv.
 
=== Implementazione ===
Questo servizio è implementato in [[C (linguaggio)|C]] sotto unix[[Unix]] (ma anche in generale in [[POSIX]] ), tramite una famiglia di chiamate di sistema ( comprese in [[unistd.h]] su unix e [[process.h]] nei sistemi dos/win32..).
 
Principalmente troviamo:
 
:<code>int execl(const char *pathpathname, const char *arg0arg, ..., (char *) NULL);</code>
:<code>int execle(const char *pathpathname, const char *arg0arg, ..., const(char *) NULL, char *const *envp[]);</code>
:<code>int execlp(const char *pathfile, const char *arg0arg, ..., (char *) NULL);</code>
:<code>int execlpe(const char *path, const char *arg0, ..., const char *const *envp); //Solo process.h ??</code>
:<code>int execv(const char *pathpathname, const char *const *argv[]);</code>
:<code>int execve(const char *pathpathname, const char *const *argv[], const char *const *envp[]);</code>
:<code>int execvp(const char *pathfile, const char *const *argv[]);</code>
:<code>int execvpe(const char *pathfile, const char *const *argv[], const char *const *envp[]);</code>
 
Il primo argomento, pathpathname che vediamo in ciascuno degli exec*, specifica il pathnamepercorso e il nome del file da eseguire come [[processo figlio]]. Gli argomenti arg0,...,argN sono una lista di puntatori agli argomenti da passare al processo figlio; argv è un array di puntatori agli argomenti. L'argomento envp e'è invece un array di puntatori alla configurazione dell'ambiente attuale. Le chiamate execle, execlpe, execve ed execvpe (ovvero quelle con il suffisso 'e'), alterano l'ambiente del loro processo figlio, passando una lista con le configurazioni d'ambiente per l'appunto tramite l'argomento envp (array di puntatori a caratteri).
 
=== Funzioni e nomi ===
L'unica vera chiamata di sistema è la execve, le altre si differenziano da questa per come vengono passati i parametri.
 
In linea di massima si puo' dire che la p finale che troviamo nelle due varianti execlp ed execvp, hanno come significato che il primo parametro passato come argomento non dev'essere necessariamente il path ma puo' essere il nome di cio' che intendiamo eseguire.
 
In linea di massima si può dire che la p finale che troviamo nelle due varianti execlp ed execvp, hanno come significato che il primo parametro passato come argomento non dev'essere necessariamente il path ma può essere il nome di ciò che intendiamo eseguire.
 
Un esempio potrebbe essere il seguente:
 
<syntaxhighlight lang="c">
execl("/bin/ls","ls", "-l", (char *)0);
execl("/bin/ls","ls", "-l", (char *)0);
</syntaxhighlight>
 
e il suo complementare che contiene la p ma non il path:
 
<syntaxhighlight lang="c">
execlp("ls","ls", "-l", (char *)0);
execlp("ls","ls", "-l", (char *)0);
</syntaxhighlight>
 
Le altre lettere che possiamo trovare oltre al suffisso "p" (e, l o v) presentano dei significati ben precisi.
 
Riassumendo si hanno le seguenti funzioni di base:
Riga 53 ⟶ 55:
|<code>p</code>|| Non serve specificare il path.
|-
|<code>v</code>|| Gli argomenti a riga di comando saranno passati alla funzione tramite un array di puntatori.
|-
|<code>e</code>|| L'insieme dei puntatori dell'environment è passato esplicitamente al processo figlio.
Riga 62 ⟶ 64:
 
=== Gestione degli errori ===
Normalmente le funzioni exec* non tornano al processo chiamante, se una funzione exec torna al processo chiamate vuol dire che la chiamata non è andata a buon fine. Viene allora tornato il valore di errore -1, ed [[errno]] ci presenta uno dei seguenti valori:
 
{| class="wikitable"
Riga 70 ⟶ 72:
| <code>E2BIG</code>||Sono stati immessi troppi argomenti.
|-
| <code>EACCES</code>||Non si puo'può avere l'accesso al file specificato (problemi di blocco/privilegi).
|-
|<code>ENOENT</code>||Il file o il path specificato non esiste.
|-
|<code>ENOMEM</code>||Non c'e'è abbastanza memoria per eseguire il processo figlio
|-
|}
 
 
Specifiche di Execve e delle sue varianti:
Riga 83 ⟶ 84:
=== execve ===
==== Sintassi ====
''int execve (const char *pathname, char *const argv[], char *const envp[])''
 
* "pathname" è il nome dell'eseguibile (completo di percorso) per il nuovo processo
''int execve (const char *filename, const char *argv[], const char *env[])''
* "argv[]" è il vettore degli argomenti in input a pathname (accessibile dal main come main(int argc, char *argv[], char *env[])).
 
* "env[]" è il vettore di ambiente in cui ''pathname'' è eseguito (accessibile dal main come main(int argc, char *argv[], char *env[])).
* filename è il nome dell'eseguibile per il nuovo processo
* Gli elementi di env hanno la forma "var=valore".
* argv è il vettore degli argomenti in input a filename
* accessibili al main in C(int argc, char * argv[], char*env[])
* env è il vettore di ambiente in cui filename è eseguito (accessibile dal main come main(int argc, char *argv[], char * env[])). Gli elementi di env hanno la forma "var=valore".
 
==== Comportamento ====
 
execve esegue le seguenti azioni
* cerca il formato di filenamepathname
* lancia il loader per il formato di filenamepathname
* crea una nuova mappa di memoria per il processo
* sistema in maniera opportuna argomenti e ambiente
Riga 102 ⟶ 101:
* trasferisce il controllo al nuovo programma
 
Il processo viene poi messo in [[user mode]]: la chiamata - come tutte quelle della famiglia exec - non ritorna che in caso di errore.
 
 
==== Esempio d'uso ====
 
<syntaxhighlight lang="c">
/*file sorgente execve.c*/
 
#include <stdio.h>
/*file sorgente execve.c*/
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main() {
char *argv[]={"nomeFile", NULL};
if(execve("nomeFile", argv, enviroment))==-1)
perror("execve fallita");
}
 
extern char **environ;
=== execl ===
 
int main() {
==== esempio ====
char *argv[] = {"nomeFile", NULL};
 
if(execve("nomeFile", argv, enviroment))==-1)
perror("execve fallita");
 
}
</syntaxhighlight>
 
=== execl ===
==== esempio ====
Passiamo a visualizzare un esempio di utilizzo di execl:
 
<syntaxhighlight lang="c">
int main(){
int main(){
printf("Esecuzione di ls\n");
execl("/bin/ls", "ls", "-l", (char*)0);
printf("Si e' verificato un errore nell'esecuzione del codice\n");
exit(1);
}
 
printf("Si è verificato un errore nell'esecuzione del codice\n");
''Osservazioni'': Ci si potrebbe chiedere come mai dopo execl, ci sia una [[printf]] che segnala un errore, ed un'uscita che richiama un po' l'attenzione, ma bisogna pensare che execl elimina il programma originale sovrascrivendolo con quello passato come parametro. Quindi le istruzioni che seguono la chiamata ad execl, verranno eseguite solo se non sono state sovrascritte dal nuovo programma, cioè solo se si e' verificato un errore durante l'esecuzione, e il controllo è stato ripassato al chiamante (execl).
exit(1);
}
</syntaxhighlight>
 
''Osservazioni'': Ci si potrebbe chiedere come mai dopo execl, ci sia una [[printf]] che segnala un errore, ed un'uscita che richiama un po' l'attenzione, ma bisogna pensare che execl elimina il programma originale sovrascrivendolo con quello passato come parametro. Quindi le istruzioni che seguono la chiamata ad execl, verranno eseguite solo se non sono state sovrascritte dal nuovo programma, cioè solo se si è verificato un errore durante l'esecuzione, e il controllo è stato ripassato al chiamante (execl).
=== execv ===
 
=== execv ===
==== esempio ====
 
Ora vediamo un esempio di utilizzo di execv:
 
<syntaxhighlight lang="c">
int main(){
int main() {
char *a[]={"ls","-l",(char *)0};
char *a[] = {"ls", "-l", (char *)0};
printf("Esecuzione di ls\n");
execv printf("/home/Esecuzione di ls\n",a);
execv("/home/ls", a);
printf("Si e' verificato un errore nell'esecuzione del codice\n");
exit(1);
}
 
printf("Si è verificato un errore nell'esecuzione del codice\n");
exit(1);
}
</syntaxhighlight>

''Osservazioni'': Come prima le istruzioni che seguono la chiamata ad execv, verranno eseguite solo se non sono state sovrascritte dal nuovo programma, cioè solo se si e'è verificato un errore durante l'esecuzione, e il controllo è stato ripassato al chiamante (execv).
 
=== execlp ===
Prototipo: <code>int execlp(const char *file, const char *arg, ..., (char *) NULL);</code>
Generalmente dopo una fork, uno dei due processi (padre/figlio) effettua una chiamta del sistema execlp per sostuiture lo spazio di memoria del processo con un nuovo programma!
* Il primo argomento specifica il path del programma (cercato nelle cartelle della variabile di ambiente path)
La chiamata del sistema execlp carica in memoria un file binario, cancellando l'immaigne di meoria del programma contente la stessa chiamta del sistema execlp, quindi avvia la sua esecuzione.
* Gli argomenti successivi, specificano una lista di stringhe terminata dalla stringa NULL che costituiscono gli argomenti passati al programma
 
Esempio di utilizzo:
In altre parole, sostituisce la porzione di codice presente in memoria relativa a chi ha invocato la funzione execlp, con qualcos'altro!
 
<syntaxhighlight lang="c">
In questo modo i due processi possono comunicare e procedere ognuno per la sua strada!
for (i=1; i<=2; i++){
fork();
execlp("echo", "myEcho", "i", (char*)0);
printf("%d\n", i); //codice che non verrà mai eseguito, a meno di errori nella execlp
}
</syntaxhighlight>
La compilazione fornirà in output:
i
i
 
=== execvp ===
Prototipo: <code>int execvp(const char *file, char *const argv[]);</code>
 
Esempio di utilizzo:
 
<syntaxhighlight lang="c">
int main(){
char *a[]={"ls", "-l", (char *)0};
printf("Esecuzione di ls\n");
execvp("ls", a);
 
printf("Si è verificato un errore nell'esecuzione del codice\n");
exit(1);
}
</syntaxhighlight>
 
=== execle ===
{{...|informatica}}
 
== Collegamenti esterni ==
* {{Collegamenti esterni|OpenBsd man=https://man.openbsd.org/OpenBSD-6.7/amd64/execle.3}}
 
{{Portale|informatica}}
 
[[Categoria:Librerie softwareC]]