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 » Lanciare programmi

Lanciare programmi




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

Not logico


Not logico Il not logico si indica con il punto esclamativo. Esso consente di

Tesina evoluzione innaturale - informatica


TESINA EVOLUZIONE INNATURALE SOMMARIO 1) Introduzione 2) La

Impiego del costrutto monitor


Impiego del costrutto monitor             X) Si implementi il problema dei
immagine di categoria

Scarica gratis Lanciare programmi

Lanciare programmi

Si tratta, ovviamente, di lanciare programmi dall'interno di altri programmi. E' una possibilità la cui utilità dipende largamente non solo dagli scopi del programma stesso, ma anche e soprattutto dalle caratteristiche del sistema operativo. E' facile intuire che un sistema in grado di dare supporto all'elaborazione multitasking (il riferimento a Unix, ancora una volta, è voluto e non casuale) offre interessanti possibilità al riguardo (si pensi, ad esempio, ad un gruppo di programmi elaborati contemporaneamente, tutti attivati e controllati da un unico programma gestore); tuttavia, anche in ambienti meno evoluti si può fare ricorso a tale tecnica (per un esempio, anche se non esaustivo, vedere pag. 169).

Per brevità, adottando la terminologia Unix, il programma chiamante si indica d'ora in poi con il termine parent, mentre child è il programma chiamato.

La libreria C

La libreria C fornisce supporto al lancio di programmi esterni mediante un certo numero di funzioni, che possono essere considerate standard entro certi limiti (più precisamente, lo sono in ambiente DOS); per approfondimenti circa la portabilità dei sorgenti che ne fanno uso vedere pag. 142.

Come al solito, per i dettagli relativi alla sintassi, si rimanda alla manualistica specifica del compilatore utilizzato; qui si intende semplicemente mettere in luce alcune interessanti caratteristiche di dette funzioni e i loro possibili ambiti di utilizzo.

system()

La funzione system() costituisce forse il mezzo più semplice per raggiungere lo scopo: essa deve infatti essere invocata passandole come unico parametro una stringa contenente, né più né meno, il comando che si intende eseguire. Ad esempio, il codice:

#include <stdlib.h>

#include <stdio.h>       // per fprintf()

#include <errno.h>           // per errno


if(system('comando param1 param2') == -1)

fprintf(stderr,'errore %d in system()n', errno);

lancia il programma comando passandogli i parametri param1 e param2. Dal momento che, nell'esempio, per comando non è specificato un path, il sistema utilizza la variabile di environment PATH (non è necessario specificare l'estensione). Dall'esempio si intuisce che system() restituisce  in caso di errore; tuttavia va sottolineato che la restituzione di  non implica che comando sia stato effettivamente eseguito secondo le intenzioni del programmatore. Infatti system() esegue il comando ricevuto come parametro attraverso l'interprete dei comandi: in altre parole, essa non fa altro che lanciare una copia dell'interprete stesso e scaricargli il barile, proprio come se fosse stata digitata la riga

command -c 'comando param1 param2'

Ne segue che system() si limita a restituire  nel caso in cui sia riuscita a lanciare correttamente l'interprete, e non si preoccupa di come questo se la cavi poi con il comando specificato: pertanto, non solo non è possibile conoscere il valore restituito al sistema dal child, ma non è neppure possibile sapere se questo sia stato effettivamente eseguito.

Se, da una parte, ciò appare come un pesante limite, dall'altra la system() consente di gestire anche comandi interni DOS, proprio perché in realtà è l'interprete a farsene carico. Ad esempio è possibile richiedere

system('dir /p');

e system() restituisce  solo se non è stato possibile lanciare l'interprete dei comandi. Inoltre, è possibile eseguire i file batch. Ancora,

system('command');

esegue un'istanza dell'interprete, mettendo il prompt del DOS a disposizione dell'utilizzatore: digitando

exit

al prompt la shell viene chiusa e l'elaborazione del programma parent riprende

Infine, system() può essere utilizzata semplicemente per verificare se l'interprete dei comandi è disponibile:

system(NULL);

restituisce un valore diverso da  se è possibile lanciare l'interprete dei comandi.

E' superfluo (speriamo!) chiarire che l'argomento di system() non deve necessariamente essere una costante stringa, come si è assunto per comodità negli esempi precedenti, ma è sufficiente che esso sia di tipo char *: ciò consente la costruzione dinamica della riga di comando, ad esempio mediante l'utilizzo di funzioni atte ad operare sulle stringhe (strcpy() strcat() sprintf(), etc.).

