Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La...

84
Llamadas al sistema para manejo de ficheros

Transcript of Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La...

Page 1: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Llamadas al sistema para manejo de ficheros

Page 2: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Llamadas al sistema para manejo de ficheros

•Estas funciones se suelen denominar "de bajo nivel". •La biblioteca estándar también ofrece otras rutinas más cómodas, construidas a partir de las llamadas al sistema. •La interfaz con estos servicios se encuentra en <stdio.h>.

Algunas de las llamadas al sistema del UNIX que trabajan con ficheros:* Abrir y cerrar un fichero* Crear y borrar un fichero* Leer en un fichero* Escribir en un fichero* Desplazarse por un fichero

Page 3: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Para trabajar con un fichero hay primero que abrirlo con una invocación a la función open. Ésta devuelve un descriptor de fichero (file descriptor en inglés), un número entero que servirá de identificador de fichero en futuras operaciones. Finalmente hay que cerrar el fichero, con la función close, para liberar los recursos que tengamos asignados.

Manejo de ficheros en UNIX

Existen al menos tres descriptores ya establecidos en la ejecución de un programa.

•El descriptor 0 es la entrada estándar (normalmente el teclado), •El descriptor 1 es la salida estándar (normalmente la pantalla) •El descriptor 2 el fichero estándar de visualización de errores (también la pantalla, normalmente).

Los pueden considerar como simples ficheros que ya han sido abiertos, y pueden trabajar con ellos con cierta normalidad. Incluso los pueden cerrar.

Page 4: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Los ficheros en UNIX permiten tanto el acceso directo como el secuencial. Cada fichero abierto dispone de un puntero que se mueve con cada lectura o escritura. Hay una función especial llamada lseek para posicionar ese puntero donde se quiera dentro del fichero.

Page 5: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Especificación de los permisos: forma octal

Las llamadas al sistema creat y open admiten un parámetro entero en el que se especifican los permisos con los que se crea un archivo. Una de las maneras más cómodas de declararlos es mediante la representación octal.

Los permisos se forman como un número de 9 bits, en el que cada bit representa un permiso, tal y como se muestra en el cuadro (es el mismo orden con el que aparecen cuando hacemos un ls).

 

RWX RWX RWX

usuario grupo otros

Page 6: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Se toman los nueve permisos como tres números consecutivos de 3 bits cada uno. Un bit vale 1 si su permiso correspondiente está activado y 0 en caso contrario. Cada número se expresa en decimal, del 0 al 7, y los permisos quedan definidos como un número octal de tres dígitos.

Para poner un número en octal en el lenguaje C, se escribe con un cero a la izquierda.

Por ejemplo, los permisos rw-r--r-x son el número octal 0645.

/* Crea un fichero con permisos RW-R--R-- */int fd = creat ( "mi_fichero", 0644);

Page 7: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Apertura, creación y cierre de ficheros

Función open

La función open abre un fichero ya existente, retornando un descriptor de fichero. La función tiene este prototipo:

int open ( char* nombre, int modo, int permisos );

El parámetro nombre es la cadena conteniendo el nombre del fichero que se quiere abrir.

Page 8: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

El parámetro modo establece la forma en que se va a trabajar con el fichero. Algunas constantes que definen los modos básicos son:

O_RDONLY  

abre en modo lectura

O_WRONLY  

abre en modo escritura

O_RDWR  

abre en modo lectura-escritura

O_APPEND  

abre en modo apéndice (escritura desde el final)

O_CREAT  

crea el fichero y lo abre (si existía, se lo machaca)

O_EXCL  

usado con O_CREAT. Si el fichero existe, se retorna un error

O_TRUNC  

abre el fichero y trunca su longitud a 0

Page 9: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Para usar estas constantes, han de incluir la cabecera <fcntl.h>. Los modos pueden combinarse, simplemente sumando las constantes, o haciendo un "or" lógico, como en este ejemplo:

La función open retorna un descriptor válido si el fichero se ha podido abrir, y el valor -1 en caso de error.

O_CREAT | O_WRONLY

Page 10: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Función creat

Si desean expresarmente crear un fichero, disponen de la llamada creat. Su prototipo es:

int creat ( char* nombre, int acceso );

Equivale (más o menos) a llamar a:

open (nombre, O_RDWR|O_CREAT, acceso);

Es decir, devuelve un descriptor si el fichero se ha creado y abierto, y -1 en caso contrario.

Page 11: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Función close

Para cerrar un fichero ya abierto está la función close:

int close ( int fichero );

donde fichero es el descriptor de un fichero ya abierto. Retorna un 0 si todo ha ido bien y -1 si hubo problemas.

Page 12: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Borrado de ficheros

La función

int unlink ( char* nombre );

borra el fichero de ruta nombre (absoluta o relativa).

Devuelve -1 en caso de error.

Page 13: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Lectura y escritura

