· web viewesa implementación antes funcionaba bien, pero hoy en la mayoría de los linux...

60
Clase 9: Linux: Señales Atrapar una señal (ctrl-C) #include <signal.h> #include <stdio.h> #include <stdlib.h> int brk = 0; /* variable externa que cuenta los ctrl-C */ /* handler de la interrupcion */ void ctrl_c() { brk++; fprintf(stderr, "Ay numero: %d!\n", brk); // exit(0); /* si quieren morir a la primera: descomentar */ } main() { char c; signal(SIGINT, ctrl_c); while((c=getchar()) != EOF) putchar(c); } Dormir un rato (sleep) Programa que atrapa interrupción de reloj para implementar una función sleep: #include <signal.h> #include <stdio.h> #include <stdlib.h> void clock() { printf("ring!\n"); } void msleep(int n) {

Upload: vuhanh

Post on 30-Apr-2018

217 views

Category:

Documents


0 download

TRANSCRIPT

Clase 9: Linux: SeñalesAtrapar una señal (ctrl-C)

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

 

int brk = 0; /* variable externa que cuenta los ctrl-C */

 

/* handler de la interrupcion */

void ctrl_c() {

brk++;

fprintf(stderr, "Ay numero: %d!\n", brk);

// exit(0); /* si quieren morir a la primera: descomentar */

}

 

main()

{

char c;

 

signal(SIGINT, ctrl_c);

while((c=getchar()) != EOF)

putchar(c);

}

Dormir un rato (sleep)Programa que atrapa interrupción de reloj para implementar una función sleep:

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

 

void clock() {

printf("ring!\n");

}

 

void msleep(int n) {

signal(SIGALRM, clock); /* una buena implementacion guarda el

handler original */

alarm(n);

pause();

}

 

main()

{

char c;

 

msleep(2);

printf("ok2\n");

msleep(1);

printf("ok1\n");

}

Timeouts (longjmp/setjmp)Ahora usamos interrupciones de reloj para implementar timeouts. La función call_with_timeout llama una función y espera su retorno un máximo de segundos. Si todo estuvo bien, retorna lo mismo que la función. Si hubo un timeout, retorna -1.

Un programa de prueba es:

#include <stdio.h>

#include <signal.h>

 

/*

* Prueba de la implementacion de call_with_timeout

* tambien se asegura que el handler de reloj vuelva al antiguo

*/

 

int ls() {

/* return system("ls -R /usr | less"); */ /* esto si quieren ver un comando largo

ejecutando */

sleep(5); /* esta funcion demora 5 segundos, asi que un timeout de 1 debiera

bastar */

return(0);

}

 

main() {

printf("llamando a ls con timeout (3 veces)\n");

printf("debiera ser 0, retorna: %d\n", call_with_timeout(ls, 10));

printf("debiera ser -1, retorna: %d\n", call_with_timeout(ls, 1));

printf("debiera ser -1, retorna: %d\n", call_with_timeout(ls, 1));

 

 

/* Esto es para probar que el hadlr de reloj volvió al default */

printf("debiera morir con alarm clock\n");

kill(getpid(), SIGALRM);

sleep(1);

}

Una primera implementación, usando setjmp/longjmp:

#include <signal.h>

#include <setjmp.h>

 

/*

* Ejemplo de uso de signal y longjmp para implementar un timeout

* Esta implementación usualmente solo funciona para la primera invocación

*/

 

static jmp_buf ring;

 

void clock(int sig)

{

longjmp(ring, 1);

}

 

int call_with_timeout(int (*f)(), int timeout)

{

int res;

void (*hdlr)();

 

hdlr = signal(SIGALRM, clock);

 

if(setjmp(ring) != 0) {

signal(SIGALRM, hdlr); /* si llegamos aquí es que ocurrió el timeout */

return(-1);

}

 

alarm(timeout); /* programamos el timer */

res = f();

alarm(0); /* apagamos el timer */

 

signal(SIGALRM, hdlr);

return(res);

}

Esa implementación antes funcionaba bien, pero hoy en la mayoría de los linux modernos la función signal se implementa con una semántica diferente: el handler de interrupciones se ejecuta con la interrupción que atrapa deshabilitada (es para evitar que el handler sea interrumpido por la misma interrupción). Al retornar del handler, la interrupción se habilita de nuevo. El problema es que, al hacer longjmp, nuestro handler nunca retorna.

Linux permite manejar las máscaras de interrupciones bastante al detalle (ver man sigaction), pero usaremos solo un flag muy simple que evita el comportamiento de bloquear la interrupción atrapada. Para ello, no usaremos la función signal, sino su reemplazo más moderno (pero más complejo): sigaction.

#include <signal.h>

#include <setjmp.h>

#include <stdio.h>

 

/*

* Ejemplo de uso de signal y longjmp para implementar un timeout

* Uso sigaction para borrar el bloqueo de la señal

*/

 

static jmp_buf ring;

 

void clock(int sig)

{

longjmp(ring, 1);

}

 

int call_with_timeout(int (*f)(), int timeout)

{

int res;

struct sigaction sig, osig;

 

sig.sa_handler = clock;

sig.sa_flags = SA_NODEFER;

sigaction(SIGALRM, &sig, &osig);

 

if(setjmp(ring) != 0) {

sigaction(SIGALRM, &osig, NULL);

return(-1);

}

 

alarm(timeout);

res = f();

alarm(0);

 

sigaction(SIGALRM, &osig, NULL);

return(res);

}

Clase 10: Linux: ProcesosCrear un proceso (fork)

La única forma de crear un proceso en Linux es fork().

Este programa simplemente crea una copia de si mismo. Para distinguir el original del clon, el truco es mirar lo que retorna la función fork: el original recibe el pid del clon (hijo), en cambio el clon recibe 0 (cero):

#include <stdio.h>

#include <stdlib.h>

 

/*

* Ejemplo trivial de un fork

*/

 

main()

{

int pid;

 

printf("voy al fork\n");

pid = fork();

if(pid < 0) {

fprintf(stderr, "falla fork!\n");

exit(1);

}

 

printf("ahora somos 2 clones\n");

 

if(pid == 0) { /* soy el hijo */

// sleep(1);

printf("yo soy el clon!\n");

exit(1);

}

 

printf("yo soy el original!\n");

}

Pueden ejecutar varias veces este programa y ver que la salida es no determinística ahora. Si des-comentan el sleep en el hijo, pueden verlo escribir sus mensajes después que su padre ya murió y el shell ya les dió el prompt (%).

Ejecutar un archivo ejecutable (exec)Modificamos un poco el ejemplo anterior para que ahora ejecute el comando ls, que está en el archivo /bin/ls :

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

 

/*

* Ejemplo trivial de un fork/exec

*/

 

main()

{

int pid;

 

printf("voy al fork\n");

pid = fork();

if(pid < 0) {

fprintf(stderr, "falla fork!\n");

exit(1);

}

 

printf("ahora son 2 clones\n");

 

if(pid == 0) { /* soy el hijo */

printf("yo soy el clon!\n");

execl("/bin/ls", "ls", "-l", 0);

fprintf(stderr, "nunca debio ocurrir!\n");

exit(1);

}

 

printf("yo soy el original!\n");

}

Esperar que un hijo muera (wait)Obviamente sería mejor que el padre esperara al hijo para evitar que salgan lineas despues que el comando ya terminó. Para eso usamos wait:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

 

