Problemas Tema 4: Gestión de procesos en Unix Problema 4.1. Feu un programa que escrigui un missatge amb la seva identificació (per exemple, “El meu pid és %d\n”,getpid ()), que executi un fork i, per tant, creï un procés fill, i finalment que torni a escriure la seva identificació. Per què es produeix la duplicació del segon missatge? Problema 4.2. Sabeu que la crida fork torna el pid del procés fill al procés pare; i torna un zero al procés fill. Modifiqueu el programa anterior de forma que pare i fill escriguin el segon missatge diferent (per exemple, “PARE: el meu pid és ...” i “FILL: el meu pid és ...”) Problema 4.3. El procés fill creat per un fork no comparteix les variables globals amb el pare; únicament hereda els seus valors. Escriviu un programa en C en el qual el fill, abans d’acabar, esperi que el pare modifiqui una variable global. Veure que el pare acaba, però el fill no. (comproveu-ho amb la comanda ps) Problema 4.4. Modifiqueu el programa anterior per veure que els valors de les variables locals (que són a la pila) també s’hereden, però les pròpies variables tampoc es comparteixen. Problema 4.5. Escriviu un programa en el qual es creï una pipe i un procés fill. El pare ha d’escriure una línia de text a la pipe i el fill l’ha de llegir. Recordeu que cada procés ha de tancar el descriptor que no faci servir. Problema 4.6. Escriviu un programa que obri un fitxer per escriptura i creï un procés fill. El fill fa avançar el punter de lectura / escriptura. El pare espera 10 segons i escriu al fitxer. Comproveu que l’escriptura del pare s’ha fet a la posició que havia fixat el fill. Problema 4.7. Creeu un procés fill que escrigui el seu identificador i el del seu pare. El pare, també escriurà la identificació del fill que ha creat i la seva pròpia. Després, esperarà la finalització del fill. Veure que l’estat que rep el pare és, en part, proporcionat per l’argument que el fill dòna a la funció exit(). Problema 4.8. Feu un programa per veure qui adopta els processos que es queden sense pares. La sortida hauria Problemas Tema 4:Gestión de procesos en Unix 1 de ser semblant a: Sóc el procés XXX i el meu fill és el YYY Sóc el procés YYY i el meu pare és el XXX El meu pare ha mort i ara el meu nou pare és el ZZZ Quin és el procés ZZZ? Qui és el seu pare? (la crida al sistema getppid() torna l'identificador del procés pare del procés que la invoca) Problema 4.9. Esperar fill Programeu una funció que rebi el pid d’un procés fill concret, esperi que aquest procés acabi i retorni l’estat en que ha acabat (int EsperarFill(int pid)). Problema 4.10. Esperar tots els fills Programeu una funció que esperi a que acabin tots els fills d’un procés, retornant el nombre de fills que tenia aquest procés (int EsperarTotsElsFills()). Problema 4.11. Ja coneixeu les diferències que hi ha entre un Sistema Operatiu que ofereix processos mulfi-fluxes enfront d’un altre que ofereix processos amb un únic fluxe. Comenteu quins avantatges i quins inconvenients suposa tenir varis fluxes per procés. EXECUCIÓ DE PROGRAMES Problema 4.12. Creeu un procés fill que escrigui el seu identificador i l’identificador del seu pare. El codi d’aquest procés estarà en un fitxer executable diferent al del pare, i s’executarà fent servir la crida execlp. El pare, també escriurà la identificació del fill que ha creat i la seva pròpia. Després, esperarà la finalització del fill. Problema 4.13. Feu un programa que rebi un argument. Aquest argument serà una cadena que representarà un número (per exemple, 10). Primer, el programa escriu el seu identificador de procés i el número. Si el número és més gran que zero, el programa s’executa a si mateix passant-se com a argument el número decrementat en 1 (en el nostre exemple, 9) i en cas contrari acaba. Problema 4.14. Creeu n (n>2) processos fills que executin un mateix programa (que atura la seva execució durant un cert temps, com fa la comanda sleep). Feu que el pare esperi a un fill concret usant la funció EsperarFill(pid) programada abans. Problema 4.15. Escribid un programa que cree tantos hijos como se le indique en la línea de comandos. Cada uno Problemas Tema 4:Gestión de procesos en Unix 2 de estos hijos le enviará al padre su identificador de proceso mediante una pipe (todos los hijos por la misma pipe) y a continuación morirá. El padre escribirá en un fichero (segundo parámetro) todos los mensajes que le lleguen. Ejemplo: prompt% crea 15 hijos Creará 15 hijos cuyos identificadores aparecerán en el fichero “hijos”. Problema 4.16. Escribid un programa que cree tantos hijos como se le indican en argv[1] (como máximo creará 10 hijos). Con cada uno de ellos deberá abrir una pipe diferente. Una vez creados los hijos y las pipes, el proceso padre deberá abrir el fichero cuyo nombre se le pasa en argv[2] y leerá de 10 en 10 bytes hasta que se acabe el fichero. De cada 10 bytes, el primer byte es un carácter ASCII en el rango de ’0’ a ’9’. Usando este primer carácter el proceso padre le enviará al hijo correspondiente los bytes leidos. Cada hijo deberá ejecutar el siguiente código: /*fichero hijo.c*/ main (int argc,char *argv[]) { int nb; char b[80]; sprintf(b,”>>> %s\n”,argv[1]); write(1,b,strlen(b)); while((nb=read(0,b,sizeof(b)))>0) write(1,b,nb); } Problema 4.17. Multihead Heu d’escriure un programa que anomenarem multihead que presenti el següent comportament: multihead file1 N1 ... filem Nm Escriurà per la seva sortida estàndar (i en aquest ordre) les N1 primeres línies del fitxer file1, les N2 primeres línies del fitxer file2,..., i les Nm primeres línies del fitxer filem. Com a codi d’acabament multihead ens haurà de retornar: • 0 Ha pogut fer la feina correctament • 2i El fitxer filei no existeix • 2i+1 El fitxer filei no té Ni línies • 1 Qualsevol altre error provocat per alguna crida al sistema Si es detecta que algun fitxer no té el nombre de línies indicat o que no existeix, aquest programa finalitzarà inmediatament, retornant el codi d’acabament pertinent. Per simplificar la solució heu de fer servir el programa de sistema head que presenta el següent comportament. head N Treu per la seva sortida estàndar les N primeres línies que li arriben per la seva entrada estàndar. Com a codi d’acabament head ens retorna: • 0 Ha pogut realitzar la feina correctament • 1 S’ha produit un error provocat per alguna crida al sistema • 2 L’entrada estàndar no té N línies Problemas Tema 4:Gestión de procesos en Unix 3 Nota: El codi d’acabament d'un procés és el paràmetre que es passa a la crida al sistema exit, i que es obtenible pel seu procés pare amb la crida wait. Problema 4.18. Donats els següents programes: Pare.c #define NULL (char *) 0 main() { int fd,fp[2],err,status,id; char c; Fill.c main() { char B while (read(0,&c,1)>0) write(1,&c,1); exit(0); } fd = open (“pp.pp”,O_RDONLY); err = pipe(fp); if (fd == -1)||(err==-1) exit(1); id = fork(); switch (id) { case 0: c; A T.Fitxers oberts close(0); dup(fp[0]); close(fp[0]; close(fp[1]); execl(“fill”,fill”,NULL); exit(1); case -1:exit(1); default:close(1); dup(fp[1]); close(fp[0]); close(fp[1]); count mode offset T.Canals punter 0 1 2 3 rw 32 B while(read(fd,&c,1)!=0) write(1,&c,1); close(1); wait(&status); exit(0);} figura 1 } A la figura 1 podeu veure la disposició dels camps de les possibles estructures de dades internes del sistema operatiu: taula de canals (una per procés) i taula de fitxers oberts (una per a tot el sistema). Aquesta taula té els camps següents: Count (nombre de descriptors de canal que apunten a aquesta entrada de la TFO), Mode (mode en què s'ha obert el fitxer (r, w, rw)) i Offset (posició actual d'on llegir i/o escriure dades). 3. Dibuixeu la Taula de Canals i la Taula de Fitxers Oberts al punt A. Cal que ompliu els camps explicats a la figura 1. Suposeu que podem tenir fins a 10 canals oberts. 4. Feu el mateix que a la pregunta anterior però en el punt d’execució B, suposant que en un moment donat tant el pare com el fill estan, simultàniament, al punt B. 5. Què fa aquest programa si l’executem des de la shell de la següent forma: prompt% pare <in.dat >& out.dat on “<“ indica redireccionament del canal d’entrada i “>&” el redireccionament dels canals de sortida i error? Problemas Tema 4:Gestión de procesos en Unix 4 GESTIÓ D’EVENTS - SIGNALS Problema 4.19. Programeu dues rutines en C que ofereixin la funcionalitat d’unes suposades crides a sistema (que no tenim a UNIX) aturar i engegar. Amb elles fer dos programes que rebin com a argument un pid i aturin o engeguin el procés sol.licitat.. Problema 4.20. Feu un programa que llegeixi una línia del terminal. Si durant un període de 10 segons no s’ha teclejat un return, el programa ha d’acabar donant un error tal com Error reading command input Timeout period expired Si s'ha llegit una línia en menys de 10 segons, el programa cridarà a la rutina processar_linia(char *). Problema 4.21. Escriviu un programa que rebi un argument. Considereu que aquest argument és, segur, una P o una F. En el primer cas (P), el pare enviarà un signal SIGKILL al fill. En el segon, serà el fill qui enviï un SIGKILL al pare. Per què el resultat de les dues execucions (amb argument P o F) és diferent ? Qui escriu el missatge killed ? Quan ? Feu el mateix que abans, però enviant un signal diferent (per exemple, SIGINT) i fent que tant el pare com el fill el tinguin programat. Suggeriment: feu que el pare i el fill s’esperin uns 2 segons abans d’acabar. Així tenen temps per rebre el signal. Problema 4.22. Runner Escriviu un programa (“Runner”) tal que quan rebi el signal SIGUSR1, vol dir que cal executar un nou programa. El nom d'aquest nou programa el llegirà per una Named Pipe. Per la Named Pipe també es reben els noms dels fitxers que han d’actuar com a canals d'entrada i sortida estàndar. Així, si tenim executant-se el “Runner” en background, des de csh podem fer la seqüència: kill echo echo echo -SIGUSR1 pid 'fill' >Named_Pipe '/dev/ttyl' >Named_Pipe 'sortida_fill' >Named_Pipe i el resultat serà la creació d'un nou procés que executarà el programa del fitxer 'fill', amb l'entrada estàndar sobre el terminal i la sortida estàndar sobre el fitxer 'sortida_fill'. Si els canals d'entrada o sortida especificats es corresponen a “herència”, cal que el procés fill heredi els canals del programa “Runner”. Feu servir la rutina de llibreria de C strcmp (s1,s2), que retorna 0 si las cadenes s1 i s2 son iguals. Problemas Tema 4:Gestión de procesos en Unix 5 Així, el programa “Runner” tindrà el següent esquema: /* Declaració de variables */ ... /* Rutina de tractament del signal */ ... main() { /* Inicialitzacions */ ... /* Esperar, per sempre, el signal SIGUSR1*/ ... } Problema 4.23. Dos mètodes Disposem de dos programes diferents (mètode1 i mètode2) que realitzen un mateix càlcul. Llegeixen 3 nombres de la seva entrada estàndar, i per la sortida estàndar escriuen un enter que és el resultat del càlcul. La diferència entre aquests dos programes és el temps d’execució; per algunes entrades el mètode1 ens donarà abans el resultat que el mètode2, però per les altres entrades serà el mètode2 el primer en donar-nos el resultat. Quan un d’aquests mètodes finalitza el seu càlcul, envia un signal al seu procés pare (un signal del tipus SIG_USR1 el mètode1, i de tipus SIG_USR2 el mètode2), i escriu per la seva sortida estàndar (en el format intern de la màquina) el resultat. Si en qualsevol moment a un dels dos mètodes li arriba un signal del tipus SIGTERM, abortarà inmediatament el càlcul sense escriure res per la seva sortida estàndar. Volem aplicar aquest càlcul a 3 enters i obtenir el resultat de la forma més ràpida possible. Com a priori no sabem quin dels mètodes ens donarà abans el resultat, executarem tots dos mètodes concurrentment, i quan amb un obtinguem el resultat aturarem l’altre. Heu d’escriure un programa que tindrà com a únic paràmetre un nom de fitxer (on estaran els 3 nombres enters). Aquest programa escriurà per la seva sortida estandar el resultat del càlcul, indicant, a més a més, per quin mètode ha obtingut el resultat. Si passats 10 segons cap dels mètodes ens dona un resultat, aturarem inmediatament tots dos mètodes i el programa finalitzarà notificant-ho. Problema 4.24. Timeout Haced un programa llamado timeout que ejecute otro comando. Si dicho comando no acaba en el tiempo especificado, será abortado cuando se acabe el plazo. El formato del comando será: timeout [-sec] comando [lista_de_parámetros_del_comando] Si no se especifica sec se considerará un segundo. PROBLEMES D’EXAMEN Problema 4.25. Minish A) Es tracta de fer un petit intèrpret de comandes (o shell) en llenguatge C i utilitzant crides a sistema UNIX. Volem que tingui les següents característiques: • El seu nom és ‘minish’; ha d’escriure el prompt “minish% ”. • Ha de poder executar qualsevol comanda externa com ho faria el shell tradicional. Problemas Tema 4:Gestión de procesos en Unix 6 • Les comandes externes poden ser a qualsevol directori contingut a la variable d’entorn PATH. • ‘Minish’ ha d’esperar que acabi cada fill - execució en foreground - i ha d’escriure el resultat que torna el fill a la pantalla. • Quan acaba l’execució d’una comanda, ha de tornar a treure el prompt. Considereu que existeixen les següents rutines: 1) void analitzar_línia ( char * línia_entrada, /* Entrada */ int * n_arguments, /* Sortida */ char * arguments [MAX], /* Sortida */ char * stdin , /* Sortida */ char * stdout , /* Sortida */ char * stderr ); /* Sortida */ que rep línia_entrada (llegida per ‘minish’ amb un read ()), la divideix en els seus components i retorna la quantitat d’arguments que ha trobat (n_arguments), quins són (arguments) i els noms dels fitxers on l’usuari vol redireccionar els canals d’entrada, sortida i error. Si algun dels components no és present a la línia de comandes, el valor retornat és NULL. Exemple: amb la següent línia, minish% ls -l >fitxer prova la funció ens retornarà: n_arguments = 3 arguments[0] = “ls” stdin = NULL arguments[1] = “-l” stdout = “fitxer” arguments[2] = “prova” stderr = NULL arguments[3] = NULL ... arguments[MAX] = NULL 2) void obtenir_path ( int * npath, /* Sortida */ char * array_path [MAX]); /* Sortida */ que llegeix els paths continguts a la variable PATH i retorna la quantitat de directoris on hem de buscar i els seus noms complets des de l’arrel. B) Què provocaria la següent comanda executada des del ‘minish’ ? minish% rm * C) Indiqueu esquemàticament tots els passos amb què farieu que si el ‘minish’ rep un signal SIGUSR1, enviï un mail a l’usuari actual, amb el missatge: minish aborted by SIGUSR1 els usuaris que treballaven a la màquina són: ... iso2302 tty01 ... iso1205 tty02 ... ... (la sortida típica de who) Problema 4.26. Tenim un sistema operatiu UNIX amb el csh com a intèrpret de comandes i rebem la següent Problemas Tema 4:Gestión de procesos en Unix 7 comanda: prompt% ls -R / | cat | sort > fitxer ( “ls -R /” lista recursivament tots els fitxers de tots els directoris) 1. Què haurà de fer el shell pel que fa a la creació de processos i la gestió de canals d’entrada/sortida? Especifiqueu les crides a UNIX i l’esquema del shell per aquest cas concret. Quin és l’ordre en que acaben el processos i quin és el motiu que ho provoca? 2. El procés cat rep un SIGKILL a la meitat de la seva execució. Què desencadena això a cadascun dels processos? Què caldria afegir al codi font del programa ls per a que informi l’usuari quan s’ha produït aquesta situació? Consulteu les crides read i write pel que fa a les pipes. Problema 4.27. El mensajero Escribid un programa tal que dado un usuario localice en qué pantalla se encuentra conectado y le envíe un mensaje. El mensaje se introducirá por la entrada estándar y aparecerá en la pantalla del destinatario. Este programa debe controlar los errores que puedan producirse al invocar las llamadas al sistema susceptibles de fallar (fork, open...). mensajero iso1227 <fich.mens P.e: Envía un mensaje a iso1227 que está contenido en el fichero “fich.mens”. Para localizar el terminal, el proceso deberá llamar al comando who seguido de grep para determinar si el usuario está conectado y en qué pantalla se encuentra. prompt% who som03 pts/2 Jan 12 11:21 iso1227 pts/6 Jan 12 08:12 iso3109 pts/0 Jan 12 11:34 iso2223 pts/10 Jan 12 10:35 prompt% who | grep iso1227 iso1227 pts/6 Jan 12 08:12 En el caso de que el usuario no estuviera el resultado sería una línea vacía. prompt% who | grep iso5555 prompt% Realizad el programa en lenguaje C, con llamadas directas al sistema y funciones de librería que no llamen al sistema. Podéis tener en cuenta que el nombre de terminal empieza en la columna 11. Problema 4.28. Betes i fils Escriviu el programa, en llenguatge C, greplc que donat un patró i un conjunt de fitxers, escriu per la seva sortida estàndar per a cada fitxer que contingui el patró, únicament el seu nombre total de línies i el seu nom. greplc patró fitxer1[ fitxer2 ... fitxerN] Heu de fer servir els executables de les comandes grep i wc, que tenen el següent comportament: Problemas Tema 4:Gestión de procesos en Unix 8 • grep patró [fitxer] Busca les aparicions a fitxer de la cadena de caràcters patró, treient per la sortida estàndar totes les línies que contenen patró. Com a codi de finalització ens retornarà 0 si ha trobat alguna aparició del patró, 1 si no n’ha trobat cap i 2 si s’ha produit algun error. Si el paràmetre fitxer no és present, grep llegirà de la seva entrada estàndar. • wc -l [fitxer] Escriu per la seva sortida estàndar el nombre de línies que conté fitxer i el nom del fitxer. Si el paràmetre fitxer no és present, wc llegirà de la seva entrada estàndar i per la sortida estàndar únicament escriurà el nombre de línies de l’entrada estàndar. Exemple: montgros> grep main errors.c montgros> grep main prova.c main(argc, argv) montgros> grep main xemeneies1.c main(argc, argv) montgros> grep main xemeneies2.c main(argc, argv) montgros> greplc main errors.c prova.c xemeneies1.c xemeneies2.c 50 prova.c 50 xemeneies1.c 57 xemeneies2.c Problema 4.29. Fes-t'ho tu mateix Volem escriure un programa que executi el mateix que faria la sentència de Bourne shell: $ primer opcio | segon 2>sortida_error & i es proposa el següent codi, que heu de completar amb les crides al sistema oportunes. Així, aquest programa s'executarà amb la següent comanda (carr es el nom de l'executable): $ carr primer opcio segon sortida_error main(int argc,char *argv[]) { char buff[256]; char *param[4]; if (analitza_arguments (argc,argv,param)==0) { sprintf (buff, ”Número incorrecte de parametres\n”); write (2, buff, strlen(buff)); exit (1); } /* Segons l'exemple anterior, en aquest punt tindrem: param[0] = “primer”; Nom del primer programa executable param[1] = “opcio”; Argument per el primer executable param[2] = “segon”; Nom del segon programa executable param[3] = “sortida_error”; Nom de fitxer on surten els missatges d'error */ .... exit (0); } Nota: Per fer el dos últims apartats, marqueu a la solució del apartat 1 el punt d'inserció de nou codi i detalleu a continuació quin és el codi afegit. Problemas Tema 4:Gestión de procesos en Unix 9 1. Completeu (en el punt marcat amb punts suspensius) el codi anterior amb les crides al sistema Unix necessàries. Especifiqueu completament els paràmetres de les crides al sistema. 2. Afegiu el codi necessari de forma que, mentre s'executa la comanda anterior, al premer les tecles (control-C) no s'acabin els processos creats. Nota: En premer el control-C, els processos creats reben el signal SIGINT. 3. Afegiu més codi de forma que si els processos fill no acaben en 5 segons, s'escrigui un missatge per la sortida d'error i acabin tots els processos. Problema 4.30. Queremos que un proceso muestre un mensaje por pantalla cada 5 segundos. Para ello tenemos: void funcionAlarma() { if(write(1,”recibida alarma”,15)==-1) mostrar_error(“en el write”); alarm(5); } void main(){ signal(SIGALRM,funcionAlarma); alarm(5); while(1) { pause(); } } Sin embargo, sólo aparece un mensaje de “recibida alarma”, y el proceso muere al recibir el segundo signal ¿Por qué ocurre esto? ¿Qué harías para solucionarlo? Problema 4.31. El impaciente Queremos escribir una utilidad (“wcTemporizado”) que nos indique, dada una lista de ficheros (como máximo, 10), el que tiene el máximo número de líneas, y el total de éstas. Existe un problema añadido, y es que queremos calcular este valor antes de N segundos (N es el primer parámetro que se le pasa al programa). Si en N segundos no se ha podido calcular qué fichero es el que tiene más líneas, el programa terminará indicando que se ha quedado sin tiempo. Por ejemplo: montgros_$ wcTemporizado 5 uno.txt dos.txt .login fichero: uno.txt lineas: 34 montgros_$ wcTemporizado 1 uno.txt dos.txt gigante.txt wcTemporizado: alarma antes de completar el trabajo Para ello escribiremos un programa que hará uso del comando wc, utilizando éste para contar las líneas de cada uno de los ficheros que se le pasan como línea de comandos. Sabemos que invocar a wc de la siguiente forma: wc -l nombre_fichero sirve para contar las líneas que tiene un fichero, y escribe por la salida estándar el total de líneas y el nombre del fichero. Además disponeis de la función: Problemas Tema 4:Gestión de procesos en Unix 10 int filtrarSalida(char *); que, dada una cadena de caracteres (la salida del comando wc), elimina de ella el nombre del fichero y retorna un número entero con el total de líneas. Se os pide que escribais el programa wcTemporizado.c utilizando sólo las llamadas a sistema UNIX vistas en clase. Problema 4.32. Watchfor Queremos implementar un programa en C que dado un username nos avise de cuándo ese usuario entra en el sistema. El funcionamiento deseado es que cada cierto tiempo T (por defecto 180 segundos) el proceso compruebe si el usuario ha entrado en la máquina. Si ha sido así mostrará un mensaje por pantalla y acabará. Si por el contrario, el usuario no ha entrado, el proceso se quedará bloqueado durante T segundos, y pasado ese tiempo volverá a realizar la comprobación. Este programa aceptará como parámetros el username y, opcionalmente, el número de segundos que tiene que esperar entre comprobaciones. Para facilitar la implementación de la comprobación, utilizaremos los comandos del shell who y grep, que se ejecutarán en dos procesos separados, comunicados por una pipe ordinaria. Recordad que el comando who sin parámetros, muestra, por la salida estándar del proceso que lo ejecuta, la lista de usuarios que en ese momento están conectados en la máquina. Este comando devuelve 0 como código de finalización (parámetro de la llamada a sistema exit) si no ha habido ningún error. Por su parte, el comando grep busca el patrón que se le pasa como parámetro en lo que recibe por la entrada estándar del proceso que lo ejecuta. El código de finalización de este comando será 0 si ha encontrado el patrón, y 1 si no lo ha encontrado. Se os pide el código de este programa, en el que deberéis utilizar las llamadas a sistema de Unix vistas en clase. Problema 4.33. Compara los dos códigos siguientes. Indica claramente el resultado de la ejecución de cada uno de ellos explicando el por qué de las diferencias de funcionamiento. Problemas Tema 4:Gestión de procesos en Unix 11 void func(){ signal(SIGALRM, func); } main(){ pid_t pid; status_t status; signal(SIGALRM, func); pid = fork(); switch (pid) { case -1:error(“fork”); exit(1); case 0: while(1){ alarm(5); pause(); write(1,”alarma\n”,7); } break; default:pid=wait(&status); write(2,”fin ejecución\n”,14); } } Código A /* fichero padre */ void func(){ signal(SIGALRM, func); } main(){ pid_t pid; status_t status; signal(SIGALRM, func); pid = fork(); switch (pid){ case -1: error(“fork”); exit(1); case 0: execlp(“hijo”, “hijo”,(char *)0); default: pid=wait(&status); write(2,”fin ejecución\n”,14); } } --------------------------------------/* fichero hijo */ main (){ while (1){ alarm(5); pause (); write(1, “alarma\n”,7); } } Código B Problema 4.34. Se quiere implementar un nuevo comando que permita lanzar un proceso con el tiempo de ejecución limitado. Este comando, que se llamará lanzar, recibirá como parámetros el tiempo máximo de ejecución (medido en segundos), el nombre del ejecutable que se quiere lanzar y sus parámetros (si es que necesita alguno). El comando lanzar se queda a la espera de que el proceso que ha creado acabe. Si pasado el tiempo máximo el proceso no ha acabado, lanzar mostrará por el canal de errores estándar un mensaje de aviso, y enviará al proceso el signal SIGKILL. En la siguiente figura se ven dos ejemplos de uso de este comando. En el primero, se lanza el ejecutable ls, con su opción -l, y se le concede 1 segundo de ejecución. Como el comando ls no finaliza en ese espacio de tiempo, el comando lazar lo mata y da por el canal de errores el mensaje de aviso. En el segundo caso se vuelve a lanzar el comando ls con la opción -l, pero esta vez con 5 segundos de tiempo. En este caso el ejecutable ls acaba a tiempo. Problemas Tema 4:Gestión de procesos en Unix 12 montgros% lanzar 1 ls -l (después de 1 segundos) error: el proceso ls se ha pasado de montgros% lanzar 5 ls -l drwx------ 2 user1 grupo1 1024 Oct 5 drwx------ 2 user1 grupo1 1024 Nov 8 -rw------- 2 user1 grupo1 1024 Dec 6 montgros% tiempo directorio1 directorio2 fichero1 • Escribid en C el código del comando lanzar, usando las llamadas a sistema de Unix que necesitéis. • Suponed ahora que queréis redireccionar la salida estándar del ejecutable que lanzamos mediante el comando lanzar. Sin modificar el código que habéis escrito ni el del ejecutable, ¿es posible hacer esta redirección? ¿Cómo ejecutaríais el comando lanzar? Justifica brevemente tus respuestas. Problema 4.35. Suponed que interesa realizar un cálculo matemático en el menor tiempo posible. Se conocen dos métodos para hacer este cálculo, y se dispone de un ejecutable para cada uno de estos métodos. Estos ejecutables (de nombre calculo1 y calculo2) reciben como parámetros los dos argumentos que necesitan para realizar el cálculo y muestran por salida estándar el resultado del mismo. En la siguiente figura podéis ver el código de uno de estos ejecutables (el otro es igual, cambiando la llamada a la función metodo1, por la llamada a metodo2). /* calculo 1 */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> int metodo1(); /*prototipo de la funcion*/ main (argc, argv) int argc; char *argv[]; { int arg1, arg2, res; char buf[80]; arg1=atoi(argv[1]); arg2=atoi(argv[2]); res=metodo1(arg1,arg2); sprintf(buf,”%d”,res); res=write (1,buf,strlen(buf); if (res<0){ sprintf(buf,”%s”,strerror(errno)); write(2,buf, strlen(buf)); exit(1); } } Para averiguar cuál de los dos cálculos acaba antes, se ha pensado en implementar un programa (evaluador) que cree 2 procesos, y que cada uno de estos procesos lance uno de los ejecutables. Problemas Tema 4:Gestión de procesos en Unix 13 Este programa evaluador usará una pipe ordinaria para recibir el resultado del cálculo, y avisará cada 30 segundos al usuario mientras no obtenga este resultado (este aviso será un mensaje por salida estándar). Una vez haya finalizado el proceso más rápido, el proceso evaluador deberá matar a los procesos hijos que todavía no hayan acabado la ejecución y finalizar, mostrando por salida estándar el resultado del cálculo, el tiempo que se ha tardado en obtenerlo y cuál de los métodos ha sido el más rápido. Se os pide el código del programa evaluador, suponiendo que disponéis de los ejecutables calculo1 y calculo2. Problema 4.36. Di cuál crees que será el resultado de ejecutar cada uno de los siguientes códigos: a) b) #include <signal.h> #include <unistd.h> #include <unistd.h> void mi_rutina(){ signal(SIGALRM,mi_rutina); write(1,”ALARMA!”,7); } main(){ int ret; signal(SIGALRM,mi_rutina); alarm(5); ret=fork() if (ret<0) {error();exit(1);} pause(); if (ret==0) write(1,”Soy el hijo\n”,12); else write(1,”Soy el padre\n”,13); } main(){ int ret; int fd[2]; char buff[20]; pipe(fd); ret=fork(): switch (ret){ case -1: error();exit(1);break; case 0: close(0);dup(fd[0]); execlp(“hijo”,”hijo”,(char *)0); default: strcpy(buff,”Buenos dias!”); close(fd[0]); ret=write(fd[1],buff,strlen(buff)); if (ret<0){ error();exit(1);} close(fd[1]); } } /* fichero hijo */ main(){ int ret; char buff[80]; ret=read(0,buff,sizeof(buff)); while (ret>0){ buff[ret]=’\0’; ret=write(1,buff,strlen(buff)); if (ret<0){ error();exit(1);} ret=read(0,buff,sizeof(buff)); } if (ret<0){ error();exit(1);} } Problema 4.37. Se quiere implementar una aplicación que realice N cálculos. Esta aplicación dejará el resultado de todos esos cálculos, junto con el tiempo que ha empleado para cada uno de ellos, en un Problemas Tema 4:Gestión de procesos en Unix 14 único fichero. Se quiere además que los resultados aparezcan ordenados (por ejemplo: el resultado del cálculo 2 debe aparecer después del resultado del cálculo 1 aunque haya acabado antes). La aplicación tiene que estar formada por dos programas diferentes. Uno de ellos será ejecutado por un único proceso (organizador), y es el encargado de crear todos los procesos que ejecutarán realmente los cálculos. Creará un proceso para cada uno de los cálculos pasándole como parámetro el identificador del cálculo que debe realizar (este identificador será simplemente un entero de 0 a N-1). Además el organizador es el encargado de que los resultados aparezcan ordenados en el fichero. Para ello, avisará mediante un signal de tipo SIGUSR1 al proceso que tiene el turno para escribir en el fichero de resultados. El otro programa es el que ejecutará cada uno de los procesos calculadores. Este código recibe como parámetro el cálculo que debe realizar y ejecuta la función cálculo, que podéis suponer ya implementada, y que tiene el siguiente interfaz: int cálculo(int id_calculo); Esta función devuelve el resultado del cálculo que identifica el parámetro id_calculo. Además deberá contabilizar el tiempo que ha invertido en hacer el cálculo. Cuando tenga el resultado deberá comprobar si ya es su turno para escribir el resultado, y si no es así esperar hasta que le toque. Cuando sea su turno mostrará por salida estándar una línea con el siguiente formato: id_calculo:resultado:tiempo de ejecución Se pide la implementación del código de los dos programas. Problema 4.38. Escribid los siguientes programas en C, usando las llamadas a sistema de Unix vistas en clase: ping_pong: este programa recibe como parámetro un valor inicial, que le pasará al proceso ping como parámetro (para que éste inicialize el cálculo). Creará dos pipes ordinarias para que sus procesos hijos se comuniquen entre sí, y a continuación creará dos procesos: uno ejecutará el programa ping y otro ejecutará el proceso pong. Una vez creados los dos procesos hijos, deberá esperar 30 segundos y a continuación enviar un signal de tipo SIGINT al proceso ping, para que éste acabe la ejecución, y esperar a que los dos hijos acaben. Por último, deberá mostrar por salida estándar el resultado del cálculo, que será el código de finalización del proceso ping. ping: este programa recibe como parámetro el valor para inicializar el cálculo. A continuación, ejecuta un bucle que acabará cuando reciba el signal de tipo SIGINT. En este bucle, multiplica por 2 ese cálculo, lo muestra por salida estándar, y se queda a la espera de recibir por entrada estándar el cálculo modificado. Cuando reciba el signal de tipo SIGINT acabará inmediatamente, devoviendo como código de retorno el cálculo tal y como estaba en ese momento. pong: este programa no recibe ningún parámetro. Ejecuta un bucle que consiste en recibir por entrada estándar un valor, y escribir por salida estándar ese valor multiplicado por 3. El bucle acabará cuando la lectura de entrada estándar indique final de fichero. Problema 4.39. Escribid dos programas en C, usando las llamadas a sistema de Unix, que se comporten de la siguiente manera. • El primer programa (escritor_padre) recibirá como parámetro el nombre de un fichero Problemas Tema 4:Gestión de procesos en Unix 15 (nom_fich) y un entero (tiempo) que representará el tiempo que debe durar la escritura. Este programa deberá crear otro proceso que ejecutará el segundo programa (escritor_hijo). A partir de este momento, mientras no se acabe el tiempo recibido como parámetro, deberá repetir la siguiente secuencia: 1. Escribir en nom_fich una línea con su pid 2. Avisar a su hijo de que le toca escribir 3. Y esperar a que le vuelva a tocar el turno de escritura. Cuando se acabe el tiempo, avisará a su hijo de que tiene que acabar la ejecución, y mostrará por salida estándard un mensaje con el número de líneas que se han escrito en el fichero. • El otro programa (escritor_hijo) mientras no reciba el aviso de fin de ejecución repetirá la siguiente secuencia de ejecución 1. Esperar a que le toque el turno 2. Escribir en nom_fich una línea con su pid 3. Avisar al padre de que le toca escribir Cuando el padre le avise de que se acaba la ejecución este proceso acabará indicando como código de retorno (valor del exit) el número de líneas que ha escrito en el fichero. Problema 4.40. Dado el código del programa prog: /* PROGRAMA prog */ #include <unistd.h> #include <signal.h> main(int argc, char *argv[]){ int pid1, pid2, ppid, mipid, valor; int fd[2]; char msg[80]; pid1 = fork(); pipe(fd); pid2 = fork(); A if (pid2 == 0) { close (fd[1]); ret = read(fd[0], &ppid, sizeof(int)); kill (ppid, SIGKILL); close (fd[1]); } else { close (fd[0]); mipid=getpid(); write(fd[1], &mipid, sizeof(int)); close (fd[1]); wait(&valor); } strcpy(msg, ”Objetivo cumplido!”); write(1,msg,strlen(msg)); } 1. ¿Cuántos procesos tendremos en ejecución en el punto A? Problemas Tema 4:Gestión de procesos en Unix 16 2. ¿Cuántas pipes crea este programa? Representa el estado de la tabla de ficheros abiertos y de la tabla de canales de cada proceso, suponiendo que el programa se lanza sin redireccionar la entrada-salida y que todos los procesos se encuentran en el punto A. 3. ¿Cuántas veces aparecerá por salida estándard el mensaje “Objetivo cumplido!”? 4. Describe brevemente lo que hace este código. Problema 4.41. Dado el siguiente código, se pretende que al final de su ejecución la primera línea de un fichero llamado pids contenga el pid del proceso hijo, y la segunda línea contenga el pid del proceso padre. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: /* miprog */ #include <unistd.h> #include <fcntl.h> main(){ int ret; int fd_pids; char buffer[50]; ret = fork(); if (ret == 0){ sprintf(buffer,”%d\n”, write(fd_pids, buffer, } else { sprintf(buffer,”%d\n”, write(fd_pids, buffer, } } getpid()); strlen(buffer)); getpid()); strlen(buffer)); Suponiendo que el programa se lanza sin redireccionar ningún canal estándar, contestad a las siguientes preguntas, justificando vuestras respuestas: 1. En qué punto o puntos del código añadiríais la llamada a sistema open y con qué parámetros 2. ¿Creéis que es necesario añadir alguna llamada a sistema más para asegurar que el funcionamiento sea el esperado? Si es así, indica cuál (o cuales), en qué punto(s) y con qué parámetros. Si por el contrario, lanzáramos el programa de la siguiente manera: angren% miprog > pids 3. ¿Deberíais modificar de alguna manera vuestra propuesta para que su funcionamiento fuera el mismo? ¿Por qué? Problemas Tema 4:Gestión de procesos en Unix 17 Problema 4.42. Se quiere implementar un programa en C que use las llamadas a sistema de Unix. El comportamiento de este programa debe ser el siguiente: recibirá como parámetro el nombre de un fichero fuente y el nombre de un fichero destino, e intentará copiar en menos de 30 segundos el contenido del fichero fuente en el fichero destino. Para hacer la copia del fichero se quiere implementar la estructura que aparece en la siguiente figura. El programa inicial creará dos procesos hijos. Cada hijo ejecutará el programa prog_hijo que también aparece en la figura. La comunicación entre los procesos se hará mediante pipes ordinarias (pipe1 y pipe2). El proceso padre enviará al primer hijo a través de pipe1 el contenido del fichero fuente. El primer hijo reenviará el contenido que reciba al segundo hijo a través de pipe2. Y será el segundo hijo el encargado de escribir en el fichero destino el contenido del fichero fuente. El proceso padre es el encargado de controlar el tiempo. Si al cabo de los 30 segundos no se ha acabado de hacer la copia deberá matar a sus dos hijos, sacar un mensaje de error por el canal de salida de errores estándar y eliminar el fichero destino. fuente /* prog_hijo */ #include <unistd.h> proceso padre pipe 1 destino proceso hijo 1 pipe 2 main(){ int ret; char buffer[80]; ret = read(0, buffer, 80); while (ret > 0){ write(1, buffer, ret); ret = read(0, buffer, 80); } } proceso hijo 2 Problemas Tema 4:Gestión de procesos en Unix 18