Para leer y escribir información en ficheros, han de abrirlos primero con open o creat. Las funciones read y write se encargan de leer y de escribir, respectivamente:

int read ( int fichero, void* buffer, unsigned bytes );int write( int fichero, void* buffer, unsigned bytes );

Ambas funciones toman un primer parámetro, fichero, que es el descriptor del fichero sobre el que se pretende actuar.

Page 14: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

El parámetro buffer es un apuntador al área de memoria donde se va a efectuar la transferencia. O sea, de donde se van a leer los datos en la función read, o donde se van a depositar en el caso de write.

El parámetro bytes especifica el número de bytes que se van a transferir.

Las dos funciones devuelven el número de bytes que realmente se han transferido. Este dato es particularmente útil en la función read, pues es una pista para saber si se ha llegado al final del fichero. En caso de error, retornan un -1.

Page 15: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Hay que tener especial cautela con estas funciones, pues el programa no se va a detener en caso de error, ni hay control sobre si el puntero buffer apunta a un área con capacidad suficiente (en el caso de la escritura), etc., etc.

La primera vez que se lee o escribe en un fichero recién abierto, se hace desde el principio del fichero (desde el final si se incluyó la opción O_APPEND). El puntero del fichero se mueve al byte siguiente al último byte leído o escrito en el fichero. Es decir, UNIX trabaja con ficheros secuenciales.

Page 16: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Movimiento del puntero del fichero

El C y UNIX manejan ficheros secuenciales. Es decir, conforme se va leyendo o escribiendo, se va avanzando en la posición relativa dentro del fichero. El acceso directo a cualquier posición dentro de un fichero puede lograrse con la función lseek.

long lseek ( int fichero, long desp, int origen );

Como siempre, fichero es el descriptor de un fichero y abierto.

Page 17: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

El parámetro desp junto con origen sirven para determinar el punto del fichero donde va a acabar el puntero. desp es un entero largo que expresa cuántos bytes hay que moverse a partir del punto indicado en origen, parámetro que podrá adoptar estos valores:

Las constantes simbólicas se encuentran en <stdlib.h> y <unistd.h>.

El parámetro desp puede adoptar valores negativos, siempre que tengan sentido. Si el resultado final da una posición mayor que el tamaño del fichero, éste crece automáticamente hasta esa posición.

0 SEEK_SET inicio del fichero

1 SEEK_CUR relativo a la posición actual

2 SEEK_END relativo al final del fichero

Page 18: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

La función lseek devuelve un entero largo que es la posición absoluta donde se ha posicionado el puntero; o un -1 si hubo error.

Obsérvese que la función lseek puede utilizarse también para leer la posición actual del puntero.

Page 19: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Primer programa: creación y escrituraEste ejemplo crea un fichero y escribe en él unos caracteres.

#include <string.h> /* Función strlen() */#include <fcntl.h> /* Modos de apertura y función open()*/#include <stdlib.h> /* Funciones write() y close() */ main ( int argc, char* argv[] ){ /* Cadena que se va a escribir */ const char* cadena = "Hola, mundo";  /* Creación y apertura del fichero */ int fichero = open ("mi_fichero", O_CREAT|O_WRONLY,0644);  /* Comprobación de errores */ if (fichero==-1) { perror("Error al abrir fichero:"); exit(1); }

/* Escritura de la cadena */ write(fichero, cadena, strlen(cadena)); close(fichero); return 0; }

Page 20: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Segundo programa: lecturaEste programa lee diez caracteres, a partir de la posición 400, de un fichero ya existente.#include <fcntl.h> /* Modos de apertura */#include <stdlib.h> /* Funciones de ficheros */ main ( int argc, char* argv[] ){ char cadena[11]; /* Depósito de los caracteres */ int leidos;  /* Apertura del fichero */  int fichero = open ("mi_fichero", O_RDONLY);  /* Comprobación */ if (fichero==-1) { perror("Error al abrir fichero:"); exit(1); }  /* Coloca el puntero en la posición 400 */ lseek(fichero,400,SEEK_SET);  /* Lee diez bytes */ leidos = read(fichero, cadena, 10); close(fichero); cadena[10]=0;  /* Mensaje para ver qué se leyó */ printf ( "Se leyeron %d bytes. La cadena leída es %s\n", leidos,cadena );  return 0; }

Page 21: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Flujos estándares y redirección

Page 22: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Un proceso toma y escribe datos desde y hacia el exterior. El shell, por ejemplo, lee caracteres del teclado e imprime caracteres en la pantalla. En UNIX, los procesos se comunican con el exterior a través de flujos (streams).

Conceptualmente, un flujo es una ristra de bytes que se puede ir leyendo o sobre la que se puede escribir caracteres.

Un flujo puede ser un fichero ordinario, o estar asociado a un dispositivo.

Cuando se lee del teclado es porque previamente se ha abierto como flujo de caracteres del que leer.

Un proceso, cuando muestra algo por pantalla, está escribiendo caracteres a un flujo de salida.