/*

* Ejemplo trivial de un fork/exec/wait

*/

 

main()

{

int pid;

int status;

 

printf("voy al ls\n");

printf("---------------\n");

fflush(stdout); /* Para asegurarme que lo anterior ya salga por la salida

* prueben comentando el fflush y redirijan la salida a un

archivo.

*/

 

pid = fork();

if(pid < 0) {

fprintf(stderr, "falla fork!\n");

exit(1);

}

 

if(pid == 0) { /* soy el hijo */

execl("/bin/ls", "ls", "-l", 0);

fprintf(stderr, "nunca debio ocurrir!\n");

exit(1);

}

 

waitpid(pid, &status, 0); /* espera la muerte del proceso pid */

printf("---------------\n");

}

Manejar propiedades de un hijoEntre el fork y el exec, se pueden manipular las característica que el hijo va a heredar: directorio actual, señales ignoradas, entrada y salida estándar, etc.

Este ejemplo hace que el hijo ignore el ctrl-C. Ustedes pueden ejecutarlo, matarlo con ctrl-C y verán que el padre muere, pero el hijo sigue ejecutando. Linux maneja las señales que van desde el teclado y le envía ctrl-C a todos los procesos que están asociados con esa ventana. Los

procesos heredan de su padre la ventana a la que se asocian. Si quieren ser independientes, deben separarse del grupo (ver la función setpgroup).

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <signal.h>

 

/*

* Ejemplo trivial de un fork/exec/wait

* cambiamos al hijo para protegerlo de ctrl-C

*/

 

main()

{

int pid;

int status;

 

printf("voy al ls\n");

printf("---------------\n");

fflush(stdout);

 

pid = fork();

if(pid < 0) {

fprintf(stderr, "falla fork!\n");

exit(1);

}

 

if(pid == 0) { /* soy el hijo */

printf("yo soy el clon!\n");

signal(SIGINT, SIG_IGN);

chdir("/"); /* vamos a la raiz del sistema */

execl("/bin/ls", "ls", "-l", 0);

fprintf(stderr, "nunca debio ocurrir!\n");

exit(1);

}

 

waitpid(pid, &status, 0);

printf("---------------\n");

}

Deberes de un padreEn Linux, un padre es completamente independiente de sus hijos, pero debe cumplir con una responsabilidad fundamental: enterrarlos cuando mueren. Para esto, basta con que invoque la función wait en cualquier momento, pero es indispensable que sea informado que su hijo murió para que Linux pueda deshacerse por completo de él. Un hijo muerto pero no enterrado (cuyo padre nunca ha sido informado de su muerte vía wait) es un Zombie: un proceso que ya no existe pero no puede reciclarse por completo.

Siempre deben evitar generar Zombies en el sistema y es su responsabilidad hacer wait de todos sus hijos muertos.

En este ejemplo, el programa crea 10 procesos de corta duración y se demora en esperarlos. Mientras el padre duerme, ustedes pueden ejecutar el comando “ps” que lista los procesos del sistema y verán los Zombies aparecer.

#include <stdio.h>

#include <stdlib.h>

 

int vars[10];

 

int create_proc(int i) {

int pid;

 

pid = fork();

if(pid == 0) { /* proceso hijo de corta duracion */

vars[i] = 1;

printf("hijo %d, pid=%d,var_i=%d\n", i, getpid(), vars[i]); /* getpid()

retorna mi pid */

exit(0); /* que pasa si no pongo este exit? (prueben!) */

}

 

return pid;

}

 

main() {

int pids[10];

int i;

 

for(i=0; i < 10; i++)

vars[i] = 0;

for(i=0; i < 10; i++)

pids[i] = create_proc(i);

sleep(20); /* pueden mirar los zombies durante un rato */

for(i=0; i < 10; i++) /* ahora los entierro para que descansen en paz */

waitpid(pids[i], NULL, 0);

 

for(i=0; i < 10; i++) /* Esto muestra que mis hijos heredan mis variables pero

no me las pueden modificar a mi */

printf("%d, ", vars[i]);

printf("\n");

sleep(20);

}

Clase 11: Linux: E/SNos queda aprender a modificar la E/S estándar de un proceso. Para ello, se definen los descriptores de archivos (file descriptors) que son enteros pequeños, asignados en orden. El descriptor 0 es la entrada estándar, el 1 es la salida estándar y el 2 es la salida de errores. Las funciones oficiales de Linux son read y write para leer y escribir datos. Un ejemplo es una nueva versión del comando copy que hicimos al comienzo del curso, pero en vez de usar las funciones de biblioteca, usamos las primitivas de Linux directamente:

#include <stdlib.h>

 

#define BUF_SIZ 1024

 

main() {

char buf[BUF_SIZ];

 

int cnt;

 

while((cnt=read(0, buf, BUF_SIZ)) > 0) /* read retorna los bytes leidos o 0 si es

EOF */

if(write(1, buf, cnt) != cnt)

exit(1); /* algo falló */

 

exit(0);

}

La forma más habitual de obtener un descriptor nuevo es con la función open, que permite abrir un archivo para leerlo o escribirlo.

PipesUn caso particular es el uso de pipes. Veamos una función que trata de hacer lo mismo que:

% ls | more

Debemos hacer dos fork/exec y conectarlos con un pipe. La lógica del código es:

1. Crear el pipe (arreglo de dos descriptores)2. fork hijo 1 que hace:

I. asignar su salida al pipeII. exec de ls

3. fork hijo 2 que hace:I. asignar su entrada al pipeII. exec de more

4. padre cierra ambos extremos del pipe5. espera la muerte de ambos (y se preocupa de cómo murió ls)

#include <stdlib.h>

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

 

/*

* Este programa ejecuta el comando ls|more en el directorio / (raiz)

* Y luego se despide y termina

*/

 

main() {

int lspid, morepid, pid;

int status = 0;

int fds[2];

 

if(pipe(fds) < 0) { /* Antes del fork: crear el pipe comun */

fprintf(stderr, "fallo el pipe\n");

exit(1);

}

printf("ls de / es:\n======================\n");

 

lspid = fork();

if(lspid < 0) {

fprintf(stderr, "fallo el fork\n");

exit(1);

}

 

if(lspid == 0) { /* ls */

/* Cerrar el extremo read del pipe que no voy a usar */

close(fds[0]);

/* Asigno: 1 (stdout) = fds[1] (lado de escritura del pipe) */

close(1); dup(fds[1]);

/* Cerrar la copia que me queda sobre el pipe o no tendre' EOF */

close(fds[1]);

 

chdir("/");

execl("/bin/ls", "ls", "-l", 0); /* Ponerle -R para probar sigpipe */

fprintf(stderr, "Fallo el exec\n");

exit(-1);

}

 

morepid = fork();

if(morepid < 0) {

fprintf(stderr, "fallo el fork\n");

exit(1);

}

 

if(morepid == 0) { /* more */

/* Cerrar el extremo write del pipe que no voy a usar */

close(fds[1]);

/* Asigno: 0 (stdin) = fds[0] (lado de lectura del pipe) */

close(0); dup(fds[0]);

/* Cerrar la copia que me queda sobre el pipe o no tendre' EOF */

close(fds[0]);

 

execl("/bin/more", "more", 0);

fprintf(stderr, "Fallo el exec\n");

exit(-1);

}

 

/* Como padre comun, cierro el pipe, ambos extremos (yo no lo uso) */

close(fds[0]); close(fds[1]);

 

/* como buen padre, espero la muerte de todos mis hijos */

/* while((pid = wait(&status)) != -1); */

/* O los puedo esperar explicitamente (si tuviera otros) */

if(waitpid(morepid, &status, 0) != morepid) {

fprintf(stderr, "fallo el wait2\n");

perror("wait2");

exit(-1);

}

 

if(waitpid(lspid, &status, 0) != lspid) {

perror("wait1");

exit(-1);

}

 

if( !WIFEXITED(status) ) {

printf("ls anormal\n");

if( WIFSTOPPED(status) ) {

printf("Esta detenido\n");

if(WSTOPSIG(status) == SIGSTOP) printf("Con SIGSTOP\n");

}

else if( WIFSIGNALED(status) ) {

printf("Murio con signal...\n");

if( WTERMSIG(status) == SIGPIPE ) printf("Murio con sigpipe\n");

}

}

else { /* Normal */

printf("ls muere normalmente con exit code = %d\n", WEXITSTATUS(status));

}

 

 

printf("========================\n");

}

