FSC CHECKLIST
¡Hola! Te propongo un listado con detalles y conceptos esenciales de la asignatura. Comprueba
que ya los dominas cuando estés repasando para el examen de laboratorio. ¡Espero que te sea
útil!
✓ Entiendo la diferencia entre array de caracteres y cadena de caracteres. Sé convertir un
array de caracteres en una cadena
✓ Sé que sólo las cadenas pueden usarse en funciones printf(), atoi(), strlen(), strcmp(),
strcpy(), strcat(), strstr()...
✓ No utilizo sizeof() para medir el tamaño en bytes de un "array"
✓ Entiendo cómo indicar la dirección de memoria de una variable de pila (con &)
✓ Entiendo cómo indicar la dirección de memoria de un array (el nombre del array)
✓ Entiendo cómo indicar la dirección de memoria de una casilla concreta del array (con &
y el corchete que indica la casilla en cuestión)
✓ Manejo la aritmética de punteros para recorrer un array con un puntero
✓ Entiendo que cuando se utiliza un main con argumentos, los diferentes argumentos
argv[0], argv[1]..., son cadenas de caracteres.
✓ Sé que, si se esperan argumentos en mi programa, hay que chequear el valor de la
variable argc (la cuenta de argumentos) antes de poder seguir ejecutando.
✓ Sé abrir un fichero que existe para lectura (open() y modo O_RDONLY)
✓ Sé crear un fichero para escritura (creat() y permisos -por ejemplo, 0644-)
✓ Sé abrir un fichero que existe para escritura (open() y modo O_WRONLY)
✓ Sé escribir datos a un fichero, de cualquier tipo (write())
✓ Sé leer datos de un fichero y guardarlos en una zona de memoria (read())
✓ Entiendo que el tercer argumento de read() indica un MÁXIMO número de bytes a leer
del fichero, pero puede que se lean menos (int leidos = read(...))
✓ Manejo la lectura completa de un fichero, que es SIEMPRE por bloques, en un bucle
✓ Entiendo cómo salir del bucle que lee un fichero por bloques
✓ Sé cómo registrar una manejadora de señal, y lo hago siempre al principio de mi
programa
✓ Entiendo la diferencia entre registrar un manejador de señal y lanzar una señal
✓ Sé lanzar señales a mi proceso desde la consola (comando kill)
✓ Entiendo la diferencia entre compilar mi programa en BSD o System V, y sé implementar
una función manejadora de señal PORTABLE.
✓ Sé que una alarma hace que el S.O. le lance a mi programa la señal SIGALRM
✓ Sé que una alarma activa se puede desactivar con un alarm(0).
✓ Entiendo que, para crear un temporizador periódico, necesito utilizar setitimer()
✓ Sé que un temporizador periódico puede desactivarse si se vuelve a llamar con los
campos de una variable de tipo struct itimerval a cero.
1
✓ Entiendo la diferencia entre un ITIMER_VIRTUAL y un ITIMER_REAL, y su interacción
con pause()
✓ Manejo con soltura la clonación de un proceso con fork() y entiendo cómo conseguir
zonas exclusivas de ejecución para un padre (el proceso original) y un hijo (el clon)
✓ Entiendo que un proceso hijo debe terminar el programa en su zona exclusiva de código
✓ Entiendo que el proceso padre debe esperar a cada hijo que haya creado usando
wait(0), y que esta llamada al sistema es BLOQUEANTE
✓ Sé que NO debo hacer el wait(0) en la zona exclusiva del padre cuando el padre tiene
más cosas que hacer. Y si el padre funciona de forma infinita, el wait(0) hay que
ejecutarlo en una manejadora de la señal SIGCHLD.
✓ Sé que cuando tengo abierto cualquier fichero antes de fork(), el hijo comparte el
fichero abierto con su padre
✓ He hecho algún ejemplo de mutación con execl() o execv() y entiendo sus argumentos
✓ Sabría implementar un system() combinando clonación+mutación+consola efímera
✓ Sé crear una tubería unidireccional con pipe() ANTES de clonar. Sé que el padre y el hijo
comparten los descriptores de lectura y escritura a la tubería y, por tanto, entiendo que
un proceso debe ser LECTOR y otro ESCRITOR
✓ Sé que, justo al principio de cada zona exclusiva del padre y del hijo, debo cerrar el
descriptor de la tubería que no se vaya a utilizar
✓ Entiendo que cuando se va a leer de una tubería sin datos, read() es bloqueante.
✓ Entiendo que cuando se va a escribir en una tubería llena, write() es bloqueante.
✓ Entiendo que puede que write() no consiga escribir todos los bytes que se le indican
como tercer argumento.
✓ Entiendo que las tuberías no almacenan mensajes, sino secuencias de bytes, así que
dos write() en un proceso no tienen por qué equivaler a dos read() en otro proceso
✓ Sé que las funciones writen() y readn() ayudan a garantizar que se escriben y leen,
exactamente, los bytes que se indican como tercer argumento. En ese caso, cada
writen() en un proceso equivale a un readn() en el otro proceso
✓ Manejo la creación de tuberías fifo en el sistema de ficheros con el comando mkfifo
✓ Entiendo que abrir una fifo para lectura o escritura es BLOQUEANTE, hasta que otro
proceso abra esa fifo para la acción contraria
✓ Sé simular un lector y un escritor de una fifo por consola, con el comando cat
✓ Sé que cuando un escritor cierra una tubería con close(), se desbloquea el read() en el
lector, devolviendo el valor cero. Este valor cero NO es un error, sino la forma de saber
que ya no se debe seguir leyendo de la tubería (sea pipe, fifo o socket TCP).
✓ Entiendo bien la utilidad de un select() como multiplexor que sondea descriptores de
ficheros que son bloqueantes por separado
✓ Manejo el concepto de las variables de tipo fd_set, y entiendo que son modificadas por
select() cuando alguno de sus descriptores está listo para E/S
✓ Entiendo que select() se suele utilizar en un bucle, y que antes de volver a ejecutar
select() hay que restaurar los valores de fd_set y tiempo a su valor original.
✓ Sé que el primer argumento de select() indica un número de casillas y que, a efectos
prácticos, esto significa que su valor es el máximo descriptor a sondear + 1
✓ Sé diferenciar un socket TCP de uno UDP
2
✓ Entiendo que un servidor debe vincular su socket a un puerto
✓ Manejo bien cómo rellenar variables de tipo struct sockaddr_in
✓ Siempre que envío datos que ocupan más de un byte los paso a formato de red antes
de enviarlos. El receptor los pasa a formato de host al recibirlos, y antes de usarlos.
✓ Nunca mando datos de tipo int por un socket. Utilizo uint8_t/int8_t, uint16_t/int16_t,
uint32_t/int32_t
✓ Entiendo que los enteros de ocho bits NO se pasan a formato de red. Para los de 16 bits
se utilizan htons()/ntohs(). Para los de 32 bits se utilizan htonl()/ntohl().
✓ Sé que los puertos siempre se rellenan, para una variable de tipo struct sockaddr_in, en
formato de red.
✓ Sé que las direcciones IP siempre se rellenan, para una variable de tipo struct
sockaddr_in, en formato de red.
✓ Entiendo que la función inet_addr() me devuelve una cadena con una dirección IP en
formato uint32_t en formato de red.
✓ Sé que los sockets UDP utilizan sendto() y recvfrom() para enviar mensajes completos
✓ Sé que los sockets TCP utilizan write() y read() porque se establece una conexión entre
cliente y servidor y, de cara al proceso, hay una tubería de bytes para envíos y otra para
recepción.
✓ Asumo que un servidor TCP no lo es hasta que no utiliza la llamada listen()
✓ Entiendo que accept() devuelve un descriptor conectado con el cliente. Y es ese nuevo
descriptor por el que se hacen las operaciones de lectura y escritura de bytes con él.
✓ Sé cómo implementar servidores que atiendan de forma concurrente a más de un
cliente, utilizando clonación.
✓ Entiendo la diferencia entre un servidor que utiliza fork() o que utiliza select() para
atender a más de un cliente a la vez.
✓ Manejo la estructura de una máquina de estados con dos variables: estado y evento, y
un doble switch anidado.
✓ Sé que, en cada iteración de la máquina de estados, se debe esperar un nuevo evento
que haga evolucionar la máquina.
✓ Entiendo que, en la función espera_evento() se bloquea el proceso y, tras
desbloquearse, la función chequea qué ha pasado y devolverá el resultado en forma de
evento concreto. NUNCA se cambia el estado de la máquina dentro de espera_evento().
✓ Entiendo cómo interpretar una cabecera de un mensaje de protocolo (IP, ICMP, UDP,
TCP, trama Ethernet) a partir de una figura del libro, y sé cómo crear una estructura en
C para rellenar sus campos.
✓ Entiendo cómo utilizar memcpy() para copiar la parte de la cabecera y la parte de datos
en un array de char, construyendo así una PDU completa que pueda enviarse por un
canal de comunicaciones.