Page 23: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Entrada, salida y error estándares

Los procesos lanzados por un procesador de órdenes (un shell) utilizan dos flujos de particular interés: la entrada estándar y la salida estándar. La entrada estándar es utilizada para recoger información, y la salida estándar para enviar información al exterior.

La entrada estándar suele ser el teclado. La salida estándar suele ser la pantalla. Por ejemplo, cuando se ejecuta ls, este programa nos muestra los ficheros del directorio actual escribiendo los caracteres necesarios sobre la salida estándar (que suele ser la pantalla). Los flujos estándares pueden ser redirigidos a otros ficheros.

Existe también el llamado error estándar, flujo donde se vierten los mensajes de error. Habitualmente coincide con la salida estándar, pero se considera un flujo diferente.

Page 24: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Utilización en un programa en C

La entrada y la salida estándares en UNIX son utilizables en forma de sendos ficheros que están abiertos desde el inicio de la ejecución del programa. Como ya sabrán, cuando se abre un fichero con open se devuelve un descriptor de fichero (file descriptor), que es un número entero que se empleará en posteriores llamadas a las funciones de manejo de ficheros como read o write.

En UNIX siempre se cumple que la entrada estándar tiene el descriptor de valor 0, y la salida estándar el de valor 1.

Page 25: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Redirección

Para redirigir la entrada o la salida hay que conseguir abrir el fichero de redirección de forma que el sistema le asigne el handle 0 ó 1. ¿Cómo lograr esto? Pues teniendo en cuenta que el algoritmo de la llamada open reserva el primer descriptor disponible, empezando a explorar desde el número cero.

Esto significa que si cerramos la salida estándar (descriptor número 1), el siguiente open que se realice asignará al fichero abierto el descriptor 1, quedando automáticamente reasignada la salida estándar. Sirva de ejemplo este código:

close (1);open ("pepe.txt",O_CREAT,0777);printf("Estoy escribiendo en pepe.txt\n");

Cualquier nueva operación sobre la salida estándar se dirigirá al fichero "pepe.txt". (Suponiendo que el descriptor 0 no esté libre).

Page 26: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Preservar el antiguo flujo estándar: función dup

A veces conviene preservar el fichero de entrada o salida estándar actual, para restituirlo en el futuro. Para ello se emplea la llamada dup, que duplica un fichero ya abierto, esto es, lo vuelve a abrir y le reserva otro descriptor disponible.

Sintaxis: int dup (int handle);

Page 27: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

int guardado = dup(1);// preserva la salida estándar

/* Realiza la redirección */

close (1);

open ("pepe.txt",O_CREAT,0777);

... trabajamos con la salida estándar redirigida ...

/* Restituye la antigua salida estándar */

close(1); // Cierra la actual

dup(guardado); // Duplica el guardado (le asigna el descriptor 1)

close(guardado); // Cierra el guardado (no hace más falta)

Ejemplo:

Page 28: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Ejecución de procesos

Page 29: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

La función main y los argumentos

Habitualmente, cuando se lanza un programa a ejecución desde el shell, se le añaden parámetros o argumentos para definir exactamente qué queremos de él. Por ejemplo:

vi pepe.txtls -l /usr/includecp pepe.c ../pipo.c

En el primer caso se invoca al editor vi especificándose que se desea trabajar con el fichero "pepe.txt". El fichero es un parámetro pasado al shell. El segundo caso es una llamada al programa ls que incorpora dos parámetros, como ocurre en el último ejemplo.

Page 30: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

¿Qué son los parámetros o argumentos?

En principio, puede afirmarse que son conjuntos de caracteres separados por blancos (espacios o tabuladores). Ahora bien, no se consideran parámetros los redireccionamientos, y caracteres como el ampersand "&" o el punto y coma ";" actúan de separadores (en general, eso ocurre con todos los caracteres que tengan un significado para el intérprete de órdenes).

Ejemplo:En la orden

ls -l /usr/include & >pepe.txt

los parámetros son "ls", "-l" y "/usr/include" ('&' y la redirección no cuentan).

Page 31: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Cada programa recibe los parámetros a través de su punto de entrada, que en el caso del lenguaje C es la función main. El formato mínimo que acepta esta función en UNIX es

main ( int argc, char* argv[] );

donde argc expresa cuántos parámetros se han reconocido, y argv es un vector de cadenas de caracteres que precisamente contienen los parámetros, siendo argv[i] el parámetro i.

Los parámetros se empiezan a numerar en 0. El parámetro 0 es el nombre del programa invocado tal y como se pasó en la línea de órdenes. En el ejemplo de vi pepe.txt, los valores de argc y argv serían:

argc = 2argv[0] = "vi"argv[1] = "pepe.txt"

Page 32: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Funciones para ejecución de programas

Page 33: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

La función system()