Si ejecutan este programa mostrando todos los archivos, ls muere normalmente y more también al recibir EOF por el pipe. Si terminan a more antes de tiempo (con 'q' desde el teclado) ls muere con sigpipe.Clase 12: Linux: Ejemplos con pipesSe trata de hacer una versión simple de la función de biblioteca system (ver man system):#include <stdio.h>

#include <sys/wait.h>

 

int system(char *cmd)

{

int pid;

int status;

 

if( (pid=fork()) < 0 )

return(-1);

 

if( pid == 0 ) /* Es el hijo */

{

execl("/bin/sh", "sh", "-c", cmd, NULL);

exit(127);

}

 

/* Es el padre: debemos esperar la muerte del hijo */

 

waitpid( pid, &status, 0 );

return(WEXITSTATUS(status));

}

 

/* ejemplo de uso */

main()

{

int ret;

 

ret = system("ls | more");

printf("done: %d\n", ret);

}

Otro ejemplo: la función popen de la biblioteca, pero a nivel de descriptores de bajo nivel:#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <fcntl.h>

#define READ 0

#define WRITE 1

 

int popen_id;

 

int fdpopen(char *cmd, int mode)

{

int fds[2];

 

if(pipe(fds) < 0) return -1;

 

if((popen_id=fork()) == 0) /* soy el hijo */

{

/* soy cmd */

if(mode == READ) { /* yo voy a escribir (ej: ls) */

close(fds[READ]);

close(1); dup(fds[WRITE]);

close(fds[WRITE]);

} else { /* yo voy a leer (ej: wc) */

close(fds[WRITE]);

close(0); dup(fds[READ]);

close(fds[READ]);

}

execl("/bin/sh", "sh", "-c", cmd, NULL);

exit(127);

}

 

if(popen_id < 0) return -1;

 

/* Este es el padre */

/* Cerrar los fds que no vamos a usar */

close((mode==READ) ? fds[WRITE] : fds[READ]);

 

return(fds[mode]);

}

 

int fdpclose(int fd) {

int status;

 

close(fd);

waitpid(popen_id, &status, 0);

return(status);

}

 

#define BUF_SIZ 1024

 

main()

{

int fd;

char buf[BUF_SIZ];

int cnt;

 

/* abrimos ls en lectura */

fd = fdpopen("ls", READ);

if(fd < 0) {

fprintf(stderr, "No pude abrir ls!\n");

perror("open");

exit(1);

}

 

/* mostramos la salida de ls */

while((cnt=read(fd, buf, BUF_SIZ)) > 0)

if(write(1, buf, cnt) != cnt)

exit(1);

 

fdpclose(fd);

printf("========================\n ingrese texto...\n");

 

fd=fdpopen("wc", WRITE);

 

/* copiamos nuestra entrada a wc */

while((cnt=read(0, buf, BUF_SIZ)) > 0)

if(write(fd, buf, cnt) != cnt)

exit(1);

 

fdpclose(fd);

 

printf("Fin del Mundo-------\n");

}

Clase 13: Linux: Sistema de archivosVersión recursiva de una función find que recorre un árbol de archivos (muestra uso de readdir y sus amigos):#include <stdlib.h>

#include <string.h>

#include <sys/stat.h>

#include <dirent.h>

#include <stdio.h>

 

/*

* Find:

* Es mejor recorrer alargando el pathname que haciendo chdir

* porque chdir("..") no siempre me lleva a donde yo creo por culpa de

* los links

*/

 

void find(char *dir)

{

DIR *fdir;

struct dirent *ent;

struct stat st;

char *fn;

 

/* Pregunto por el directorio o archivo */

if( stat( dir, &st ) < 0 ) /* o lstat si no quiero seguir los links a dir */

return;

 

printf("%s\n", dir);

 

/* Si no es un directorio, no hago nada mas */

if( !S_ISDIR(st.st_mode) ) {

return;

}

 

/* Si es directorio lo recorro */

if( (fdir = opendir(dir)) == NULL)

return;

 

/* ent es el par nombre/i-node recorriendo el directorio */

for(ent = readdir(fdir); ent != NULL; ent = readdir(fdir))

{

/* Saltamos . y .. que siempre estan */

if( strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)

continue;

 

/* llamo a find con todos los elementos del directorio */

/* fn = dir + "/" + ent->d_name; */

fn = (char *) malloc(strlen(dir)+strlen(ent->d_name)+2);

strcpy(fn, dir);

strcat(fn, "/");

strcat(fn, ent->d_name);

 

find( fn );

free(fn);

}

closedir(fdir);

}

 

main()

{

/* esto recorre el arbol a partir del dir actual */

find( "." );

}

Clase 17: Threads: Monitores y CondicionesTbox con monitores

Cuando las condiciones para la espera son más complejas que simplemente contar buffers, usamos condiciones. En pthreads los monitores como tales no existen, pero se implementan con un mutex común y condiciones que permiten quedarse bloqueados. Las condiciones solo pueden usarse dentro de un mutex común que ya está tomado. Aunque es un mal ejemplo, podemos implementar el administrador de buffers con múltiples productores/consumidores para mostrar como funcional. Como ahora no tenemos los semáforos protegiéndonos, debemos preguntar explícitamente por las condiciones de todos los buffers vacíos o todos los buffers llenos. Para eso, agregamos dos variables (llenos y vacios) que siempre suman N:

tbox-cond.h:

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

 

#define NBUFS 1000

 

typedef struct {

char buf[NBUFS];

pthread_cond_t vacios, llenos; /* condiciones de espera */

pthread_mutex_t mutex; /* mutex compartido */

int in, out;

int nvacios, nllenos;

} BOX;

 

BOX *createbox();

void putbox(BOX *b, char c);

char getbox(BOX *b);

tbox-cond.c:

#include <stdio.h>

#include "tbox-cond.h"

 

BOX *createbox()

{

BOX *b;

 

b = (BOX *)malloc(sizeof(BOX));

pthread_mutex_init(&b->mutex, NULL);

pthread_cond_init(&b->llenos, NULL);

pthread_cond_init(&b->vacios, NULL);

b->in = b->out = 0;

b->nllenos = 0; b->nvacios = NBUFS;

return(b);

}

 

/* Implementacion para un Productor y un Consumidor */

void putbox(BOX *b, char c)

