CAPÍTULO I. Conceptos fundamentales de
UNIX.
Tipos de archivos
Programas y procesos
Señales
Process-ID
Permisos
Otros atributos de proceso
TIPOS
DE ARCHIVOS
Ficheros ordinarios: contienen bytes de datos
organizados en vectores lineales. No tienen nombre, se nombran por medio de
números (i-number). Estos números son los índices a la tabla de i-nodes. Cada
i-node consta de las siguientes partes:
-
Tipo de fichero (ordinario, directorio o especial)
-
Número de links
-
user-ID y group-ID
-
Permisos para el propietario, grupo y otros
-
Tamaño en bytes
-
Fecha del último uso, última modificación y último cambio de permisos
y
-
Punteros a los bloques de memoria donde se encuentran los datos del
fichero
Directorios: Se permite usar nombres en los
directorios para designar a los ficheros que contienen. Cada directorio consta
de una con el nombre del fichero y otra con el i-number del fichero correspondiente, un link será el
par nombre/i-nodo.
Cuando se le manda al kernel
acceder a un fichero por nombre, se busca en el directorio el nombre y su
correspondiente i-number, a continuación se busca el i-nodo del fichero que se
corresponde con ese i-number y el i-nodo nos dice donde encontrar el fichero en
el disco. Por ejemplo, con el path memo/julio/oscar se busca el i-nodo del
directorio actual para localizar sus bytes de datos, encontramos memo entre
ellos, extraemos su i-number y de él
sacamos su i-nodo para encontrar los bytes de datos de memo, entre ellos
buscamos julio y cogemos su i-number, de él sacamos el i-nodo para localizar
sus bytes de datos y encontramos oscar, cogiendo su correspondiente i-nodo.
Ficheros especiales: se dividen en block special
files que contienen vectores de bloques de tamaño fijo para controlar I/O y
character special files que no necesitan regalas especiales para su
funcionamiento y pueden hacer I/O.
PROGRAMAS Y PROCESOS
Programa: son datos e instrucciones
guardados en un fichero ordinario o en disco. Su i-nodo lo marca como ejecutable. Para crearlo se escribe el
programa en un lenguaje de programación (C para UNIX), lo guardamos como
fichero de texto y por medio de un compilador se traduce a lenguaje máquina
obteniendo un fichero objeto y si no tiene fallos se crea el ejecutable.
Procesos: Para ejecutar un programa, el kernel
crea un entorno donde se pueda desarrollar (proceso), que consta de segmento de
instrucciones, segmento de datos de usuario y segmento de datos del sistema. El
proceso proporciona recursos como ficheros abiertos, más memoria... que el
programa no presenta.
SEÑALES
Se mandan del
kernel al proceso para indicar por ejemplo una violación de segmento, alarma de
reloj...
Hay unas 19 señales, el proceso podrá aceptar la acción asociada por
defecto a la señal recibida (finalización del proceso), puede ignorar la señal
o bien la puede capturar pasándola como parámetro (número del 1 al 19) a una
subrutina de tratamiento de la señal.
PROCESS-ID (PID)
Todos procesos
se designan con un entero positivo único llamado pid. Todos procesos menos uno
(proceso 0 que es creado por el propio kernel) tienen un proceso padre. Los
datos del sistema de un proceso guarda su ppid (pid del proceso padre). Si un
hijo se queda huérfano al finalizar su padre antes que él, toman como ppid 1
que es el pid del proceso de inicialización INIT.
PERMISOS
El user-ID
(uid) es un entero positivo asociado con el login de un usuario en el fichero
de passwords (/etc/passwd). Cuando un usuario entra al sistema, el comando
login hace este ID el uid del primer proceso creado (login shell). Los procesos
que descienden tienen el mismo uid.
Los usuarios
también se dividen en grupos que tienen sus propios Ids (gid). Los grupos están
definidos en /etc/group.
Un usuario se
puede cambiar de grupo, lo que hace que su gid cambie, este es heredado por los
hijos del proceso que cambia de grupo. Estos dos Ids vistos hasta ahora se
llaman ruid (real user ID) y rgid (real group ID).
Otros Ids son
el effective user-ID (euid) y el effective group-ID (egid) que normalmente son
iguales a los anteriores. El effective ID indica permisos y el real ID la
identidad. Cada fichero tiene en su i-nodo (como se ha visto antes) un uid del
propietario y un gid del grupo del propietario.
Existen 9 bits
de permisos (3 para el usuario, para el grupo y para otros de lectura,
escritura y ejecución). Si euid es 0, el usuario va a ser el superuser
(administrador del sistema)
OTROS ATRIBUTOS DE PROCESO
Otros
atributos serán grabados en el segmento de datos de sistema del proceso.
Uno de ellos
va a ser el descriptor de ficheros (fd) que es un entero del 0 al 19 (un
descriptor de ficheros para cada proceso que se abre y 2 para cada tubería que
se crea). El hijo no hereda los descriptores de fichero de los ficheros
abiertos por el padre sino que hereda copias de ellos. Son índices a la tabla
de procesos abiertos (padre e hijo tienen el mismo puntero de fichero)
CAPÍTULO II. Conceptos básicos de
ficheros de E/S
Descriptores de
fichero
creat
open
write
read
close
lseek
DESCRIPTORES
DE FICHERO
Cada proceso tiene un conjunto de 20 descriptores de fichero (fd)
numerados del 0 al 19. Los tres primeros se abren automáticamente cuando el
proceso comienza (0 es la entrada estándar, 1 la salida estándar y 2 la salida
estándar de errores). Un proceso UNIX leerá de 0, escribirá en 1 y usará 2 para
mensajes importantes. Estos tres fd están preparados para ser usados
inmediatamente con read y write; los otros 17 se pueden usar con ficheros,
tuberías (pipes) y ficheros especiales que el proceso abre para su uso propio.
Hay cinco llamadas que producen fd, serán creat, open, fcntl, pipe y
dup.
CREAT
int
creat(path,permisos) crea un fichero
char
*path
nombre del path
int
permisos bits de permiso
Devuelve fd o –1 si ha habido error.
Se comporta de distinta forma dependiendo de si el fichero existe
o no.
Si el fichero no existe, se crea un nuevo i-nodo y un link a él se pone
en el directorio en donde se va a crear; euid y egid del proceso que llama
deben tener permiso en este directorio, convirtiéndose en propietarios del
fichero.
Si el fichero ya existe, no se necesita permiso de escritura en el
directorio al que se linka, solamente permiso de ejecución (búsqueda), de todos
modos euid y egid deben tener permiso de escritura en el fichero.
Un ejemplo es fd0=creat(“temp”,0666)
OPEN
Hay dos formas para esta llamada. La antigua forma es:
int
open(path,flags) abrir
el fichero
char *path; nombre del path
int
flags; read,write o ambas
Devuelve fd o –1 si hay error. El fichero dado por path debe existir
previamente.
Los flags son 0 para lectura, 1 para escritura y 2 para
lectura/escritura. El fd devuelto se podrá usar luego para otras llamadas como
lseek, que veremos más adelante; euid y egid deben tener permiso de lectura y/o
escritura dependiendo de los flags que se tengan.
Una versión más moderna de esta llamada es la siguiente:
#include
<fcntl.h>
int
open(path,flags,permisos)
char *path;
int flags;
int permisos;
Devuelve fd o –1 si hay error. En lugar de usar números para los flags
hay constantes simbólicas incluidas en usr/include/fcntl.h
WRITE
int write(fd,buf,nbytes) escritura en el fichero
int fd;
descriptor de fichero
char
*buf; dirección del buffer
unsigned
nbytes; número de bytes a escribir
Devuelve el número de bytes escritos o –1 si se ha cometido algún
fallo. Esta orden escribe nbytes apuntados en buf al fichero abierto
representado por fd. Se puede usar para escribir en tuberías, pero esto se verá
más adelante.
READ
int
read(fd,buf,nbytes)
se lee de fichero
int fd;
descriptor de fichero
char
*buf; dirección del buffer
unsigned
nbytes; número de bytes a leer
Devuelve el número de bytes leído o 0 en caso de EOF o –1 si se produce
error.
CLOSE
int
close(fd) se cierra el fichero
int fd; descriptor de fichero
Devuelve 0 si se lleva a cabo
correctamente o –1 si no es así. No hace falta usar esta llamada ya que el
fichero se cierra automáticamente cuando termina el proceso.
LSEEK
long lseek(fd,offset,interp) mueve el puntero de fichero
int fd; descriptor
de ficheros
long offset; offset en el fichero
int interp; interpretación del offset
Devuelve el valor del puntero
de fichero o –1 si hay error. Sitúa el puntero de fichero para la próxima
lectura o escritura.
CAPÍTULO V. Procesos
exec
fork
exit
wait
Llamadas para conocer Ids (get Ids)
Generación de
Ids y proceso de arranque
Antes e empezar, debemos recordar que un programa es un conjunto de
instrucciones y datos usados para inicializar la instrucción y el segmento de
datos de un proceso, mientras que el entorno es la instrucción y los segmentos
de datos del usuario y del sistema asociados a ella.
Exec reinicializa un proceso
de un determinado programa; el programa cambia mientras el proceso se mantiene.
Fork crea un nuevo proceso exactamente igual a otro existente, copiando la
instrucción y los segmentos de datos del usuario y del sistema, el nuevo
proceso no es inicializado desde un programa.
Exec es la única forma de que un programa se ejecute en UNIX, y fork es
la única manera de crear procesos.
EXEC
El proceso 1, que ya existe, ejecuta un programa, exec reemplaza el
proceso 1 con el nuevo programa. El pid del proceso 1 no cambia (el proceso
pasa a ejecutar B en lugar de A). Hay un nuevo programa ejecutándose en el
mismo contexto, el ppid no cambia, la instrucción exec sólo devuelve el control
al llamador si hay error. Hay distintos tipos, vamos a ver los más importantes:
-
execl
int execl(path,arg0,arg1,...,argn,0)
char *path;
char *arg0; primer argumento (nombre de fichero)
char *arg1;
............
char *argn;
Devuelve –1 si hay error, el
programa busca en path, siempre hay un 0 al final; fin de los argumentos: el
entorno se pasa automáticamente.
-
execv
int execv(path,argv)
char *path;
char *argv[] punteros a los argumentos
No busca en path, los
argumentos son dados como un vector de punteros (tengo en mi programa un vector
llamado argv cuyos elementos son punteros a memoria)
-
execle
int execle(path,arg0,...,argn,0,envp)
char *path;
char *ârg0;
..............
char *argn;
char *envp[]; punteros a las variables de entorno
-
execlp
int execlp(file,arg0,...,argn,0)
char *file nombre del fichero de programa
char *arg0;
............
char *argn;
Busca en path (“ls”)
-
execvp
int execvp(file,argv)
char *file;
char *argv[];
Busca en path.