La forma más sencilla de invocar una orden UNIX desde un programa en C es mediante la función system, que toma como único parámetro la orden que quieren ejecutar. Reconoce redirecciones, expresiones regulares, conductos (pipes), etc. Por ejemplo, la línea:

system("ls -l /usr/include/*.h >pepe.txt")

ejecuta la cadena pasada como parámetro tal y como si la hubiéramos tecleado desde la consola. La función system se limita a lanzar un shell hijo pasándole como parámetro de entrada la cadena suministrada en la función.

Page 34: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

La forma de más bajo nivel para ejecutar una orden consiste en lanzar a ejecución el programa deseado mediante alguna de las llamadas al sistema que empiezan por exec.

Existen varias modalidades que difieren en la forma de pasar los parámetros al programa (aunque realmente se trata de una sola llamada al sistema UNIX).

Page 35: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Las llamadas exec...El sistema operativo UNIX ofrece una llamada al sistema llamada 'exec' para lanzar a ejecución un programa, almacenado en forma de fichero. Aunque en el fondo sólo existe una llamada, las bibliotecas estándares del C disponen de varias funciones, todas comenzando por 'exec' que se diferencian en la manera en que se pasan parámetros al programa.

La versión típica cuando se conoce a priori el número de argumentos que se van a entregar al programa se denomina execl. Su sintaxis es

int execl ( char* fichero, char* arg0, char* arg1, ... , 0 );

Es decir, el nombre del fichero y luego todos los argumentos consecutivamente, terminando con un puntero nulo (vale con un cero).

Page 36: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Sirva este ejemplo:

Para ejecutar/bin/ls -l /usr/include

Escribiríamos

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

Obsérvese que el primer argumento coincide con el nombre del programa.

Page 37: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

En caso de desconocer con anticipación el número de argumentos, habrá que emplear la función execv, que tiene este prototipo:

execv ( char* fichero, char* argv [] );

El parámetro argv es una tira de cadenas que representan los argumentos del programa lanzado, siendo la última cadena un nulo (un cero). El ejemplo anterior se resolvería así:

char* tira [] = { "ls", "-l", "/usr/include", 0 };...execv ( "/bin/ls", tira );

Page 38: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

En los anteriores ejemplos se ha escrito el nombre completo del fichero para ejecutar ("/bin/ls" en vez de "ls" a secas). Esto es porque tanto execl como execv ignoran la variable PATH, que contiene las rutas de búsqueda. Para tener en cuenta esta variable pueden usarse las versiones execlp o execvp.

Por ejemplo:

execvp ( "ls", tira );

ejecutaría el programa "/bin/ls", si es que la ruta "/bin" está definida en la variable PATH.

Page 39: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Todas las llamadas exec... retornan un valor no nulo si el programa no se ha podido ejecutar.

En caso de que sí se pueda ejecutar el programa, se transfiere el control a éste y la llamada exec... nunca retorna.

En cierta forma el programa que invoca un exec desaparece del mapa.

Page 40: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Procesos concurrentes

Page 41: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Procesos concurrentes: llamadas fork y wait

Para crear nuevos procesos, el UNIX dispone únicamente de una llamada al sistema, fork, sin ningún tipo de parámetros. Su prototipo es:

int fork();

Al llamar a esta función se crea un nuevo proceso (proceso hijo), idéntico en código y datos al proceso que ha realizado la llamada (proceso padre). Los espacios de memoria del padre y el hijo son disjuntos, por lo que el proceso hijo es una copia idéntica del padre que a partir de ese momento sigue su vida separada, sin afectar a la memoria del padre; y viceversa.

Page 42: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Siendo más concretos, las variables del proceso padre son inicialmente las mismas que las del hijo. Pero si cualquiera de los dos procesos altera una variable, el cambio sólo repercute en su copia local. Padre e hijo no comparten memoria.

El punto del programa donde el proceso hijo comienza su ejecución es justo en el retorno de la función fork, al igual que ocurre con el padre.

Si el proceso hijo fuera un mero clon del padre, ambos ejecutarían las mismas instrucciones, lo que en la mayoría de los casos no tiene mucha utilidad. El UNIX permite distinguir si se es el proceso padre o el hijo por medio del valor de retorno de fork. Esta función devuelve un cero al proceso hijo, y el identificador de proceso (PID) del hijo al proceso padre. Como se garantiza que el PID siempre es no nulo, basta aplicar un if para determinar quién es el padre y quién el hijo para así ejecutar distinto código.

Page 43: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

main()

{

int x=1;

if ( fork()==0 )

{

printf ("Soy el hijo, x=%d\n",x);

} else {

x=33;

printf ("Soy el padre, x=%d\n",x);

}

}

Con un pequeño ejemplo:

Page 44: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Este programa mostrará por la salida estándar las cadenas "Soy el hijo, x=1" y "Soy el padre, x=33", en un orden que dependerá del compilador y del planificador de procesos de la máquina donde se ejecute.

Page 45: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

1 if ( fork()==0 )