spawn()

Come la system(), anche le funzioni della famiglia spawn() consentono di lanciare programmi esterni come se fossero subroutine del parent; tuttavia esse non fanno ricorso all'interprete dei comandi, in quanto si basano sul servizio 4Bh dell'int 21h : di conseguenza, non è possibile utilizzarle per invocare comandi interni DOS né file batch, tuttavia si ha un controllo più ravvicinato sull'esito dell'operazione. Esse infatti restituiscono  se l'esecuzione del child non è riuscita; in caso contrario restituiscono il valore che il programma child ha restituito a sua volta.

Tutte le funzioni spawn() richiedono come primo parametro un intero, di solito dichiarato nei prototipi con il nome mode, che indica la modalità di esecuzione del programma child: in PROCESS.H sono definite le costanti manifeste P_WAIT (il child è eseguito come una subroutine) e P_OVERLAY (il child è eseguito sostituendo in memoria il parent, proprio come se fosse chiamata la corrispondente funzione della famiglia exec()). Come osservato riguardo system() (vedere pag. 136), anche le funzioni spawn() non possono essere utilizzate per lanciare shell permanenti o programmi TSR (vedere pag. 169); tuttavia l'utilizzo del valore P_OVERLAY per il parametro mode consente un'eccezione, in quanto il parent scompare senza lasciare traccia di sé e, in uscita dal child, la sua esecuzione non può mai riprendere.

Il secondo parametro, di tipo char *, è invece il nome del programma da eseguire: esso, diversamente da quanto visto circa la system(), deve essere completo di estensione; inoltre, se non è specificato il path, solo le funzioni spawnlp() spawnlpe() spawnvp() e spawnvpe() utilizzano la variabile di environment PATH (la lettera 'p' presente nel suffisso finale dei nomi delle funzioni indica proprio detta caratteristica).

Funzioni del gruppo 'l'

Le funzioni del gruppo 'l' si distinguono grazie alla presenza, nel suffisso finale del loro nome, della lettera 'l', la quale indica che gli argomenti della riga di comando del child sono accettati dalla funzione spawnl() come una lista di parametri, di tipo char *, conclusa da un puntatore nullo.

Ad esempio, per eseguire il comando

myutil -a -b 5 arg1 arg2

si può utilizzare la funzione spawnl()

#include <process.h>


spawnl(P_WAIT,'myutil.exe','myutil','-a','-b','5','arg1','arg2',NULL);

Si noti che il nome del programma è passato due volte a spawnl(): la prima stringa indica il programma da eseguire, mentre la seconda rappresenta il primo parametro ricevuto dal programma child: essa deve essere comunque passata alla funzone spawnl() e, per convenzione, è uguale al nome del programma stesso (il valore di argv[0], se questo è stato a sua volta scritto in linguaggio C: vedere pag. 107 e seguenti). Il programma myutil è ricercato solo nella directory corrente; la funzione spawnlp(), la cui sintassi è identica a quella di spawnl(), effettua la ricerca in tutte le directory specificate dalla variabile di environment PATH

Il processo child eredita l'ambiente del parent: in altre parole, le variabili di environment del child sono una copia di quelle del programma chiamante. I due environment sono pertanto identici, tuttavia il child non può accedere a quello del parent, né tantomeno modificarlo. Se il parent ha la necessità di passare al child un environment diverso dal proprio, può farlo mediante le funzioni spawnle() e spawnlpe(), che, pur essendo analoghe alle precedenti, accettano un ulteriore parametro dopo il puntatore nullo che chiude la lista degli argomenti:

static char *newenv[] = ;


spawnv(P_WAIT,'myutil.exe',childArgv);

Si intuisce facilmente che la spawnvp() cerca il comando da eseguire in tutte le directory definite nella variabile di ambiente PATH (qualora il suo path non sia specificato esplicitamente), mentre spawnv() lo ricerca solo nella directory corrente.

Si noti che il primo elemento dell'array childArgv[] punta, per convenzione, al nome del child medesimo (del resto il nome scelto per l'array nell'esempio dovrebbe suggerire che esso viene ricevuto dal child come parametro argv di main(): vedere pag. 107).

Infine, le funzioni spawnve() e spawnvpe(), analogamente a spawnle() e spawnlpe(), accettano come ultimo parametro un puntatore ad un array di stringhe, che costituiranno l'environment del child.