{

pthread_mutex_lock(&b->mutex);

while(b->nvacios == 0)

pthread_cond_wait(&b->vacios, &b->mutex);

b->buf[b->in] = c;

b->in = (b->in+1)%NBUFS;

b->nllenos++; b->nvacios--;

pthread_cond_signal(&b->llenos);

pthread_mutex_unlock(&b->mutex);

}

 

char getbox(BOX *b)

{

char c;

 

pthread_mutex_lock(&b->mutex);

while(b->nllenos == 0)

pthread_cond_wait(&b->llenos, &b->mutex);

c = b->buf[b->out];

b->out = (b->out+1)%NBUFS;

b->nllenos--; b->nvacios++;

pthread_cond_signal(&b->vacios);

pthread_mutex_unlock(&b->mutex);

return(c);

}

Si se fijan, siempre partimos por tomar el mutex compartido. Luego vemos si se cumple la condición que necesitamos para poder ejecutar. Si no se cumple, nos bloqueamos en un wait de una condición. Esa llamada siempre es bloqueante. Al contrario de los semáforos, las condiciones no tienen un estado. Las operaciones no las modifican, simplemente sirven para darle un nombre al punto de sincronización. Al esperar la condición, debo especificar el mutex que la protege, de modo que la llamada me bloquea y libera el mutex. Si se fijan, no hacemos: if(b->nllenos == 0)

pthread_cond_wait(&b->llenos, &b->mutex);

sino que usamos un while. Esto es obligatorio, porque siempre debo volver a verificar la condición antes de seguir ejecutando mi código. Para despertar de un wait, otro thread debe invocar un signal sobre esa misma condición. En este caso, lo hace la función putbox es la que me despierta al hacer signal de la condición llenos. Esa función despierta a un thread que esté esperando. Si no hay ninguno, no hace nada. Como usamos la estructura clásica de monitores (con un mutex compartido) no estamos siendo óptimos y no permitimos paralelismo entre productores y consumidores. Pthreads lo permite y podríamos tener dos mutexes (pruébenlo como tarea).

En algunos casos más avanzados, podemos querer despertar a todos los threads que están esperando en una condición (no es el caso del ejemplo). Para eso, existe pthread_cond_broadcast().

Semáforo con monitoresComo otro ejemplo, podemos implementar un semáforo usando condiciones. Tendrá las funciones P() (wait) y V() (post) como siempre:

jsem.h:

#include <pthread.h>

 

typedef struct {

int val;

pthread_cond_t paso; /* Condicion para esperar entrar al semáforo */

pthread_mutex_t mutex;

} JSEM;

 

JSEM *jsem_init(int);

 

void P(JSEM *s);

void V(JSEM *s);

jsem.c:

#include "jsem.h"

 

JSEM *jsem_init(int val) {

JSEM *s;

 

s = (JSEM *)malloc(sizeof(JSEM));

if(s == NULL) return NULL;

 

s->val = val;

pthread_cond_init(&s->paso, NULL);

pthread_mutex_init(&s->mutex, NULL);

return s;

}

 

void P(JSEM *s) {

pthread_mutex_lock(&s->mutex);

while(s->val == 0) /* Nunca un if! */

pthread_cond_wait(&s->paso, &s->mutex);

 

s->val--;

pthread_mutex_unlock(&s->mutex);

}

 

void V(JSEM *s) {

pthread_mutex_lock(&s->mutex);

s->val++;

pthread_cond_signal(&s->paso); /* siempre libero a uno solo */

pthread_mutex_unlock(&s->mutex);

}

Pueden probar los productores/consumidores con esta implementación de semáforos. Todos los códigos de threads vistos hasta aquí se los dejo en tbox.tgzClase 19: Threads: capturadora de videoUn ejemplo que muestra bien la utilidad de las condiciones es un administrador de buffers que almacena frames de video capturados de una cámara (productor) y que se comparten entre múltiples clientes que los despliegan (consumidores). Es un esquema de un productor y múltiples consumidores y un número fijo de buffers en memoria.

La cámara genera un nuevo frame y busca un buffer en memoria donde almacenarlo. Esto lo pide con una función:

void PutFrame(TIME tstamp, char *buf);

Los threads de los clientes buscan un frame entre los buffers y lo marcan para usarlo un rato. Después de terminar con él, lo liberan y piden otro. Esto lo hacen con dos funciones:

/* Busca un buffer */

int GetFrame(TIME tstamp);

/* Libera un frame */

void FreeFrame(int i)

Sin embargo, debemos respetar que la cámara no puede usar un buffer que esté siendo ocupado por un cliente. Por otro lado, el cliente, cuando pide un frame, sólo le sirve uno más nuevo que el último que recibió. Para estas condiciones usamos, para cada frame, un contador de referencias (ref_cnt) que cuenta el número de clientes que tiene “marcado” ese frame y un timestamp que marca la hora absoluta en que fue generado (suponemos que es un número monótonamente creciente).

La implementación con monitores y condiciones es:

/* Busca un buffer con ref_cnt == 0 */

void PutFrame(TIME tstamp, char *buf) {

int i;

 

pthread_mutex_lock( &(master_lock));

for(;;) {

for(i=0; i<NBUFS; i++)

if(Buffers[i].ref_cnt == 0)

break;

if(i==NBUFS) pthread_cond_wait(&free_frame, &master_lock);

else break;

}

 

Buffers[i].tstamp = tstamp;

Buffers[i].buf = buf;

pthread_cond_broadcast(&new_frame);

pthread_mutex_unlock( &(master_lock));

}

 

/* Busca un buffer con timestamp > tstamp */

int GetFrame(TIME tstamp) {

int i;

 

pthread_mutex_lock( &(master_lock));

for(;;) {

for(i=0; i<NBUFS; i++)

if(Buffers[i].tstamp > tstamp)

break;

if(i==NBUFS) pthread_cond_wait(&new_frame, &master_lock);

else break;

}

Buffers[i].ref_cnt++;

pthread_mutex_unlock( &(master_lock));

return i;

}

 

/* Libera un frame */

void FreeFrame(int i) {

pthread_mutex_lock( &(master_lock));

Buffers[i].ref_cnt--;

if(Buffers[i].ref_cnt == 0) pthread_cond_signal(&free_frame);

pthread_mutex_unlock( &(master_lock));

}

Estudien el código. Vean porqué hacemos cond_signal en un lugar y cond_broadcast en otro.

Clase 20: Threads: Filósofos muertos de hambreUn problema clásico de sincronización es el de los filósofos hambrientos. La idea es un grupo de N filósofos y N tenedores sentados alrededor de una mesa. El problema es que se necesitan 2 tenedores para poder comer. Cuando alguno de los tenedores está ocupado por nuestro vecino, debemos esperar que lo desocupe. Esta es la solución inocente que podríamos proponer (vean que piensan poco y comen harto… ¡no son buenos filósofos!):

#include <stdio.h>

#include <pthread.h>

 

#define N_FILO 10

 

pthread_mutex_t forks[N_FILO];

 

/* Si pensar es mucho mas rápido que comer, aumentamos las

* probabilidades de deadlock

*/

 

void pensar(int f) {

printf("%d: pensando...\n", f);

}

 

void comer(int f) {

printf("%d: comiendo..\n", f);

sleep(1);

}

 