2 {

3 execlp ("ls","ls","-l","/usr/include",0);

4 printf ("Si ves esto, no se pudo ejecutar el programa\n");

5 exit(1);

6 }

7 else

8 {

9 int tonta;

10 wait(&tonta);

11 }

Como aplicación de fork a la ejecución de programas, véase este otro pequeño ejemplo, que además nos introducirá en nuevas herramientas:

Page 46: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Este fragmento de código lanza a ejecución la orden ls -l /usr/include, y espera por su terminación.

En la línea 1 se verifica si se es padre o hijo. Generalmente es el hijo el que toma la iniciativa de lanzar un fichero a ejecución, y en las líneas 2 a la 6 se invoca a un programa con execlp. La línea 4 sólo se ejecutará si la llamada execlp no se ha podido cumplir. La llamada a exit garantiza que el hijo no se dedicará a hacer más tareas.

Las líneas de la 8 a la 11 forman el código que ejecutará el proceso padre, mientras el hijo anda a ejecutar sus cosas. Aparece una nueva llamada el sistema, la función wait. Esta función bloquea al proceso llamador hasta que alguno de sus hijos termina. Para nuestro ejemplo, dejará al padre bloqueado hasta que se ejecute el programa lanzado o se ejecute la línea 5, terminando en ambos casos el discurrir de su único hijo.

Page 47: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Es decir, la función wait es un mecanismo de sincronización entre un proceso padre y sus hijos.

La llamada wait recibe como parámetro un puntero a entero donde se deposita el valor devuelto por el proceso hijo al terminar; y retorna el PID del hijo. El PID del hijo es una información que puede ser útil cuando se han lanzado varios procesos hijos y se desea discriminar quién exactamente ha terminado. En nustro ejemplo no hace falta este valor, porque sólo hay un hijo por el que esperar.

Page 48: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Como ejemplo final, esta función en C es una implementación sencilla de la función system, haciendo uso de fork, wait y una función exec.

int system ( const char* str ) {

int ret;

if ( !fork() ){

execlp ( "sh", "sh", "-c", str, 0 );

return -1;

}

wait (&ret);

return ret;

}

Page 49: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Comunicación entre procesos (IPC)

Page 50: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Comunicación entre procesos (IPC)

• Los procesos en UNIX no comparten memoria, ni siquiera los padres con sus hijos. Por tanto, hay que establecer algún mecanismo en caso de que se quiera comunicar información entre procesos concurrentes.

• El sistema operativo UNIX define tres clases de herramientas de comunicación entre procesos (IPC): los semáforos, la memoria compartida y los mensajes.

Page 51: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Semáforos

¿Qué es un semáforo para el UNIX? Formalmente es muy similar a la definición clásica de Dijkstra, en el sentido de que es una variable entera con operaciones atómicas de inicialización, incremento y decremento con bloqueo.

El UNIX define tres operaciones fundamentales sobre semáforos:

* semget Crea o toma el control de un semáforo* semctl Operaciones de lectura y escritura del estado del semáforo. Destrucción del semáforo* semop Operaciones de incremento o decremento con bloqueo

Page 52: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    A veces es necesario que dos o más procesos o hilos (threads) accedan a un recurso común (escribir en un mismo fichero, leer la misma zona de memoria, escribir en la misma pantalla, etc). El problema es que si lo hacen simultáneamente y de forma incontrolada, pueden "machacar" el uno la operación del otro (y dejar el fichero o la memoria con un contenido inservible o la pantalla ilegible).

    Para evitar este problema, están los semáforos. Un semáforo da acceso al recurso a uno de los procesos y se lo niega a los demás mientras el primero no termine. Los semáforos, junto con la memoria compartida y las colas de mensajes, son los recursos compartidos que suministra UNIX para comunicación entre procesos.

Page 53: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

   El funcionamiento del semáforo es como el de una variable contador. Imaginemos que el semáforo controla un fichero y que inicialmente tiene el valor 1 (está "verde"). Cuando un proceso quiere acceder al fichero, primero debe decrementar el semáforo. El contador queda a 0 y como no es negativo, deja que el proceso siga su ejecución y, por tanto, acceda al fichero.

    Ahora un segundo proceso lo intenta y para ello también decrementa el contador. Esta vez el contador se pone a -1 y como es negativo, el semáforo se encarga de que el proceso quede "bloqueado" y "dormido" en una cola de espera. Este segundo proceso no continuará por tanto su ejecución y no accederá al fichero.

Page 54: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    Supongamos ahora que el primer proceso termina de escribir el fichero. Al acabar con el fichero debe incrementar el contador del semáforo. Al hacerlo, este contador se pone a 0. Como no es negativo, el semáforo se encarga de mirar el la cola de procesos pendientes y "desbloquear" al primer proceso de dicha cola. Con ello, el segundo proceso que quería acceder al fichero continua su ejecución y accede al fichero.

    Cuando este proceso también termine con el fichero, incrementa el contador y el semáforo vuelve a ponerse a 1, a estar "verde".