exec()

Le funzioni della famiglia exec(), a differenza delle spawn(), non trattano il child come una subroutine del parent: esso, al contrario, viene caricato in memoria ed eseguito in luogo del parent, sostituendovisi a tutti gli effetti.

I nomi e la sintassi delle funzioni exec() sono strettamente analoghi a quelli delle spawn(): esistono otto funzioni exec(), ciascuna delle quali può essere posta in corrispondenza biunivoca con una spawn(): a seconda della presenza delle lettere 'l v p' ed 'e' il comportamento di ciascuna exec() è assolutamente identico a quello della corrispondente spawn() chiamata con il parametro mode uguale a P_OVERLAY (le funzioni exec() non accettano il parametro mode; il loro primo parametro è sempre il nome del programma da eseguire).

Se si desidera che il solito comando degli esempi precedenti sostituisca in memoria il parent e sia eseguito in luogo di questo, è del tutto equivalente utilizzare

spawnv(P_OVERLAY,'myutil.exe',childArgv);

oppure

execv('myutil.exe',childArgv);

ad eccezione di quanto specificato in tema di portabilità (pag. 142).

Tabella sinottica

Di seguito si presenta una tabella sinottica delle funzioni spawn() ed exec()

Sintassi e caratteristiche delle funzioni spawn() e exec()


modo

nome del child

argomenti del child

environment del child

spawnl()

int P_WAIT,
P_OVERLAY

char *

lista di char *
il primo è = child
l'ultimo è
NULL


spawnlp()

int P_WAIT,
P_OVERLAY