void *filo(void *f2) {

int i, f=(int)f2;

 

for(i=0; i < 10; i++) {

pensar(f);

pthread_mutex_lock(&forks[f]);

printf("%d: locked %d\n", f ,f);

pthread_mutex_lock(&forks[(f+1)%N_FILO]);

printf("%d: locked %d\n", f, (f+1)%N_FILO);

comer(f);

pthread_mutex_unlock(&forks[(f+1)%N_FILO]);

printf("unlocked %d\n", f);

pthread_mutex_unlock(&forks[f]);

printf("unlocked %d\n", (f+1)%N_FILO);

}

printf("%d: finito!\n", f);

return NULL;

}

 

main() {

int i;

pthread_t pid[N_FILO];

 

for(i=0; i < N_FILO; i++)

pthread_mutex_init(&forks[i], NULL);

for(i=0; i < N_FILO; i++)

pthread_create(&pid[i], NULL, filo, (void *)i);

for(i=0; i < N_FILO; i++)

pthread_join(pid[i], NULL);

}

Si ejecutan este código, después de un rato aleatorio, termina bloqueándose para siempre en undeadlock. El problema es que podemos caer en el caso en que cada filósofo tiene un tenedor y está esperando que su vecine suelte el otro, formando un ciclo de espera infinito. Para evitar este deadlockdebemos esperar una condición (que ambos tenedores estén desocupados) sin tomar ninguno. Con condiciones queda así:#include <stdio.h>

#include <pthread.h>

 

/*

* Solucion con condiciones: sin deadlocks pero, ¿es justa?

*/

 

#define N_FILO 4

 

pthread_mutex_t table;

pthread_cond_t forks2[N_FILO];

int forks[N_FILO];

 

void pensar(int f) {

printf("%d: pensando...\n", f);

}

 

void comer(int f) {

printf("%d: comiendo..\n", f);

sleep(1);

}

 

void* filo(void *f2) {

int i, f = (int)f2;

 

for(i=0; i < 10; i++) {

pensar(f);

pthread_mutex_lock(&table);

while(forks[f] != 1 || forks[(f+1)%N_FILO] != 1)

pthread_cond_wait(&forks2[f], &table);

forks[f]=forks[(f+1)%N_FILO]=0;

pthread_mutex_unlock(&table);

comer(f);

pthread_mutex_lock(&table);

forks[f] = forks[(f+1)%N_FILO] = 1;

pthread_cond_signal(&forks2[(f-1+N_FILO)%N_FILO]);

pthread_cond_signal(&forks2[(f+1)%N_FILO]);

pthread_mutex_unlock(&table);

}

printf("%d: finito!\n", f);

return NULL;

}

 

main() {

int i;

pthread_t pid[N_FILO];

 

pthread_mutex_init(&table, NULL);

 

for(i=0; i < N_FILO; i++) {

pthread_cond_init(&forks2[i], NULL);

forks[i] = 1;

}

for(i=0; i < N_FILO; i++)

pthread_create(&pid[i], NULL, filo, (void *)i);

for(i=0; i < N_FILO; i++)

pthread_join(pid[i], NULL);

}

Un nuevo problema que aparece al resolver este deadlock es la justicia: ¿podemos garantizar que todos los filósofos comerán algún día? ¿Todos reciben comida al mismo ritmo? Pues no. Si ejecutan este programa, verán que se genera un orden de a pares donde 2 filósofos comen mucho más que los otros 2. Hacer una solución justa es complejo y requiere usualmente manejar las colas de espera a mano. El conflicto habitual es al señalar una condición y despertar un thread y luego liberar el mutex. ¿Quién parte ejecutando? Puede entrar un nuevo thread que esperaba en el mutex, o puede partir el thread que se despertó de su condición. Usualmente, no sé quién es.

El fenómeno de la injusticia en sincronización puede llegar al extremo que un filósofo no coma nunca y muera de hambre. Por eso, se conoce como el problema de starvation o inanición.

AUX 5

Pregunta 1Se quiere implementar un sistema de autos que se adelantan en una callecon tres vías: una sólo para el sur, otra sólo para el norte y una al medio quesólo se usa para adelantamientos en ambas direcciones. Para evitar accidentes,implementamos acceso exclusivo a la pista de adelantamiento:pthread_mutex_t pista;adelantar(int dir) {pthread_mutex_lock(&pista);}volver(int dir) {pthread_mutex_unlock(&pista);}auto(int dir) { /* Un thread por auto */for(;;) {avanzar_hasta_prox_auto(dir);adelantar(dir);avanzar_pista_aux(dir);volver(dir);} }Parte I

El problema de esta solución es que no permite compartir la vía de adelantamientoentre autos que van en la misma dirección. Modifique el códigopara que esto sea posible.1Parte IIModifique su solución para asegurar que la pista de adelantamiento nopueda ser monopolizada en una sola dirección. Para ello, utilice un contadorde autos que van entrando en la misma dirección. Llegado a MAX autos,si hay autos esperando adelantar en la dirección contraria, deben dejar deaceptar nuevos autos, esperar que se desocupe completamente la pista, yaceptar los autos en dirección contraria.Pregunta 2Se propone la siguiente solución para implementar un semáforo usandomutexes:typedef struct {int n;pthread_mutex key;} SEM;SEM *init_sem(int n) {SEM *s;s = (SEM *)malloc(sizeof(SEM));s->n = n; pthread_mutex_init(&s->key, NULL);pthread_mutex_lock(&s->key);return s;}void P(SEM *s) {s->n--;if(s->n < 0)pthread_mutex_lock(&s->key);}void V(SEM *s) {s->n++;if(s->n <= 0)pthread_mutex_unlock(&s->key);}Comente si está correcta, si permite paralelismo entre P (wait) y V (signal)y si no genera deadlocks o incorrectitudes. Corrija lo necesario.2Pregunta 3

Se quiere implementar una primitiva de sincronización que permite esperarun número de threads totales que lleguen al mismo punto antes de seguirejecutando. La idea es que se inicializa la estructura con un entero que indicala cantidad de threads que deben llegar:SYNC *create_sync(int n);void sync(SYNC *s);Por ejemplo, esta creaciónSYNC *s;s = create_sync(10);Hace que 9 threads se quedarán bloqueadas llamando a sync(s) hastaque llegue la décima que la invoque, y entonces las 10 retornarán de sync(), yseguirán su ejecución en paralelo. Cada vez que esto ocurre, la variable vuelvea funcionar igual, y comienza a dejar bloqueados a los threads siguientes hastacompletar otros 10.Implemente la estructura con su función de inicialización y la primitivasync().

P1a)int dir = SUR;int count = 0;pthread_cond_t cnt0;cur_dir = null;

adelantar(int dir) {

pthread_mutex_lock(&pista); while(dir != cur_dir && count > 0)

pthread_cond_wait(&cnt0, &pista); count++; cur_dir = dir; pthread_mutex_unlock(&pista);}

volver(int dir) {pthread_mutex_lock(&pista);count--;if(count == 0)

pthread_cond_broadcast(&cnt0); pthread_mutex_unlock(&pista);

}

b)

int cur_dir = SUR;int count = 0;int tot_lado = 0;pthread_cond_t cntsur0, cntnorte0; int wait_norte = 0;int wait_sur = 0;