Page 55: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    Es posible hacer que el valor inicial del semáforo sea, por ejemplo, 3, con lo que pasarán los tres primeros procesos que lo intenten. Pueden a su vez quedar muchos procesos encolados simultáneamente, con lo que el contador quedará con un valor negativo grande. Cada vez que un proceso incremente el contador (libere el recurso común), el primer proceso encolado despertará. Los demás seguirán dormidos.

    Como vemos, el proceso de los semáforos requiere colaboración de los procesos. Un proceso debe decrementar el contador antes de acceder al fichero e incrementarlo cuando termine. Si los procesos no siguen este "protocolo" (y pueden no hacerlo), el semáforo no sirve de nada.

Page 56: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Memoria compartida

Las utilidades de memoria compartida permiten crear segmentos de memoria a los que pueden acceder múltiples procesos, pudiendo definirse restricciones de acceso (sólo lectura).

Para trabajar con un segmento de memoria compartida, es necesario crear un vínculo (attachment) entre la memoria local del proceso interesado y el segmento compartido. Esto se realiza con la función shmat. El proceso que vincula un segmento de memoria compartida cree estar trabajando con ella como si fuera cierta área de memoria local. Para deshacer el vínculo está la función shmdt.

Page 57: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

COMANDOS DE UNIX UTILES

    Unos comandos de UNIX que nos pueden resultar de utilidad son ipcs y ipcrm.

    ipcs nos da un listado de recursos compartidos que están creados en ese momento.

    ipcrm nos permite eliminar algunos de estos recursos. Si paramos el programa con un Ctrl-C o simplemente sale de forma anormal, el recurso (la memoria compartida) no se libera y queda en el sistema. La forma de borrarla sería con este comandos.

    Es bastante normal mientras desarrollamos y depuramos nuestro programa que se nos caiga, lo abortemos, etc. El número de memorias compartidas que podemos crear está limitado en UNIX, así que a la cuarta o quinta prueba empezaremos a obtener errores de que no se pueden crear las memorias. ipcrm nos permite eliminar  las memorias que se nos han quedado "pendientes" en nuestras pruebas.

Page 58: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

COLAS DE MENSAJES

    Las colas de mensajes, junto con los semáforos y la memoria compartida son los recursos compartidos que pone unix a disposición de los programas para que puedan intercambiarse información.

    En C para unix es posible hacer que dos procesos (dos programas) distintos sean capaces de enviarse mensajes (estructuras de datos) y de esta forma pueden intercambiar información. El mecanismo para conseguirlo es el de una cola de mensajes. Los procesos introducen mensajes en la cola y se van almacenando en ella. Cuando un proceso extrae un mensaje de la cola, extrae el primer mensaje que se introdujo y dicho mensaje se borra de la cola.

Page 59: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Sockets

Page 60: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

En una red de ordenadores hay varios ordenadores que están conectados entre si por un cable. A través de dicho cable pueden transmitirse información. Es claro que deben estar de acuerdo en cómo transmitir esa información, de forma que cualquiera de ellos pueda entender lo que están transmitiendo los otros, de la misma forma que nosotros nos ponemos de acuerdo para hablar en inglés cuando uno es italiano, el otro francés, el otro español y el otro alemán.

Al "idioma" que utilizan los ordenadores para comunicarse cuando están en red se le denomina protocolo. Hay muchísimos protocolos de comunicación, entre los cuales el más extendido es el TCP/IP.

El más extendido porque es el que se utiliza en Internet.

Page 61: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Con C en Linux/Unix tenemos una serie de funciones que nos permiten enviar y recibir datos de otros programas, en C o en otros lenguajes de programación, que estén corriendo en otros ordenadores de la misma red.

Page 62: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

LOS SOCKETS

Una forma de conseguir que dos programas se transmitan datos, basada en el protocolo TCP/IP, es la programación de sockets. Un socket no es más que un "canal de comunicación" entre dos programas que corren sobre ordenadores distintos o incluso en el mismo ordenador.

Desde el punto de vista de programación, un socket no es más que un "fichero" que se abre de una manera especial. Una vez abierto se pueden escribir y leer datos de él con las habituales funciones de read() y write() del lenguaje C.

Page 63: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Existen básicamente dos tipos de "canales de comunicación" o sockets, los orientados a conexión y los no orientados a conexión.

Page 64: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

orientados a conexión

ambos programas deben conectarse entre ellos con un socket y hasta que no esté establecida correctamente la conexión, ninguno de los dos puede transmitir datos. Esta es la parte TCP del protocolo TCP/IP, y garantiza que todos los datos van a llegar de un programa al otro correctamente.

Se utiliza cuando la información a transmitir es importante, no se puede perder ningún dato y no importa que los programas se queden "bloqueados" esperando o transmitiendo datos.