char *
(utilizza
PATH

lista di char *
il primo è = child
l'ultimo è
NULL


spawnle()

int P_WAIT,
P_OVERLAY

char *

lista di char *
il primo è = child
l'ultimo è
NULL

char **Env

spawnlpe()

int P_WAIT,
P_OVERLAY

char *
(utilizza
PATH

lista di char *
il primo è = child
l'ultimo è
NULL

char **Env

spawnv()

int P_WAIT,
P_OVERLAY

char *

char **Argv
Argv[0] =
child
Argv[ultimo] = NULL


spawnvp()

int P_WAIT,
P_OVERLAY

char *
(utilizza PATH

char **Argv Argv[0] = child
Argv[ultimo] = NULL


spawnve()

int P_WAIT,
P_OVERLAY

char *

char **Argv
Argv[0] =
child
Argv[ultimo] = NULL

char **Env

spawnvpe()

int P_WAIT,
P_OVERLAY

char *
(utilizza PATH

char **Argv Argv[0] = child
Argv[ultimo] = NULL

char **Env

execl()


char *

lista di char *
il primo è = child
l'ultimo è
NULL


execlp()


char *
(utilizza
PATH

lista di char *
il primo è = child
l'ultimo è
NULL


execle()


char *

lista di char *
il primo è = child
l'ultimo è
NULL

char **Env

execlpe()


char *
(utilizza
PATH

lista di char *
il primo è = child
l'ultimo è
NULL

char **Env

execv()


char *

char **Argv
Argv[0] =
child
Argv[ultimo] = NULL


execp()


char *
(utilizza PATH

char **Argv Argv[0] = child
Argv[ultimo] = NULL


execve()


char *

char **Argv
Argv[0] =
child
Argv[ultimo] = NULL

char **Env

execvpe()


char *
(utilizza PATH

char **Argv Argv[0] = child
Argv[ultimo] = NULL

char **Env

Condivisione dei file

I processi child lanciati con spawn() e exec() condividono i file aperti dal parent. In altre parole, entrambi i processi possono accedere ai file aperti dal parent, per ciascuno dei quali il sistema operativo mantiene un unico puntatore: ciò significa che le operazioni effettuate da uno dei processi (spostamento lungo il file, lettura, scrittura) influenzano l'altro processo; tuttavia se il child chiude il file, questo rimane aperto per il parent. Vediamo un esempio:

Il seguente frammento di codice, che si ipotizza appartenere al parent, apre il file C:AUTOEXEC.BAT, effettua un'operazione di lettura e lancia il child, passandogli il puntatore allo stream (vedere pag. 120).


#define   MAX 128


char sPrtStr[10], line[MAX];

FILE *inP;


inP = fopen('C:AUTOEXEC.BAT','r');

printf(fgets(line,MAX,inP));

sprintf(sPtrStr,'%p',inP);

spawnl(P_WAIT,'child','child',sPtrStr,NULL);

printf(fgets(line,MAX,inP));


Se si eccettua la mancanza del pur necessario codice per la gestione degli eventuali errori, tralasciato per brevità, il listato appare piuttosto banale: l'unica particolarità è rappresentata dalla chiamata alla funzione sprintf(), con la quale si converte in stringa il valore contenuto nella variabile inP (l'indirizzo della struttura che descrive lo stream aperto dalla fopen()). Come si può vedere, il parent passa al child proprio detta stringa (è noto che i parametri ricevuti da un programma sulla riga di comando sono necessariamente stringhe), alla quale esso può accedere attraverso il proprio argv[1]. Ecco un frammento del child:


#define   MAX 128


int main(int argc,char **argv)


Il child memorizza in inC l'indirizzo della struttura che descrive lo stream aperto dal parent ricavandolo da argv[1] mediante la sscanf(), effettua un'operazione di lettura e chiude lo stream; tuttavia, il parent è ancora in grado di effettuare operazioni di lettura dopo il rientro dalla spawnl(): l'effetto congiunto dei due programmi consiste nel visualizzare le prime tre righe del file C:AUTOEXEC.BAT

Va sottolineato che è necessario compilare entrambi i programmi per un modello di memoria che gestisca i dati con puntatori a 32 bit (medium, large, huge: vedere pag. 151 e seguenti): è infatti molto verosimile (per non dire scontato) che il child non condivida il segmento dati del parent, nel quale è allocata la struttura associata allo stream: l'utilizzo di indirizzi a 16 bit, che esprimono esclusivamente offset rispetto all'indirizzo del segmento dati stesso, condurrebbe inevitabilmente il child a utilizzare quel medesimo offset rispetto al proprio data segment, accedendo così ad una locazione di memoria ben diversa da quella desiderata.

Portabilità

Date le differenti caratteristiche del supporto fornito dai diversi sistemi operativi (DOS e Unix in particolare), sono necessarie alcune precisazioni relative alla portabilità del codice tra i due ambienti.

La funzione system() può essere considerata portabile: essa è infatti implementata nelle librerie standard dei compilatori in entrambi i sistemi.

Analoghe considerazioni valgono per le funzioni exec(), ma con prudenza: in ambiente Unix, solitamente, non sono implementate le funzioni execlpe() e execvpe(). Inoltre, le funzioni execlp() e execvp() in versione Unix sono in grado di eseguire anche shell script (analoghi ai file batch del DOS). Tutte le funzioni exec() in Unix, infine, accettano come nome del child il nome di un file ASCII che a sua volta, con una particolare sintassi, specifica qual è il programma da eseguire (ed eseguono quest'ultimo).

Le funzioni spawn() non sono implementate in ambiente Unix. La modalità di gestione dei child, in questo caso, si differenzia profondamente proprio perché Unix è in grado di eseguire più processi contemporaneamente: pertanto un child non è necessariamente una subroutine del parent; i due programmi possono essere eseguiti in parallelo. Un modo per emulare le spawn() consiste nell'uso congiunto delle funzioni fork() (assente nelle librerie C in DOS) ed exec(): la fork() crea una seconda istanza del parent; di conseguenza, essa fa sì che coesistano in memoria due processi identici, l'esecuzione di entrambi i quali riprende in uscita dalla fork() stessa.. Dall'esame del valore restituito dalla fork() è possibile distinguere l'istanza parent dall'istanza child, in quanto fork() restituisce  al child, mentre al parent restituisce il PID del child stesso. L'istanza child può, a questo punto, utilizzare una delle exec() per eseguire il programma desiderato, mentre il parent, tramite la funzione waitpid() (anch'essa non implementata nel C in DOS) può attendere la terminazione del child e esaminarne il valore restituito mediante la macro WEXITSTATUS(). A puro titolo di esempio si riporta di seguito un programma, compilabile in ambiente Unix, che utilizza la tecnica descritta.

#include <stdio.h>                /* printf(), puts(), fprintf(), stderr */

#include <unistd.h>                           /* fork(), execlp(), pid_t */

#include <errno.h>            /* errno */

#include <sys/wait.h>                        /* waitpid(), WEXITSTATUS() */


int main(void);

void child(void);

void parent(pid_t pid);


int main(void)


return(0);



void child(void)




void parent(int pid)


printf('Il child ha restituito %d.n',WEXITSTATUS(status));


In uscita dalla fork() entrambe le istanze del programma effettuano il test sul valore da questa restituito, e solo in base al risultato del test medesimo esse si differenziano, eseguendo parent() oppure child(). E' ovvio che l'istanza child non deve necessariamente eseguire una exec() e annullarsi: essa può eseguire qualunque tipo di operazione (comprese ulteriori chiamate a fork()), come del resto l'istanza parent non ha l'obbligo di attendere la terminazione del child, ma, al contrario, può eseguire altre elaborazioni in parallelo a quello e verificarne lo stato solo in un secondo tempo.

La libreria C in ambiente Unix implementa altre funzioni (assenti sotto DOS) per il controllo dei processi child: ad esempio la popen(), che, con una sintassi del tutto analoga alla fopen() (vedere pag. 120 e seguenti), consente di lanciare un programma e al tempo stesso rende disponibile uno stream di comunicazione, detto pipe, mediante il quale il parent può leggere dallo standard output o scrivere sullo standard input del child. Ancora, la pipe() apre una pipe (questa volta non collegata a standard input e standard output) che può essere utilizzata come un file virtuale in condivisione tra processi parent e child.

Come strumento di comunicazione inter‑process, in DOS si può ricorrere alla condivisione dei file, come descritto a pag. 141. Trattandosi di file reali, il metodo è certo meno efficiente della pipe, ma ha il vantaggio di risultare portabile tra i due sistemi. Per utilizzare in DOS aree di memoria in condivisione (tecnica in qualche modo paragonabile alla shared memory supportata da Unix) si può ricorrere, rinunciando alla portabilità, allo stratagemma illustrato a pag. .

Per approfondimenti circa le problematiche di portabilità dipendenti dai sistemi operativi si veda pag. 169.




Se viene fornito un path, tutte le backslash presenti nella stringa devono essere raddoppiate, onde evitare che il compilatore le interpreti come parte di sequenze di escape. Ad esempio, la stringa

'c:dostree'

non viene gestita correttamente, mentre

'c:dostree'

funziona come desiderato.

A dire il vero anche le altre funzioni (spawn() exec() possono eseguire un'istanza dell'interprete dei comandi. Bisogna però fare attenzione a non cacciarsi nei guai: una chiamata come

system('command /p');

è lecita e viene tranquillamente eseguita, ma l'effetto dell'opzione /p su command.com è di renderne permanente in memoria la nuova istanza: in tal caso il comando exit non ha alcun effetto, e non è più possibile riprendere l'esecuzione del parent. Per ragioni analoghe, l'esecuzione di un programma TSR (vedere pag. 169) attraverso la system() potrebbe avere conseguenze distruttive.

Vedere pag.  e seguenti.

Detto servizio utilizza il valore presente nel registro macchina AL per stabilire il tipo di azione da intraprendere: in particolare, AL = 0 richiede il caricamento e l'esecuzione del programma (funzioni spawn() se il primo parametro è P_WAIT), mentre AL = 3 richiede il caricamento e l'esecuzione del child nella memoria riservata al parent (overlay), il quale viene a tutti gli effetti sostituito dal nuovo programma (funzioni spawn() se il primo parametro è P_OVERLAY, e funzioni exec()

E' immediato verificarlo con il codice seguente:

static char *newenv[] = {'USER=Pippo','PATH=C:DOS',NULL);


spawnlpe(P_WAIT,'command.com','command.com',NULL,newenv);

che esegue una istanza dell'interprete dei comandi; digitando il comando SET al prompt viene visualizzato l'environment corrente. Il comando EXIT consente di chiudere la shell e restituire il controllo al parent.

La variabile environ contiene l'indirizzo dell'array di stringhe (ciascuna avente formato NOME=VAL, dove NOME rappresenta il nome della variabile e VAL il suo valore) rappresentanti l'environemnt del programma. Se si utilizza putenv() per modificare il valore di una variabile o per inserirne una nuova, il valore di environ viene automaticamente aggiornato qualora sia necessario rilocare l'array. Dichiarando main() con tre parametri (vedere pag. 107) il terzo rappresenta il puntatore all'array delle stringhe di environment ed inizialmente ha lo stesso valore di environ, ma non viene modificato da putenv()

Process IDentifier. E' un intero positivo che identifica univocamente un processo tra tutti quelli in esecuzione in un dato momento. Si tratta di un concetto sconosciuto in DOS.

Scarica gratis Lanciare programmi
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 ...