adelantar(int dir) {pthread_mutex_lock(&pista);while((dir != cur_dir && count > 0) || tot_lado >= MAX){

if(dir == SUR) { wait_sur++;

pthread_cond_wait(&cntsur0, &pista); wait_sur--; } else { wait_norte++; pthread_cond_wait(&cntnorte0, &pista); wait_norte--; }

} count++; tot_lado++; cur_dir = dir; pthread_mutex_unlock(&pista);}

volver(int dir) { pthread_mutex_lock(&pista); count--; if(count == 0) { if(dir == SUR) { if(wait_norte > 0)

pthread_cond_broadcast(&cntnorte0); else pthread_cond_broadcast(&cntsur0); } if(dir == NORTE) { if(wait_sur > 0) pthread_cond_broadcast(&cntsur0); else pthread_cond_broadcast(&cntnorte0);

} tot_lado = 0; } pthread_mutex_unlock(&pista);}

P2

/* lo importante es que analicen los casos correctamente: * 1) si un P() y un V() se ejecutan concurrentemente: * podemos hacer un unlock de mas dejando el sistema indefinido. * Necesitamos rodear el codigo de un mutex. * 2) probar paralelismo entre varios P() y varios V(): * varios V() pueden evitar que se haga un unlock(), siendo grave. * Solucion propuesta: */

void P(SEM *s) { pthread_mutex_lock(&s->mutex); s->n--; if(s->n < 0) { pthread_mutex_unlock(&s->mutex); pthread_mutex_lock(&s->key); pthread_mutex_lock(&s->mutex);

} pthread_mutex_unlock(&s->mutex);}

void V(SEM *s) { pthread_mutex_lock(&s->mutex); s->n++; if(s->n <= 0) pthread_mutex_unlock(&s->key); pthread_mutex_unlock(&s->mutex);}

/* Analizar la nueva solucion: punto de fragilidad que debieran

* mostrar es el instante en que uno suelta el mutex en P() antes * de hacer el lock de key. Si la llave acepta bien hacer mas * unlocks que locks, la solucion esta correcta. * El caso contrario no se puede solucionar :( */

P3

typedef struct { int nmax; int waiting; pthread_mutex_t mutex; pthread_cond_t arrived;} SYNC;

SYNC *create_sync(int n){ SYNC *s;

s = (SYNC *)malloc(sizeof(SYNC)); s->waiting = 0;

s->nmax = n; pthread_mutex_init(&s->mutex, NULL); pthread_cond_init(&s->arrived, NULL); return s;

}

void sync(SYNC *s) {pthread_mutex_lock(&s->mutex);s->waiting++;if(s->waiting == s->nmax) { // es un error poner un while

aqui... s->waiting = 0;

pthread_cond_broadcast(&s->arrived);// Justo despertaran nmax

}else

pthread_cond_wait(&s->arrived, &s->mutex); pthread_mutex_unlock(&s->mutex);}

AUX 6

1Se desea escribir un programa que escriba en pantalla una secuencia deenteros, y que cada vez que se presione CTRL+C, se reinicie el contador de

secuencias. El programa debe esperar 1 segundo entre secuencias. Tambiénse debe poder especificar un tiempo máximo de ejecución del programa. Acontinuación se encuentran los parámetros a recibir y una salida de ejemplo:./sseq ini step fin [maxtime]sseq 5 2 20 251: 5 7 9 11 13 15 17 19<CTRL+C>---------------1: 5 7 9 11 13 15 17 192: 5 7 9 11 13 15 17 193: 5 7 9 11 13 15 17 19<CTRL+C>---------------1: 5 7 9 11 13 15 17 192: 5 7 9 11 13 15 17 193: 5 7 9 11 13 15 17 194: 5 7 9 11 13 15 17 195: 5 7 9 11 13 15 17 19---------------Se acabo el tiempoTerminando el programa1HINT: Las variables no se mantienen a través de un jmp a menos que seandefinidas a través del keyword volatile.Pregunta 2Para demostrar el concepto de proceso zombie se le pide que haga un programamodificable a través de la macro ZOMBIE, que deja procesos zombiessi está en un 1, y los erradica si es que está en 0. El flujo del programa consisteen que el proceso padre trate de matar reiteradas veces al proceso hijoa través de la función kill, acción que es impedida sólo 3 veces por un handler.Después de esto, si la macro ZOMBIE está activada, debe imprimirsepermanentemente un mensaje, avisando que existen procesos zombies.Pregunta 3Se quiere implementar una marca en el código a la que se retorna cuandose han recibido 5 CTRL+C y que envía una advertencia al cuarto CTRL+C.Además, al sexto CTRL+C el programa debe cerrarse. El uso sería:

if(setjmp(ctrlc) != 0)printf("5 CTRL+C! \n");Para esto, programe un handler de manejar la señal enviada por CTRL+C.

P1#include <stdio.h>#include <stdlib.h>#include <setjmp.h>#include <signal.h> #include <unistd.h> /*para alarm*/

volatile int count=1;volatile int execute=1;

jmp_buf jump;