Si uno de los programas está atareado en otra cosa y no atiende la comunicación, el otro quedará bloqueado hasta que el primero lea o escriba los datos.

Page 65: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

No es necesario que los programas se conecten.

Cualquiera de ellos puede transmitir datos en cualquier momento, independientemente de que el otro programa esté "escuchando" o no.

Es el llamado protocolo UDP, y garantiza que los datos que lleguen son correctos, pero no garantiza que lleguen todos.

Se utiliza cuando es muy importante que el programa no se quede bloqueado y no importa que se pierdan datos.

no orientados a conexión.

Page 66: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Imaginemos, por ejemplo, un programa que está controlando la temperatura de un horno y envía dicha temperatura a un ordenador en una sala de control para que éste presente unos gráficos de temperatura. Obviamente es más importante el control del horno que el perfecto refresco de los gráficos.

Normalmente cuando se habla de sockets se únicamente a sockets TCP. Los UDP son básicamente iguales, aunque hay pequeñas diferencias en la forma de abrirlos.

Page 67: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

ARQUITECTURA CLIENTE / SERVIDOR

A la hora de comunicar dos programas, existen varias posibilidades para establecer la conexión inicialmente. Una de ellas es la utilizada aquí. Uno de los programas debe estar arrancado y en espera de que otro quiera conectarse a él. Nunca da "el primer paso" en la conexión. Al programa que actúa de esta forma se le conoce como servidor. Su nombre se debe a que normalmente es el que tiene la información que sea disponible y la "sirve" al que se la pida. Por ejemplo, el servidor de páginas web tiene las páginas web y se las envía al navegador que se lo solicite.

El otro programa es el que da el primer paso. En el momento de arrancarlo o cuando lo necesite, intenta conectarse al servidor. Este programa se denomina cliente. Su nombre se debe a que es el que solicita información al servidor. El navegador de Internet pide la página web al servidor de Internet.

Page 68: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

En este ejemplo, el servidor de páginas web se llama servidor porque está (o debería de estar) siempre encendido y pendiente de que alguien se conecte a él y le pida una página. El navegador de Internet es el cliente, puesto que se arranca cuando nosotros lo arrancamos y solicita conexión con el servidor cuando nosotros escribimos, por ejemplo, www.marca.com

Page 69: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

En el juego del Quake, debe haber un servidor que es el que tiene el escenario del juego y la situación de todos los jugadores en él. Cuando un nuevo jugador arranca el juego en su ordenador, se conecta al servidor y le pide el escenario del juego para presentarlo en la pantalla. Los movimientos que realiza el jugador se transmiten al servidor y este actualiza escenarios a todos los jugadores.

Resumiendo, servidor es el programa que permanece pasivo a la espera de que alguien solicite conexión con él, normalmente, para pedirle algún dato. Cliente es el programa que solicita la conexión para, normalmente, pedir datos al servidor.

Page 70: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

LA CONEXIÓN

Para poder realizar la conexión entre ambos programas son necesarias varias cosas:

• Dirección IP del servidor.

• Servicio que queremos crear / utilizar.

Page 71: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Servicio que queremos crear / utilizar.

En un mismo ordenador pueden estar corriendo varios programas servidores, cada uno de ellos dando un servicio distinto. Por ejemplo, un ordenador puede tener un servidor de Quake y un servidor de páginas web corriendo a la vez. Cuando un cliente desea conectarse, debe indicar qué servicio quiere.

Por ello, cada servicio dentro del ordenador debe tener un número único que lo identifique. Estos números son enteros normales y van de 1 a 65535. Los número bajos, desde 1 a 1023 están reservados para servicios habituales de los sistemas operativos (www, ftp, mail, ping, etc). El resto están a disposición del programador y sirven para cosas como Quake.

Page 72: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Tanto el servidor como el cliente deben conocer el número del servicio al que atienden o se conectan. El servidor le indica al sistema operativo qué servicio quiere atender.

En el caso del navegador de Internet, estamos indicando el servicio con la www en www.marca.com, servicio de páginas web. También es posible, por ejemplo "ftp.marca.com", si marca.com admite clientes ftp.

Estos servicios están detallados en el fichero /etc/services .

Page 73: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

RPC (Remote Procedure Call)

Page 74: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    En unix es posible tener en ejecución un programa en C con varias funciones que pueden ser llamadas desde otro programas. Estos otros programas pueden estar corriendo en otros ordenadores conectados en red.

    Supongamos, por ejemplo, que tenemos un ordenador muy potente en cálculo matemático y otro con un buen display para gráficos. Queremos hacer un programa con mucho cálculo y con unos gráficos "maravillosos". Ninguno de los dos ordenadores cumple con ambos requisitos. Una solución, utilizando RPC (Llamada a procedimientos remotos), consiste en programar las funciones matemáticas en el ordenador de cálculo y publicar dichas funciones. Estas funciones podrán ser llamadas por el ordenador gráfico, pero se ejecutarán en el ordenador de cálculo. Por otro lado, hacemos nuestros gráficos en el ordenador gráfico y cuando necesitemos algún cálculo, llamamos a las funciones del ordenador de cálculo.