/*cuando llega una señal se ejecuta handler * y el numero de la señal se pasa como argumento */void handler(int sig){ if(sig==SIGINT){ /*reseteamos el handler*/ signal(SIGINT,handler); count=1; /*ejecutamos longjmp para reiniciar el proceso*/ longjmp(jump,1); }else if(sig==SIGALRM){ printf("---------------\nSe acabo el tiempo\nTerminando el programa\n"); execute=0;v} /*impime forma de uso*/void usage(){ printf("Uso:\n\tsseq ini step fin [maxtime]\n"); printf("Se recomienda el uso de maxtime para que el programa muera\n");}/*escribe secuencias de numeros * recomendable que se use parametro maxtime para qeu el programa muera*/int main(int argc, char *argv[]){ int ini,fin,step,time,i; if(argc==4||argc==5){ ini=atoi(argv[1]); step=atoi(argv[2]); fin=atoi(argv[3]); if(argc==5) time=atoi(argv[4]); }else{ usage();

return EXIT_FAILURE; } signal(SIGINT,handler); signal(SIGALRM,handler); if(argc==5) alarm(time); while(execute){ if(!setjmp(jump)){ printf("%d: ",count); for(i=ini;i<=fin;i+=step) printf("%d ",i); count++; }else{ /*count lo maneja handler */ printf("\n---------------"); } printf("\n"); sleep(1); } return EXIT_SUCCESS;}

P2

#include <stdio.h>#include <stdlib.h>#include <setjmp.h>#include <signal.h> #include <unistd.h> /*para alarm*/#include <sys/wait.h> #define ZOMBIE 1 /* 1 TRUE... 0 FALSE, cambiar entre estos valores para ver que pasa cuando un proceso es un zombie */

int i;

void zombie() {if(i < 3){

printf("\nEl padre est‡ tratando de matar a su hijo :(\n\n");

}else{

printf("\nSuficiente: el hijo ha de morir!\n\n");exit(0);

}++i;

}

void child() { int status;

while((status=waitpid(-1, NULL, WNOHANG)) > 0) fprintf(stderr, "Padre: por fin acabe contigo, hijo numero %d\n", status); exit(0);}

int main() { pid_t child_pid;

i = 0; int j=0; signal(SIGINT, zombie);#if ZOMBIE == 0 signal(SIGCHLD, child);#endif child_pid = fork(); if(child_pid > 0){ printf("Padre: Proceso %d, yo soy tu padre\n", child_pid); while(i++ < 5){

sleep(3); kill(child_pid, SIGINT); } #if ZOMBIE == 1

for(;;){printf("Mi hijo esta muerto, pero como no

hice wait es un zombie!!\n");printf("El proceso %d deber’a ser un zombie!\

n", child_pid);sleep(4);

} #endif }else{ printf("Hijo: Ya llegue\n");

for(;;){ printf("Hijo: Estoy esperando senales ahora!\

n"); pause(); /*esperara por se–ales*/

printf("Hijo: Me acaba de llegar una senal!\n"); } } }

P3

#include <stdio.h>

#include <stdlib.h>#include <setjmp.h>#include <signal.h>#include <unistd.h> /*para alarm*/

volatile int count=0;/*definimos un ambiente donde guardar las variables*/

jmp_buf env;

/*cuando llega una señal se ejecuta handler * y el numero de la señal se pasa como argumento */void handler(int sig){ if(sig==SIGINT){ /*reseteamos el handler*/ signal(SIGINT,handler); count++; if(count==4){ printf("Este es tu cuarto CTRL+C\n"); } /*ejecutamos longjmp para reiniciar el proceso*/ if(count==5){ longjmp(env,1); } if(count==6){ printf("Adios!\n"); exit(0); } }}

int main(int argc, char *argv[]){int i=1;

/* creamos el signal para el handler */

signal(SIGINT,handler); /* Almacenamos el ambiente actual: * variables, registros, stack pointer y framepointer * setjmp retorna 0 si almaceno de forma correcta. * * */

if(setjmp(env) != 0)printf("Aviso: 5 CTRL+C!\n");

printf("Holaaa \n");

for(;;){

printf("%d\n",i++);sleep(1);

}

return EXIT_SUCCESS;}

ANEXOS AUX6

FORK

#include<stdio.h>#include<unistd.h>#include<signal.h>#include<stdlib.h>

int main() { int i = 0; int pid; int status = 0; printf("Estoy ejecutando el proceso principal\n"); pid = fork(); if (pid < 0) { fprintf(stderr,"No pude crear el proceso\n"); return(1); } printf("Estoy duplicado\n"); if (pid == 0) { signal(SIGINT,SIG_IGN); sleep(5); i = 10; printf("Soy el clon con i %d\n",i); exit(3); } else { /* verificar si el hijo sigue vivo */ while (waitpid(pid,&status,WNOHANG) == 0) { printf("El hijo aun vive\n"); sleep(1); } printf("Mi hijo murio con estado %d\n",WEXITSTATUS(status)); }

}

JMP

#include <setjmp.h>

main(){ jmp_buf env; int i; i = setjmp(env); printf("i = %d\n", i); if (i != 0) exit(0); longjmp(env, 2); printf("Hello! :-)"); }

AUX 7

Problema 1Escriba un programa el cual lee un string como parámetro de entrada yenvía los datos leídos a un proceso hijo el cual retorna la cantidad de bytesleídos.Problema 2Se le pide implementarvoid pipeline(char *cmds[] , int size)Que recibe un arreglo de comandos y los ejecuta conectados por pipes,al igual que lo hace el shell cuando tipean una secuencia. Por ejemplo, lasecuencia:% cat | sort | uniq | morese crea haciendo:cmds[0] = "cat"; cmds[1] = "sort";

cmds[2] = "uniq"; cmds[3] = "more";pipeline(cmds, 4);Suponga que las primitivas nunca fallan y que los comandos existen. Elprimer comando lee desde la entrada estándar, y el último comando escribeen la salida estándar. No se requiere esperar a que los procesos mueran, bastacon crearlos bien conectados con sus pipes. En el caso anterior, deben crear3 pipes.1Problema 3Implemente la función,int nfiles(char *path)que cuenta los archivos y directorios presentes en la ruta path. Puedeasumir que no hay links ni puntos de montaje bajo path.

P1

#include <stdio.h>#include <string.h>#include <sys/types.h>#include <unistd.h>

#define BUFFER_SIZE 1024#define READ 0#define WRITE 1

int main(int argc, char *argv[]){ /*En caso que se quiera enviar un String...

char write_msg[BUFFER_SIZE] = "CC3301-1 Programacion de Software de Sistema"; */

char *write_msg = argv[1];char read_msg[BUFFER_SIZE];int fd[2];pid_t pid;

/*Creamos el pipe * un pipe es de comunicacion unidireccional utilizando los

fd que se pasan como argumento*/if(pipe(fd) == -1){

fprintf(stderr, "Fallo el pipe");return 1;

}

/*Creamos el proceso*/pid = fork();if(pid < 0) {

fprintf(stderr, "Fallo el fork");return 1;

}

if(pid > 0) {/*Soy el padre*//*Debo cerrar el extremo del pipe que no uso*/close(fd[READ]);

printf("Soy el padre y voy a escribir en el pipe: \n");/*Escribimos en el pipe*/write(fd[WRITE], write_msg, strlen(write_msg)+1);

/*Cerramos el pipe*/close(fd[WRITE]);

} else {/*Soy el hijo*//*Cerramos el extremo que no uso*/close(fd[WRITE]);

/*Leemos desde el pipe*/int n = read(fd[READ], read_msg, BUFFER_SIZE);printf("Soy el hijo y lei: %s.\n", read_msg);

printf("El tamano del mensaje es: %d \n", n-1);

/*Cerramos el pipe*/close(fd[READ]);

}

return 0; }

P2

#include <stdio.h>#include <unistd.h>#include <stdlib.h>

#define READ 0#define WRITE 1

int pipeline(char *cmds[], int ncmds) {

int fdin, fdout; int fds[2]; int i; fdin = 0; for(i=0; i < ncmds-1; i++) { pipe(fds); fdout = fds[WRITE]; if(fork() == 0) { if( fdin != 0 ) { close(0); dup(fdin); close(fdin); } if( fdout != 1 ) { close(1); dup(fdout); close(fdout); } close(fds[READ]); execlp(cmds[i], cmds[i], NULL); exit(1); } if(fdin != 0) close(fdin); if(fdout != 1) close(fdout); fdin = fds[READ]; } /* Ultimo comando */ fdout = 1; if(fork() == 0) { if( fdin != 0 ) { close(0); dup(fdin); close(fdin); } close(fds[READ]); execlp(cmds[i], cmds[i], NULL); exit(1); } if( fdout != 1) close(fdout); if( fdin != 0 ) close(fdin);}

char *cmds[] = {"cat", "sort", "uniq"};main() { pipeline(cmds, 3); while(wait(NULL) > 0) ;}

P3

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <dirent.h>#include <string.h>#include <sys/stat.h>#include <unistd.h>

int nfiles(char *);

int main(){

char dir[] = ".";printf("%d archivos bajo el directorio %s\n", nfiles(dir),

dir); exit(EXIT_SUCCESS);

}

int nfiles(char *path){

DIR *dir;struct dirent *de;struct stat st;int count = 0;char *childpath; // obtengo el handler del directoriodir = opendir(path);

if(!dir) {perror(path);exit(EXIT_FAILURE);

} // itero sobre el directoriowhile((de = readdir(dir))) {

// siempre hay que saltarse el . y el .., explicar cual es la unica carpeta en la que . y .. son iguales

// también hay que hacer hincapié que siempre están ahÃ

if(!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))

continue; // sprintf con malloc incluidoasprintf(&childpath, "%s/%s", path, de->d_name); // señalar diferencia entre stat y lstatif(stat(childpath, &st)){

perror(de->d_name);exit(EXIT_FAILURE);

} // ojo con S_ISDIRif(S_ISDIR(st.st_mode))