Page 75: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    Al programa con las funciones se le llama "servidor". Al programa que llama a esas funciones se le llama "cliente".

Normalmente el servidor está siempre corriendo y a la espera de que algún cliente llame a alguna de sus funciones.

Cuando el cliente llama a una función del servidor, la función se ejecuta en el servidor y el cliente detiene su ejecución hasta que el servidor termina.

Page 76: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    En el código del programa servidor básicamente hay que seguir los siguientes pasos:

Codificar las funciones que van a ser llamadas siguiendo un determinado mecanismo. Informar al sistema operativo (unix) de un nombre, una versión y funciones que publica. Meterse en un bucle infinito esperando que alguien llame a alguna de sus funciones.

    Mientras que el programa cliente debe:

Establecer una conexión con el servidor.Llamar a las funciones.Cerrar la conexión con el servidor.

Page 77: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    También es posible hacer "tipos" de mensajes distintos, de forma que cada tipo de mensaje contiene una información distinta y va identificado por un entero. Por ejemplo, los mensajes de tipo 1 pueden contener el saldo de una cuenta de banco y el número de dicha cuenta, los de tipo 2 puden contener el nombre de una sucursal bancaria y su calle, etc. Los procesos luego pueden retirar mensajes de la cola selectivamente por su tipo. Si un proceso sólo está intersado en saldos de cuentas, extraería únicamente mensajes de tipo 1, etc.

Page 78: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

SEÑALES Y ALARMAS

Page 79: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    Una señal es un "aviso" que puede enviar un proceso a otro proceso. El sistema operativo unix se encarga de que el proceso que recibe la señal la trate inmediatamente. De hecho, termina la línea de código que esté ejecutando y salta a la función de tratamiento de señales adecuada. Cuando termina de ejecutar esa función de tratamiento de señales, continua con la ejecución en la línea de código donde lo había dejado.

Page 80: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    El sistema operativo envía señales a los procesos en determinadas circunstancias. Por ejemplo, si en el programa que se está ejecutando en una shell nosotros apretamos Ctrl-C, se está enviando una señal de terminación al proceso. Este la trata inmediatamente y sale. Si nuestro programa intenta acceder a una memoria no válida (por ejemplo, accediendo al contenido de un puntero a NULL), el sistema operativo detecta esta circunstancia y le envía una señal de terminación inmediata, con lo que el programa "se cae".

    Las señales van identificadas por un número entero. No vamos a detallar todas, pero sí alguna de las más interesantes para el programador.

Page 81: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Nombre de señal

Nº Propósito. Se envia al proceso cuando...

SIGINT  2 ... se pulsa Ctrl-C

SIGFPE 8... hay un error en coma flotante (ejemplo, división por cero)

SIGPIPE 13... se intenta escribir en una conexión (socket, tubería, ...) rota (no hay proceso leyendo al otro lado).

SIGALRM 14 ... cuando termina un temporizador.

SIGUSR1 16... el programador lo decide. Esta señal es para uso del programador. No la utiliza el sistema operativo.

SIGUSR2 17 ... el programador lo decide. Idem a la anterior

Page 82: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

    Nosotros, desde un shell de comandos unix, podemos enviar señales a los procesos con el comando kill. Su uso más conocido es el kill -9 idProceso, que envía una señal 9 al proceso, haciendo que termine. También es posible enviar señales desde un programa en C a otro programa en C que esté corriendo en el mismo ordenador.

   

Page 83: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Todas las señales tienen una función de tratamiento por defecto. Cuando nuestro programa está en ejecución, si recibe una señal, saltará a la función por defecto para tratamiento de esa señal y luego continuará su ejecución normal (si la señal no es de terminación). Todo esto es totalmente invisible para nosotros. Únicamente lo notaremos si nuestro programa se sale inesperadamente.

    Sin embargo, nuestro programa puede cambiar casi todas las funciones por defecto de tratamiento de señales, pudiendo hacer que al recibir una señal el programa haga lo que nosotros queramos.

Page 84: Llamadas al sistema para manejo de ficheros. Estas funciones se suelen denominar "de bajo nivel". La biblioteca estándar también ofrece otras rutinas.

Alarmas

    Las alarmas las genera la función alarm(). Una vez llamada esta función, pasándole como parámetro un tiempo en segundos, se inicia una cuenta atrás de esa duración.

Una vez terminada, se envía al proceso una señal SIGALRM, que se puede capturar. De esta forma podemos, por ejemplo, mantener en pantalla actualizado un reloj o mostrar una información durante un tiempo determinado y luego borrarla.

    Para tiempos más finos, tenemos la función setitimer(), algo más compleja, pero que permite definir tiempos de micro-segundos. Esta función permite además que se nos avise repetidamente a intervalos de tiempo fijos.