count += nfiles(childpath);

++count;

// ojo con el malloc del asprintffree(childpath);

} // cierro el directorioclosedir(dir);

return count;}

ANEXOS AUX 7

POPEN

#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <fcntl.h>#define READ 0#define WRITE 1

int popen_id;

int fdpopen(char *cmd, int mode){ int fds[2];

if(pipe(fds) < 0) return -1;

if((popen_id=fork()) == 0) /* soy el hijo */ {

/* soy cmd */if(mode == READ) { /* yo voy a escribir (ej: ls) */ close(fds[READ]);

close(1); dup(fds[WRITE]); close(fds[WRITE]);

} else { /* yo voy a leer (ej: wc) */ close(fds[WRITE]);

close(0); dup(fds[READ]); close(fds[READ]);

}execl("/bin/sh", "sh", "-c", cmd, NULL);exit(127);

}

if(popen_id < 0) return -1;

/* Este es el padre *//* Cerrar los fds que no vamos a usar */ close((mode==READ) ? fds[WRITE] : fds[READ]);

return(fds[mode]);}

int fdpclose(int fd) { int status;

close(fd); waitpid(popen_id, &status, 0); return(status);}

#define BUF_SIZ 1024

main(){ int fd; char buf[BUF_SIZ]; int cnt; /* abrimos ls en lectura */ fd = fdpopen("ls", READ); if(fd < 0) {

fprintf(stderr, "No pude abrir ls!\n");perror("open");

exit(1); }

/* mostramos la salida de ls */

while((cnt=read(fd, buf, BUF_SIZ)) > 0)if(write(1, buf, cnt) != cnt) exit(1);

fdpclose(fd); printf("========================\n ingrese texto...\n");

fd=fdpopen("wc", WRITE);

/* copiamos nuestra entrada a wc */ while((cnt=read(0, buf, BUF_SIZ)) > 0)

if(write(fd, buf, cnt) != cnt) exit(1);

fdpclose(fd); printf("Fin del Mundo-------\n");}

SYSTEM

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>

int msystem(char *cmd){ int pid; int status;

if( (pid=fork()) < 0 ) return(-1);

if( pid == 0 ) /* Es el hijo */ {

execl("/bin/sh", "sh", "-c", cmd, NULL);exit(127);

}

/* Es el padre: debemos esperar la muerte del hijo */

waitpid( pid, &status, 0 ); return(WEXITSTATUS(status));}

/* ejemplo de uso */main(){ int ret;

ret = msystem("ls | more"); printf("done: %d\n", ret);}

AUX 8

Pregunta 1 - Control 2 Otoño 2011Queremos agregar a box un par de primitivas para escribir y leer un bufferen la caja en una sola operación:void putnbox(BOX *b, char *buf, int n);/* escribe los n chars de buf en box */int getnbox(BOX *b, char *buf, int n);/* lee chars en buf. maximo n, retorna lo leido */putnbox() siempre escribe los n bytes seguidos. Es decir, nunca debepoder entrar otro proceso haciendo put entre medio. Recuerde que n podríaser mayor que NBUFS.getnbox() se bloquea si no hay bytes en la caja. Una vez que hay bytesdisponibles, los lee todos (hasta un máximo de n), copiándolos al buffer.Luego retorna el número de bytes copiados.Implemente ambas funciones utilizando las primitivas de sincronizaciónque prefiera.Revise su código y explique si funciona para un productor y un consumidor;luego, si funciona para múltiples productores y consumidores simuláneos.Pregunta 2 - Control 2 Otoño 2011Escriba las siguientes funciones (sin usar popen()):int more();/* retorna el file descriptor para escribir a "more" por un pipe */1void more_close(int fd);/* Cierra el fd y espera la muerte de "more" */La idea es generar un file descriptor en el que podemos escribir y que estáconectado por un pipe al comando /bin/more, para que vaya mostrandopágina por página lo que escribimos.Pregunta 3 - Control 2 Otoño 2011En clases vimos la función:static jmp_buf ring;

void clock(){longjmp(ring, 1);}int call_with_timeout(int (*f)(), int timeout){int res;struct sigaction sig, osig;memset(&sig, 0, sizeof(struct sigaction));sig.sa_handler = clock;sig.sa_flags = SA_NODEFER;sigaction(SIGALRM, &sig, &osig);if(setjmp(ring) != 0) {sigaction(SIGALRM, &osig, NULL);return -1;}alarm(timeout);res = f();alarm(0);sigaction(SIGALRM, &osig, NULL);return(res);}Modifíquela para que ahora también sobreviva si alguien genera una interrupcióncon ctrl-C durante la ejecución de la función f. En ese caso, debemosretornar -2 para diferenciar del timeout. Recuerde anular el timeout cuandohubo un ctrl-C.

P1

/*typedef struct{ * * char buf[NBUFS]; * pthread_cond_t vacios, llenos; * pthread_mutex_t mutex, put; * int in, out; * int nvacios, nllenos; * }BOX;*/

void putnbox(BOX *b, char *buf, int n){

pthread_mutex_lock(&b->put);pthread_mutex_lock(&b->mutex);

while(n > 0){while(b->nvacios==0)

pthread_cond_wait(&b->vacios, &b->mutex);b->buf[b->in] = *buf++;n--;b->in = (b->in+1)%NBUFS;b->nllenos++;b->nvacios--;

}pthread_cond_signal(&b->llenos);pthread_mutex_unlock(&b->mutex);pthread_mutex_unlock(&b->put);

}

void getnbox(BOX *b, char *buf, int n){

int cnt = 0;pthread_mutex_lock(&b->mutex);while(b->nllenos == 0)

pthread_cond_wait(&b->llenos, &b->mutex);while(b->nllenos > 0 && n>0){

**buf++ = b->buf[b->out];n--;cnt++;b->out = (b->out +1)%NBUFS;b->nllenos--;b->nvacios++;

}pthread_cond_signal(&b->vacios);pthread_mutex_unlock(&b->mutex);return cnt;

}

P2

int mpid;

int more(){

int fds[2];pipe(fds);mpid = fork();if(mpid < 0)

return -1;if(mpid == 0){ /*Soy el hijo!*/

close(fds[1]);close(0); dup(fds[0]); close(fds[0]);execl("/bin/more", "more", NULL);fprint("error!");exit(1);

}close(fds[0]);return fds[1];

}

void more_close(int fd){close(fd);waitpid(mpid, NULL, NULL);

}

P3

void clock(){longjmp(ring, 1);

}

void brk(int sig){alarm(0);longjmp(ring, 2);

}

int call_with_timeout(int (*f)(), int timeout){int res;int c;struct sigaction sig, osig1, osig2;memset(&sig, 0, sizeof(struct sigaction));sig.sa_handler = clock;sig.sa_flags = SA_NODEFER;sigaction(SIGALRM, &sig, &osig1);

sig.sa_handler = brk;sigaction(SIGINT, &sig, &osig2);if(c = setjmp(ring) != 0) {

sigaction(SIGALRM, &osig1, NULL);sigaction(SIGINT, &osig2, NULL);if(c==1) return -1;else return -2;

}alarm(timeout);res = f();alarm(0);

sigaction(SIGALRM, &osig1, NULL);sigaction(SIGINT, &osig2, NULL);return(res);

}