ERROR USUARIO NO VALIDADO 1

MÓDULO DE FICHEROS

El módulo de ficheros se estructura en los siguientes apartados:


NOTA: No realizar estos ejercicios en carpetas compartidas con otro sistema operativo, pues los permisos de los ficheros pueden cambiar respecto de los indicados aquí. Esto ocurre por ejemplo en Linux virtualizado sobre Windows, si se han configurado carpetas compartidas. Basta con trabajar sobre carpetas de Linux que no estén compartidas.

INTRODUCCIÓN

Este módulo incide fundamentalmente en los servicios sobre ficheros y directorios suministrados por un sistema operativo tipo UNIX.

Los programas utilizados en el módulo son los siguientes:

Abrir.c CambiaDir.c carga1
CreaDir.c CreaHueco.c EnlaceF.c
EnlaceS.c Enmascarado.c Lectores.c
LeeyEscribe.c Listar_Directorio.c PunteroFichero.c
Redirige.c Robot_B.c Tuberia.c
Ventanilla_pth.c Ventanilla_srl.c

Los servicios UNIX utilizados en dichos programas son los siguientes:

chdir close dup
exec link mkdir
open opendir pipe
read readdir sched_yield
stat symlink umask
unlink write

Las funciones de biblioteca utilizadas en dichos programas son las siguientes:

FUNCIÓN F.CABECERA SINTAXIS Y BREVE DESCRIPCIÓN
lseek unistd.h * off_t lseek( int fildes, off_t offset, intwhence);
* La función lseek, reposiciona el puntero de lectura/escritura de un fichero, fildes, con el argumento offset, de acuerdo a la directiva whence.

Los mandatos utilizados en este módulo son:

cat cd chmod
echo gcc ls
od pwd pico
rm stat

DIRECTORIOS

La función principal de los directorios es presentar una visión lógica simple al usuario del sistema de directorios. Un directorio es un fichero que contiene una tabla de elementos de directorio. Un directorio contendrá tantos elementos como ficheros sean accesibles a través de él. Cada elemento es de tamaño variable e incluye el nodo_i y el nombre de un fichero.

Como ya sabemos, existen mandatos externos, como ls que nos permiten conocer el contenido de un directorio y las características de los elementos que contiene. Además, el lenguaje C, nos ofrece una serie de funciones de la familia opendir, que nos van a permitir acceder al contenido de cualquier directorio y conocer su contenido.

Analicemos el código del programa Listar_Directorio.c.

LD-01.- #define MYNAME  "Listar_Directorio"
LD-02.- #include <sys/types.h>
LD-03.- #include <sys/stat.h>
LD-04.- #include <dirent.h>
LD-05.- #include <errno.h>
LD-06.- #include <stdio.h>
LD-07.- #include <stdlib.h>
LD-08.- #include <unistd.h>
LD-09.- int main(int argc, char * argv[])
LD-10.- {
LD-11.-         DIR * dir;
LD-12.-         struct dirent *entrada;
LD-13.- 
LD-14.-         if (argc != 2) 
LD-15.-         {
LD-16.-                 printf("Uso: "MYNAME" directorio\n"); exit(0);
LD-17.-         }
LD-18.- 
LD-19.-         dir = opendir(argv[1]);
LD-20.-         if (dir == NULL) 
LD-21.-         {
LD-22.-                 perror(MYNAME": opendir()"); exit(1);
LD-23.-         }
LD-24.- 
LD-25.-         printf("%10s   %s\n", "Nodo_i", "Nombre");
LD-26.- 
LD-27.-         while ((entrada = readdir(dir)) != NULL) 
LD-28.-                 printf("%10ld   %s\n", entrada->d_ino, entrada->d_name);
LD-29.-         
LD-30.-         if (!entrada && errno != 0) 
LD-31.-         {
LD-32.-                 perror(MYNAME": readdir()"); exit(1);
LD-33.-         }
LD-34.-         closedir(dir);
LD-35.-         return 0;
LD-36.- }

A partir del código anterior, podemos deducir, que el programa Listar_Directorio recibe un argumento de entrada, que es el directorio que se quiere listar. Adicionalmente se utilizan dos estructuras de datos para manejar los directorios. Por un lado DIR, que es el tipo de datos que representa a un directorio, y por otra parte, la estructura dirent. Este último tipo es el que se utiliza para representar cada uno de los elementos del directorio. (Observe que es el tipo del valor devuelto por la función readdir).

La definición de la estructura dirent incluye como mínimo los siguientes elementos:

struct dirent { 
	long d_ino;              // Nodo_i
	char d_name[];           // Nombre del fichero
 };

Sin embargo, dicha definición depende de la plataforma concreta y podría incluir otros elementos. Por ejemplo:

struct dirent {
        u64             d_ino;       // Nodo_i
        s64             d_off;       // Posición en el fichero del elemento del directorio
        unsigned short  d_reclen;    // Tamaño del directorio
        unsigned char   d_type;      // Tipo del elemento
        char            d_name[0];   // Nombre del fichero
};

En la máquina triqui, podemos encontrar los ficheros de definición en el directorio /usr/include. En concreto el fichero dirent.h en el que se definen las estructuras previamente comentadas.



Compile el programa Listar_Directorio.c.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc Listar_Directorio.c -o Listar_Directorio


DIRECTORIO DE TRABAJO

Cada proceso posee una serie de atributos asociados mantenidos en su BCP, uno de ellos es el identificador del nodo_i de su directorio actual de trabajo. El servicio chdir permite cambiar ese atributo para el proceso que lo solicita. La función de biblioteca getcwd permite averiguar la ruta al directorio actual de trabajo.

Analice el código del programa CambiaDir.c

CD-01.- #define MYNAME "CambiaDir"
CD-02.- #include <limits.h>
CD-03.- #include <stdio.h>
CD-04.- #include <unistd.h>
CD-05.- int main(int argc, char * argv[])
CD-06.- {
CD-07.-    char path[PATH_MAX+1];
CD-08.-    if (argc != 2) 
CD-09.-       printf("Uso: %s directorio\n", MYNAME);
CD-10.-    else 
CD-11.-    {
CD-12.-       printf("%s:  Antes  cwd=%s\n", MYNAME, getcwd(path, PATH_MAX));
CD-13.-       if (chdir(argv[1]) < 0)
CD-14.-       {
CD-15.-          fprintf(stderr, "%s: ", MYNAME); perror(argv[1]);
CD-16.-       }
CD-17.-       printf("%s: Despues cwd=%s\n", MYNAME, getcwd(path, PATH_MAX));
CD-18.-    }
CD-19.-    return 0;
CD-20.- }

La ejecución de dicho programa cambia el directorio actual de trabajo del proceso CambiaDir. Es importante recordar que estos atributos son propios de los procesos, por lo que aunque un proceso cambie su directorio actual de trabajo, este cambio no afectará, por ejemplo, al proceso Shell desde el que se invocó el programa. Esto lo podemos comprobar en la siguiente secuencia de mandatos:

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ pwd
/home/alumnosSSOO/alumno/PracticasAnalisis/ModuloFicheros
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc CambiaDir.c -o CambiaDir
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./CambiaDir /
CambiaDir:  Antes  cwd=/home/alumnosSSOO/alumno/PracticasAnalisis/ModuloFicheros
CambiaDir: Despues cwd=/

Terminado el proceso CambiaDir se vuelve al Shell, que conserva su directorio de trabajo.

/home/alumnosSSOO/alumno/PracticasAnalisis/ModuloFicheros$ pwd
/home/alumnosSSOO/alumno/PracticasAnalisis/ModuloFicheros

Si se quiere cambiar el directorio actual de trabajo del Shell deberá ser el propio proceso Shell el que solicite el servicio chdir, no puede delegar esa tarea a un proceso hijo. Esta es la razón por la cual el mandato cd ha de ser interno y no externo. Este mandato interno cd además mantendrá actualizada la variable de entorno PWD con el valor de la ruta al directorio actual de trabajo del propio Shell.


SERVICIO OPEN

El servicio open permite abrir un fichero. Para ello se debe especificar el nombre del fichero (con su ruta de acceso) y una serie de opciones que especifica el modo de apertura del fichero. Las opciones de apertura son las siguientes:
(Recordatorio: En lenguaje C, las constantes que comienzan por 0 están en octal.)

O_ACCMODE   0003  
O_RDONLY    00  
O_WRONLY    01  
O_RDWR      02  
O_CREAT     0100  
O_EXCL      0200  
O_NOCTTY    0400  
O_TRUNC     01000  
O_APPEND    02000  
O_NONBLOCK  04000  
O_SYNC      010000  
O_ASYNC     020000

Se puede obtener información adicional sobre estas opciones de apertura utilizando: man 2 open

Analice el programa Abrir.c

A-01.- #define MYNAME  "Abrir"
A-02.- #include <sys/types.h>
A-03.- #include <sys/stat.h>
A-04.- #include <pwd.h>
A-05.- #include <grp.h>
A-06.- #include <fcntl.h>
A-07.- #include <stdio.h>
A-08.- #include <stdlib.h>
A-09.- #include <time.h>
A-10.- #include <unistd.h>
A-11.- 
A-12.- /* Se omite la función mode2str */
A-13.- 
A-14.- int main(int argc, char * argv[])
A-15.- {
A-16.-    char * ptr = 0;
A-17.-    int perm = 0777;
A-18.-    int mode = 0;
A-19.-    char * file = 0;
A-20.-    int fd, ret;
A-21.-    struct stat stt[1];
A-22.-    switch (argc) 
A-23.-    {
A-24.-       case 4: /* perm */
A-25.-              perm = strtol(argv[3], &ptr, 8);
A-26.-              if (!ptr || *ptr) break;
A-27.-       case 3: /* mode */
A-28.-              mode = strtol(argv[2], &ptr, 8);
A-29.-              if (!ptr || *ptr) break;
A-30.-       case 2: /* file */
A-31.-              file = argv[1];
A-32.-    }
A-33.- 
A-34.-    if (!file) 
A-35.-    {
A-36.-       printf("Uso: "MYNAME" archivo [modo [permisos]]\n");
A-37.-       /* Más líneas sobre uso que se omiten */
A-38.-    } 
A-39.-    else 
A-40.-    {
A-41.-       printf(MYNAME": open(\"%s\",0%o,0%o);\n", file, mode, perm);
A-42.-       fd = open(file, mode, perm);
A-43.-       if (fd < 0) 
A-44.-       {
A-45.-          perror(MYNAME": open");
A-46.-          ret = stat(argv[1], stt);
A-47.-       } 
A-48.-       else 
A-49.-          ret = fstat(fd, stt);
A-50.- 
A-51.-       if (ret < 0) 
A-52.-          perror(MYNAME": f/stat()");
A-53.-       else 
A-54.-       {
A-55.-          printf(MYNAME":   dev   = %d\n", (int)stt->st_dev);
A-56.-          printf(MYNAME":   ino   = %ld\n", stt->st_ino);
A-57.-          printf(MYNAME":   mode  = %0o (%s)\n", stt->st_mode, mode2str(stt->st_mode));
A-58.-          printf(MYNAME":   nlnks = %d\n", stt->st_nlink);
A-59.-          printf(MYNAME":   uid   = %d (%s)\n", stt->st_uid, getpwuid(stt->st_uid)->pw_name);
A-60.-          printf(MYNAME":   gid   = %d (%s)\n", stt->st_gid, getgrgid(stt->st_gid)->gr_name);
A-61.-          printf(MYNAME":   rdev  = %d\n", (int)stt->st_rdev);
A-62.-          printf(MYNAME":   size  = %ld\n", stt->st_size);
A-63.-          printf(MYNAME":   blksz = %ld\n", stt->st_blksize);
A-64.-          printf(MYNAME":   nblks = %d\n", (int)stt->st_blocks);
A-65.-          printf(MYNAME":   atime = %s", ctime(&stt->st_atime));
A-66.-          printf(MYNAME":   mtime = %s", ctime(&stt->st_mtime));
A-67.-          printf(MYNAME":   ctime = %s", ctime(&stt->st_ctime));
A-68.-       }
A-69.-    }
A-70.-    return 0;
A-71.- }

El programa anterior admite tres argumentos. El primero es obligatorio y especifica el nombre del fichero a abrir. El segundo es opcional y especifica el modo en que se abre el fichero. El tercero, también opcional, sirve para indicar los permisos del fichero, sólo se utiliza si se está creando un fichero.

Compile el programa Abrir.c.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc Abrir.c -o Abrir

Si ejecutamos el programa anterior sobre un fichero inexistente, se produce el siguiente error:

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Abrir nuevo
Abrir: open("nuevo",00,0777);
Abrir: open: No such file or directory
Abrir: f/stat(): No such file or directory


Ejecutemos el programa Abrir sobre un fichero inexistente pero con la opción de O_CREAT y permisos = 0666, crearemos un fichero con tamaño real 0.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Abrir nuevo 000100 0666
 
Abrir: open("nuevo",0100,0666);
Abrir:   dev   = 64773
Abrir:   ino   = 230599
Abrir:   mode  = 100644 (-rw-r--r--)   <<-- Permisos
Abrir:   nlnks = 1
Abrir:   uid   = 27182  (alumno)
Abrir:   gid   = 6000   (GrupoAlumno)
Abrir:   rdev  = 0
Abrir:   size  = 0
Abrir:   blksz = 4096
Abrir:   nblks = 0
Abrir:   atime = Wed Nov 23 12:33:19 2005
Abrir:   mtime = Wed Nov 23 12:33:19 2005
Abrir:   ctime = Wed Nov 23 12:33:19 2005


Al fichero creado, podemos cambiarle los permisos.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ chmod 0600 nuevo

Incluso podremos escribir en el fichero.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ echo HOLA >> nuevo

Utilizando el mandato cat podemos acceder al contenido del fichero. Podemos comprobar como el fichero tiene el contenido escrito mediante el mandato echo y la redirección al fichero.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ cat nuevo
HOLA

Ejecutemos el programa Abrir sobre un fichero existente con la opción de O_WRONLY.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Abrir nuevo 01
Abrir: open("nuevo",01,0777);
Abrir:   dev   = 64773
Abrir:   ino   = 230599
Abrir:   mode  = 100600    (-rw-------)  <<-- El chmod cambió los permisos
Abrir:   nlnks = 1
Abrir:   uid   = 27182     (alumno)
Abrir:   gid   = 6000      (GrupoAlumno)
Abrir:   rdev  = 0
Abrir:   size  = 5                       <<-- Tamaño alterado por el echo
Abrir:   blksz = 4096
Abrir:   nblks = 8
Abrir:   atime = Wed Nov 23 12:34:28 2005
Abrir:   mtime = Wed Nov 23 12:34:22 2005
Abrir:   ctime = Wed Nov 23 12:34:22 2005

Ejecutemos el programa Abrir sobre un fichero existente con las opciones de O_CREAT | O_RDWR y permisos = 0666.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Abrir nuevo 000102 0666
Abrir: open("nuevo",0102,0666);
Abrir:   dev   = 64773
Abrir:   ino   = 230599
Abrir:   mode  = 100600 (-rw-------)     <<-- No cambia
Abrir:   nlnks = 1
Abrir:   uid   = 27182 (alumno)
Abrir:   gid   = 6000  (GrupoAlumno)
Abrir:   rdev  = 0
Abrir:   size  = 5                        <<-- No cambia
Abrir:   blksz = 4096
Abrir:   nblks = 8
Abrir:   atime = Wed Nov 23 12:34:28 2005
Abrir:   mtime = Wed Nov 23 12:34:22 2005
Abrir:   ctime = Wed Nov 23 12:34:22 2005

Observe que no cambian los permisos ni el tamaño del fichero.
Pista: Consultar en las transparencias de teoría de este capítulo, o bien en man 2 open, cómo actúa O_CREAT cuando el fichero ya existe, en particular qué ocurre con los permisos solicitados.


Ejecutemos el programa Abrir sobre un fichero existente con la opción de O_TRUNC | O_WRONLY.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Abrir nuevo 001001
Abrir: open("nuevo",01001,0777);
Abrir:   dev   = 64773
Abrir:   ino   = 230599
Abrir:   mode  = 100600 (-rw-------)
Abrir:   nlnks = 1
Abrir:   uid   = 27182 (alumno)
Abrir:   gid   = 6000  (GrupoAlumno)
Abrir:   rdev  = 0
Abrir:   size  = 0                        <<-- Se trunca
Abrir:   blksz = 4096
Abrir:   nblks = 0
Abrir:   atime = Wed Nov 23 12:34:28 2005
Abrir:   mtime = Wed Nov 23 12:35:01 2005
Abrir:   ctime = Wed Nov 23 12:35:01 2005

Observe que el fichero ha sido truncado por lo que pierde todo su contenido y queda en tamaño 0.

Borre el fichero nuevo.

SERVICIOS READ Y WRITE

Los servicios read y write nos permiten acceder al contenido de un fichero o bien modificarlo.

Los servicios read y write devuelven el número de bytes realmente leídos o escritos, que puede ser distinto del solicitado. Este comportamiento es fácil de comprobar con el read, pero es más difícil con el write, ya que en ficheros o pipes se suele escribir lo solicitado a menos que falten recursos (p. ej. que se llene el disco).

Para comprobar el funcionamiento de estos servicios, analizaremos el programa LeeyEscribe.c

LE-01.- #define MYNAME "LeeyEscribe"
LE-02.- #include <sys/types.h>
LE-03.- #include <sys/stat.h>
LE-04.- #include <fcntl.h>
LE-05.- #include <stdio.h>
LE-06.- #include <unistd.h>
LE-07.- int main (int argc, char * argv[])
LE-08.- {
LE-09.-    int perm = 0600; // Lectura y Escritura
LE-10.-    int mode = O_CREAT|O_TRUNC|O_RDWR; //1102;  // O_CREATE|O_RDWR
LE-11.-    int fd,i;
LE-12.-    int cnt_Lect, cnt_Esc;
LE-13.-    char buff[512];
LE-14.- 
LE-15.-    if (argc!=2)
LE-16.-       printf("Uso: "MYNAME" archivo \n");
LE-17.-    else
LE-18.-    {
LE-19.-       fd = open(argv[1],mode,perm);
LE-20.-       if (fd<0)
LE-21.-          perror(MYNAME": open");
LE-22.-       else
LE-23.-       {
LE-24.-          while ((cnt_Lect = read(0, buff, 512)) > 0)
LE-25.-          {
LE-26.-             printf("LEIDOS: %d - ", cnt_Lect);
LE-27.-             for (i = 0; i < cnt_Lect; i++)
LE-28.-             {
LE-29.-                if (iscntrl(buff[i]))
LE-30.-                   printf("%03o,", (int)buff[i]);
LE-31.-                else
LE-32.-                   printf("'%c',", buff[i]);
LE-33.-             }
LE-34.-             printf("\n");
LE-35.-             cnt_Esc=write(fd, buff, cnt_Lect);
LE-36.-             if (cnt_Esc<0)
LE-37.-                perror(MYNAME);
LE-38.-          }
LE-39.-          close(fd);
LE-40.-       }
LE-41.-    }
LE-42.-    return 0;
LE-43.- }

Compile el programa LeeyEscribe.c.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc LeeyEscribe.c -o LeeyEscribe

A continuación ejecute el programa e introduzca por el teclado varias líneas de longitud diferente. Tenga en cuenta que:

  • El final de línea en Unix es el carácter ‘form feed’ de valor 012.
  • El final de fichero es [Ctrl-D]
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./LeeyEscribe fichero.txt
abc[Enter]
LEIDOS: 4 - 'a','b','c',012,
[Enter]                                            <--- Sólo el [Enter]
LEIDOS: 1 - 012,
xyz[Ctrl+D]
LEIDOS: 3 - 'x','y','z',
mno[Enter]
LEIDOS: 4 - 'm','n','o',012,
[Ctrl+D]                                           <--- Sólo un [Ctrl+D]

Observe que a pesar de que en la llamada a read se especifica que se leerán 512 bytes, la cantidad de bytes realmente depende de la información disponible.

Nótese que el [Ctrl+D] no se envía a read(), por lo que no lo contabiliza. Aunque [Ctrl+D] se asocia a 'fin de fichero', realmente es un carácter de control para liberar el buffer de E/S del terminal, pero no se transmite un byte EOF como tal. Además, no impide que se sigan almacenando caracteres tecleados en el buffer. Cuando se teclea un [Ctrl+D] aislado, read() recibe 0 bytes por lo que retorna 0 al programa.


ENLACES FÍSICOS Y SIMBÓLICOS

Un mismo fichero puede tener más de un nombre. Estos alias se crean con los servicios de link y symlink.

La diferencia fundamental entre los enlaces físicos y los enlaces simbólicos es que los primeros son referencias directas, mientras que los segundos son una referencia que se establece a través de un nombre. Para comprobar el funcionamiento de los enlaces, tanto físicos como simbólicos, utilizaremos un conjunto de programas: CreaDir.c, EnlaceF.c, EnlaceS.c. Dichos programas se limitan a utilizar, respectivamente, los servicios mkdir, link, symlink.

CD-01.- #define MYNAME	"CreaDir"
CD-02.- #include <sys/stat.h>
CD-03.- #include <sys/types.h>
CD-04.- #include <stdio.h>
CD-05.- #include <stdlib.h>
CD-06.- int main(int argc, char * argv[])
CD-07.- {
CD-08.-    char * ptr = NULL;
CD-09.-    int perm = 0777;
CD-10.- 
CD-11.-    if (argc == 3) 
CD-12.-    {
CD-13.-       perm = strtol(argv[2], &ptr, 8);
CD-14.-       if (!ptr || *ptr) return 1;
CD-15.-       mkdir(argv[1], perm);
CD-16.-    }
CD-17.-    else if (argc == 2)
CD-18.-       mkdir(argv[1], 0777);
CD-19.-    else 
CD-20.-       printf("USO: %s nombre [permisos]\n", MYNAME);
CD-21.- 
CD-22.-    return 0;
CD-23.- }

Analizando el código anterior, podemos ver que el objetivo de programa es crear un directorio (especificado en el primer parámetro), con los permisos indicados en el segundo parámetro.


A continuación vamos a compilar el programa y vamos a crear un directorio Pruebas, en nuestro directorio home.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc CreaDir.c -o CreaDir
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./CreaDir ~/Pruebas 0700
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ls -l ~
 
total  4              
drwxr-xr-x  2  alumno  GrupoAlumno  536  Jan 23  12:02  PracticasAnalisis
drwx------  2  alumno  GrupoAlumno  48   Jan 23  12:02  Pruebas

Para comprobar el funcionamiento de los enlaces físicos y simbólicos vamos a proceder, en primer lugar, a crear un fichero. Para ello, en nuestro directorio local, podemos crear un fichero redirigiendo la salida del mandato ls -l al fichero Pru.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros $ ls -l ~ > Pru

Mediante el mandato stat, podemos comprobar el estado del fichero recién creado.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ stat Pru
 
  File: 'Pru'
  Size: 226                Blocks: 8               IO Block: 131072 regular file
Device: fd05h/64773d       Inode: 343141           Links: 1
Access: (0644/-rw-r--r--)  Uid: (27182/  alumno)   Gid: ( 6000/  GrupoAlumno)
Access: 2006-01-23 12:03:35.000000000 +0100
Modify: 2006-01-23 12:03:35.000000000 +0100
Change: 2006-01-23 12:03:35.000000000 +0100


El siguiente paso es crear un enlace físico a dicho fichero. Para ello vamos a utilizar el programa EnlaceF.c.

EF-01.- #define MYNAME	"EnlaceF"
EF-02.- #include <stdio.h>
EF-03.- #include <stdlib.h>
EF-04.- #include <unistd.h>
EF-05.- 
EF-06.- int main(int argc, char * argv[])
EF-07.- {
EF-08.-    if (argc == 3) 
EF-09.-       link(argv[1], argv[2]);
EF-10.-    else 
EF-11.-       printf("USO: %s fich_existente nuevo_nombre\n", MYNAME);
EF-12.- 
EF-13.-    return 0;
EF-14.- }



Tras compilar el programa anterior, vamos a utilizarlo para crear un enlace al fichero Pru anteriormente creado.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc EnlaceF.c -o EnlaceF
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./EnlaceF Pru ~/Pruebas/Pru2




A continuación vamos a crear un enlace simbólico mediante el programa EnlaceS.c.

ES-01.- #define MYNAME	"EnlaceS"
ES-02.- #include <stdio.h>
ES-03.- #include <stdlib.h>
ES-04.- #include <unistd.h>
ES-05.- int main(int argc, char * argv[])
ES-06.- {
ES-07.-    if (argc == 3) 
ES-08.-       symlink(argv[1], argv[2]);
ES-09.-    else
ES-10.-       printf("USO: %s fich_existente nuevo_nombre\n", MYNAME);
ES-11.- 
ES-12.-    return 0;
ES-13.- }
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc EnlaceS.c -o EnlaceS
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./EnlaceS ~/PracticasAnalisis/ModuloFicheros/Pru ~/Pruebas/Pru3

El enlace simbólico exige que el nombre del fichero existente se defina completamente, no vale dar un nombre local (en nuestro ejemplo, no podemos poner simplementePru, aunque estemos en el subdirectorio que contiene a Pru).


Por su lado, Pru3 tiene las siguientes características.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros $ stat ~/Pruebas/Pru3
  File: '~/Pruebas/Pru3' -> '/home/alumnosSSOO/alumno/PracticasAnalisis/ModuloFicheros/Pru'
  Size: 40                 Blocks: 0               IO Block: 131072 symbolic link
Device: fd05h/64773d       Inode: 557777           Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (27182/  alumno)   Gid: ( 6000/  GrupoAlumno)
Access: 2006-01-23 12:04:46.000000000 +0100
Modify: 2006-01-23 12:04:46.000000000 +0100
Change: 2006-01-23 12:04:46.000000000 +0100

Hacemos un ls del directorio en el que hemos creado los enlaces.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros $ ls -l ~/Pruebas/
 
total  4              
-rw-r--r--  2  alumno  GrupoAlumno  226  Jan 23  12:03  Pru2  
lrwxrwxrwx  1  alumno  GrupoAlumno  40   Jan 23  12:04  Pru3-> /home/alumnosSSOO/alumno/PracticasAnalisis/ModuloFicheros/Pru

Observe que el enlace simbólico indica el nombre completo del fichero sobre el que ha hecho el enlace.


Borre los todos los ficheros Pru y el directorio Pruebas.

REDIRECCIÓN DE DESCRIPTORES ESTÁNDAR

En el módulo de arquitectura e introducción al S.O., ya vimos que cada proceso dispone de tres descriptores estándar (entrada, salida y error) y también vimos como redirigir dichos descriptores desde el Shell.

En el caso de querer redirigir dichos descriptores desde en un programa, basta con cerrar el descriptor mediante el servicio close y seguidamente crear un nuevo descriptor, que se asignará al hueco dejado por el close anterior. Esta última operación (la de crear el nuevo descriptor) es conveniente realizarla mediante el servicio dup, puesto que si se utiliza el servicio open y éste falla, nos encontraremos sin el correspondiente descriptor estándar.

Para comprobar el funcionamiento de las redirecciones de descriptores estándar, utilizaremos el programa Redirige.c

RD-01.- #define MYNAME	"Redirige"
RD-02.- #include <fcntl.h>
RD-03.- #include <stdio.h>
RD-04.- #include <stdlib.h>
RD-05.- #include <unistd.h>
RD-06.- int main(int argc, char * argv[])
RD-07.- {
RD-08.-    int fd1, fd2;
RD-09.-    if (argc > 3) 
RD-10.-    {
RD-11.-       fd1 = open(argv[1], O_RDONLY);
RD-12.-       if (fd1 < 0)   
RD-13.-       {
RD-14.-          perror(MYNAME": 1"); return 1;
RD-15.-       }
RD-16- 
RD-17.-       fd2 = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0600);
RD-18.-       if (fd2 < 0)   
RD-19.-       {
RD-20.-          close(fd1);
RD-21.-          perror(MYNAME": 2"); return 2;
RD-22.-       }
RD-23.-       close(0);
RD-24.-       dup(fd1);
RD-25.-       close(fd1);
RD-26.-       close(1);
RD-27.-       dup(fd2);
RD-28.-       close(fd2);
RD-29.-       execvp(argv[3], &argv[3]);
RD-30.-       perror(MYNAME": 3"); return 3;
RD-31.-    } 
RD-32.-    else 
RD-33.-       printf("USO: %s fichero_entrada fichero_salida programa [argumentos]\n", MYNAME);
RD-34.- 
RD-35.-    return 0;
RD-36.- }

Observe que el primer argumento se toma como fichero para redirigir la entrada estándar, el segundo argumento se toma como fichero para redirigir la salida estándar y el resto de los argumentos corresponde al programa que se ejecutará con el servicio exec.



TUBERÍAS

Las tuberías permiten comunicar el proceso que creó la tubería con procesos descendientes.

En el módulo de arquitectura e introducción al S.O. ya vimos como la utilización de tuberías permitía comunicar dos procesos del mismo computador. En concreto, veíamos como el carácter «|» se utiliza en el Shell para unir dos mandatos mediante una tubería de modo que la salida generada por el primer mandato actúe como entrada del segundo mandato (básicamente, en este caso se trata de una redirección de los descriptores estándar).

Para ver el procedimiento de creación de tuberías para la comunicación entre procesos, analizaremos el programa Tuberia.c

T-01.- #define MYNAME	"Tuberia"
T-02.- #include <stdio.h>
T-03.- #include <stdlib.h>
T-04.- #include <unistd.h>
T-05.- #define MAX_BUF		10
T-06.- int main(void)
T-07.- {
T-08.-    int fd[2], nleidos;
T-09.-    char buffer[MAX_BUF];
T-10.- 
T-11.-    if (pipe(fd) < 0) 
T-12.-    {
T-13.-       perror(MYNAME": Error al crear el pipe\n");
T-14.-       return 1;
T-15.-    }
T-16.- 
T-17.-    switch(fork()) 
T-18.-    {
T-19.-       case -1:
T-20.-               perror(MYNAME": fork()");
T-21.-               return 2;
T-22.-       case  0: /*LECTOR DEL PIPE (Proceso HIJO) */
T-23.-               close (fd[1]);
T-24.-               do 
T-25.-               {
T-26.-                   nleidos = read(fd[0], buffer, MAX_BUF);
T-27.-                   if (nleidos < 0) 
T-28.-                      perror(MYNAME" - PROCESO Y: Error en la lectura\n");
T-29.-                   else 
T-30.-                   {
T-31.-                      fprintf(stderr, MYNAME" - PROCESO Y: %d datos: ", nleidos);
T-32.-                      write (1, buffer, nleidos);
T-33.-                      fprintf(stderr, "\n");
T-34.-                   }
T-35.-               } while (nleidos > 0);
T-36.-               fprintf(stderr, MYNAME" - PROCESO Y: termina correctamente\n");
T-37.-               break;
T-38.-       default: /*ESCRITOR DEL PIPE (Proceso PADRE) */
T-39.-               close (fd[0]);
T-40.- 	             do 
T-41.-               {
T-42.-                  nleidos = read(0, buffer, MAX_BUF);
T-43.-                  fprintf(stderr, MYNAME" - PROCESO X: %d datos \n", nleidos);
T-44.-                  write (fd[1], buffer, nleidos);
T-45.-               } while (nleidos > 0);
T-46.-               fprintf(stderr, MYNAME" - PROCESO X: termina correctamente\n");
T-47.-    }
T-48.-    return 0;
}

En la línea T-11, del código anterior, se procede a crear un tubería. Dicha tubería dispone de dos descriptores asociados, uno de escritura (fd[1]) y otro de lectura (fd[0]). A continuación se crea un proceso hijo. Ambos procesos conocen la existencia de la tubería (tanto el descriptor de lectura como el de escritura), por lo que podemos utilizar este mecanismo para comunicar ambos procesos.

Como podemos observar en la figura anterior, uno de los procesos (proceso escritor) depositará información en la tubería a través del descriptor de escritura, y el otro (proceso lector) recuperará la información de la tubería a través del descriptor de lectura. Otro aspecto a tener en cuenta, es que el proceso escritor, cierra el descriptor de lectura de la tubería (puesto que no lo va a utilizar), mientras que el proceso lector, cerrará el descriptor de escritura de la tubería.



Cuando se utilizan tuberías, es necesario tener en cuenta dos aspectos fundamentales:

  • Cuando un proceso intenta escribir en una tubería que no tiene ningún proceso lector asociado (es decir, ningún proceso tiene abierto el descriptor de lectura) el proceso escritor recibe una señal (sigpipe), cuya acción por defecto es matar al proceso que la recibe.
  • Si no existe ningún proceso escritor sobre una tubería (es decir, ningún proceso tiene abierto el descriptor de escritura), entonces todos los procesos lectores finalizan las lecturas sobre la tubería (es decir, el servicio read recibe un fin de fichero, por lo que el valor devuelto por el servicio read será 0).

Para este conjunto de ejercicios, además del programa Tubería.c, utilizaremos dos Shell.

Shell 1 Compile y ejecute el programaTubería. Teclee datos terminados en [Enter]. Cuando desee finalizar teclee [Ctrl+D].

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc Tuberia.c -o Tuberia
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Tuberia
12345[Enter]
Tuberia PROCESO X: 6 datos
Tuberia PROCESO Y: 6 datos: 12345
 
1234567890123456789012345[Enter]
Tuberia PROCESO X: 10 datos
Tuberia PROCESO X: 10 datos
Tuberia PROCESO X: 6  datos
Tuberia PROCESO Y: 10 datos: 1234567890
Tuberia PROCESO Y: 10 datos: 1234567890
Tuberia PROCESO Y: 6  datos: 12345
[Ctr+D]
Tuberia PROCESO X: 0 datos
Tuberia PROCESO X: termina correctamente
Tuberia PROCESO Y: 0 datos: 
Tuberia PROCESO Y: termina correctamente




Shell 1: Ejecute otra vez el programa.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Tuberia
123[Enter]
Tuberia PROCESO X: 4 datos
Tuberia PROCESO Y: 4 datos: 123

Shell 2: Teniendo los procesos Tuberia activos, abra otro Shell y mate al proceso padre.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ps -lu alumno
F  S  UID    PID    PPID   C  PRI  NI  ADDR  SZ    WCHAN   TTY     TIME      CMD  
5  S  27182  32132  32118  0  75   0   -     1901  -       ?       00:00:00  sshd  
0  S  27182  2707   32132  0  76   0   -     826   wait    pts/9   00:00:00  bash  
0  S  27182  4479   32132  0  75   0   -     826   wait    pts/14  00:00:00  bash  
0  S  27182  12638  2707   0  76   0   -     360   -       pts/9   00:00:00  Tuberia  
1  S  27182  12639  12638  0  76   0   -     360   pipe_w  pts/9   00:00:00  Tuberia  
0  R  27182  12640  4479   0  76   0   -     649   -       pts/14  00:00:00  ps  
 
 
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ kill 12638 (en su caso será otro valor de PID)

Shell 1: Observe lo que ocurre en el Shell 1.

Tuberia PROCESO Y: 0 datos 
Tuberia PROCESO Y: termina correctamente
Terminated


Shell 1: Ejecute otra vez el programa

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Tuberia
123[Enter]
Tuberia PROCESO X: 4 datos
Tuberia PROCESO Y: 4 datos: 123

Shell 2: Teniendo los procesos Tuberia activos, liste el contenido del subdirectorio /proc/pid_proceso_padre/fd

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ps -lu alumno
 
F  S  UID    PID    PPID   C  PRI  NI  ADDR  SZ    WCHAN   TTY     TIME      CMD  
5  S  27182  32132  32118  0  75   0   -     1901  -       ?       00:00:00  sshd  
0  S  27182  2707   32132  0  75   0   -     826   wait    pts/9   00:00:00  bash  
0  S  27182  4479   32132  0  75   0   -     826   wait    pts/14  00:00:00  bash  
0  S  27182  13354  2707   0  76   0   -     360   -       pts/9   00:00:00  Tuberia  
1  S  27182  13355  13354  0  76   0   -     360   pipe_w  pts/9   00:00:00  Tuberia  
0  R  27182  13557  4479   0  76   0   -     649   -       pts/14  00:00:00  ps  
 
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ls -l /proc/13354/fd (en su caso será otro valor de PID)


Shell 2: Teniendo los procesos Tuberia activos, mate al proceso hijo.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ kill 13355 (en su caso será otro valor de PID)

Shell 1 Teclee unos caracteres terminado en [Enter] y observe lo que ocurre.

qwer[Enter]
Tuberia PROCESO X: 5 datos




COUTILIZACIÓN DE FICHEROS

Se entiende por coutilización el hecho de que varios procesos estén accediendo simultáneamente al mismo fichero. El resultado obtenido dependerá de si los procesos comparten o no comparten el puntero de posición del fichero.

Varios lectores simultáneos

Cuando existen varios procesos lectores simultáneos no está predefinido el orden en que se ejecutan las lecturas. Es decir, el planificador de procesos determina qué proceso entra en ejecución en cada momento, por lo que a priori, no podremos conocer qué proceso lector está realizando la lectura en cada instante.

Para verificar este funcionamiento, utilizaremos el programa Lectores.c

L-01.- #define MYNAME "Lectores"
L-02.- #include <sys/wait.h>
L-03.- #include <ctype.h>
L-05.- #include <stdio.h>
L-06.- #include <stdlib.h>
L-07.- #include <unistd.h>
L-08.- int main(void)
L-09.- {
L-10.-    int pp[2];
L-11.-    int i;
L-12.-    char ch;
L-13.- 
L-14.-    if (pipe(pp) < 0) 
L-15.-    {
L-16.-       perror(MYNAME": pipe()"); exit(1);
L-17.-    }
L-18.- 
L-19.-    for (i = 1; i <= 9; i++) /*Crea 9 hijos del mismo padre*/
L-20.-    {
L-21.-       switch (fork()) 
L-22.-       {
L-23.-          case -1:
L-24.-                  perror(MYNAME": fork()");
L-25.-                  exit(1);
L-26.-          case 0: /* HIJO #i: */
L-27.-                  close(pp[1]);
L-28.-                  while (read(pp[0], &ch, 1) == 1) 
L-29.-                  {
L-30.-                     if (isdigit(ch)) ch += i;
L-31.-                     write(1, &ch, 1);
L-32.-                  }
L-33.-                  exit(0);
L-34.-       }
L-35.-    }
L-36.-    /* PADRE: */
L-37.-    close(pp[0]);
L-38.-    i = 0;
L-39.-    while (i < 1000) /* 1000 = 20 * 50. Escribe 20 líneas de 50 caracteres cada una*/ 
L-40.-    {
L-41.-       write(pp[1], "0", 1);
L-44.-       i++;
L-45.-       if (i%50 == 0)
L-46.-          write(pp[1], "\n", 1); /* Añade cambio de línea (cada 50 caracteres) */
L-47.-    }
L-48.-    close(pp[1]);
L-49.- 
L-50.-    for (i = 1; i <= 9; i++)
L-51.-       wait(NULL);
L-52.- 
L-53.-    return 0;
L-54.- }





Compile y ejecute el programa Lectores varias veces y observe los resultados obtenidos.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc Lectores.c -o Lectores
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Lectores


Desplazamiento dentro de un fichero

La función de biblioteca lseek, nos permite reposicionar el puntero de lectura/escritura de un fichero.

Para comprobar el funcionamiento de esta función de biblioteca, utilizaremos el programa Robot_B.c

RB-01.- #define MYNAME "Robot_B"
RB-02.- #include <sys/types.h>
RB-03.- #include <fcntl.h>
RB-04.- #include <stdio.h>
RB-05.- #include <stdlib.h>
RB-06.- #include <unistd.h>
RB-07.- int main(int argc, char * argv[])
RB-08.- {
RB-09.-    char ch;
RB-10.-    off_t offset =  0;
RB-11.-    int   whence = -1;
RB-12.-    char * ptr = 0;
RB-13.-    int perm = 0777;
RB-14.-    int mode = 0;
RB-15.-    char * file = 0;
RB-16.-    int fd;
RB-17.- 
RB-18.-    switch (argc) 
RB-19.-    {
RB-20.-       case 3:	/* mode */
RB-21.-              mode = strtol(argv[2], &ptr, 8);
RB-22.-              if (!ptr || *ptr) break;
RB-23.-       case 2:	/* file */
RB-24.-              file = argv[1];
RB-25.-    }
RB-26.- 
RB-27.-    fd = open(file, mode, perm);
RB-28.-    if (fd < 0) 
RB-29.-    {
RB-30.-       perror(MYNAME": open");	return 1;
RB-31.-    }
RB-32.- 	
RB-33.-    while (read(0, &ch, 1) > 0) 
RB-34.-    {
RB-35.-       switch (ch) 
RB-36.-       {
RB-37.-          case '0' ... '9':
RB-38.-                   offset *= 10;
RB-39.-                   offset += ch - '0';
RB-40.-                   break;
RB-41.-          case '-': 
RB-42.-                   if (offset > 0) offset = -offset;
RB-43.-                   break;
RB-44.-          case '+': 
RB-45.-                   if (offset < 0) offset = -offset;
RB-46.-                   break;
RB-47.-          case '<': 
RB-48.-                   if (whence < 0) whence = SEEK_SET;
RB-49.-          case '=': 
RB-50.-                   if (whence < 0) whence = SEEK_CUR;
RB-51.-          case '>': 
RB-52.-                   if (whence < 0) whence = SEEK_END;
RB-53.-                   offset = lseek(fd, offset, whence);
RB-54.-                   if (offset < 0)
RB-55.-                      perror(MYNAME": lseek()");
RB-56.-                   offset =  0;
RB-57.-                   whence = -1;
RB-58.-                   break;
RB-59.-          case 'a' ... 'z':
RB-60.-          case 'A' ... 'Z':
RB-61.-                   if (write(fd, &ch, 1) < 0)
RB-62.-                      perror(MYNAME" write()");
RB-63.-                   break;
RB-64.-          case '@':
RB-65.-                   close(fd);
RB-66.-                   read(0, &ch, 1);
RB-67.-                   return 0;
RB-68.-          default:
RB-69.-                   offset =  0;
RB-70.-                   break;
RB-71.-       }
RB-72.-    }
RB-73.-    return 0;
RB-74.- }

El programa recibe dos argumentos, de los cuales sólo uno es obligatorio. El primero es el nombre del fichero (obligatorio), y el segundo argumento (opcional) especifica las opciones que se utilizan en el servicio open. Una vez abierto el fichero, el programa entra en un bucle infinito de lectura de la entrada estándar y de escritura en el fichero abierto. Los caracteres leídos se interpretan de la siguiente forma:

  • Las letras minúsculas o mayúsculas se escriben en el fichero.
  • El carácter ‘@’ termina el programa.
  • Mediante los signos ‘+’ o ‘-‘ y una cifra se define el tamaño de un offset.
  • Con los caracteres ‘<’, ‘=’ o ‘>’ se realiza un lseek de valor offset relativo a: principio, posición actual o final del fichero, respectivamente. Por ejemplo, ‘+125<’ posiciona el puntero de posición del fichero en el byte 125. La secuencia ‘+40>’ deja un hueco de 40 bytes al final del fichero.

Compruebe que no tiene un fichero fich en su directorio de trabajo, compile el programa Robot_B y realice la secuencia siguiente (observe que se van a utilizar 3 Shell):

Shell 1:Abrimos el fichero fich inexistente mediante el programa Robot_B.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc Robot_B.c -o Robot_B
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fich
Robot_B: open: No such file or directory

Podemos comprobar que si el fichero no existe y en el servicio open no se usan opciones de creación, se produce un error.

Shell 1:Abrimos el fichero con opciones de O_CREAT | O_RDWR, escribimos varias líneas y terminamos la ejecución del programa Robot_B con el carácter @.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fich 000102
abcdefghijk
lmnopq
@

Shell 3:Observamos el contenido del fichero fich. Para ello utilizamos el mandato od con la opción –a de alfanumérico. Dicho mandato imprime la dirección en octal seguida de los valores de 16 bytes.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p
0000020   q
0000021


Shell 1: Abrimos nuevamente el fichero con opciones de O_CREAT | O_RDWR y escribimos una línea.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fich 000102
xx

Shell 3: Observamos el contenido del fichero fich

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   x   x   c   d   e   f   g   h   i   j   k   l   m   n   o   p
0000020   q
0000021

Vemos que se han sobrescrito los primeros caracteres del fichero, puesto que al abrir un fichero su puntero de posición apunta al inicio del mismo.

Shell 1: Nos ponemos en la posición 15 a contar desde el principio del fichero y escribimos una línea.

+15<
xxxx

Shell 3: Observamos el contenido del fichero fich

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   x   x   c   d   e   f   g   h   i   j   k   l   m   n   o   x
0000020   x   x   x
0000023

Vemos que se han escrito los caracteres a partir de la posición 15. Se han sobrescrito el 15 y 16, y se han añadido el 17 y el 18.

Shell 2: Abrimos el fichero con opciones de O_CREAT | O_RDWR y escribimos una línea.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fich 000102
qqq

Shell 3: Observamos el contenido del fichero fich.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   q   q   q   d   e   f   g   h   i   j   k   l   m   n   o   x
0000020   x   x   x
0000023

Vemos que se han sobrescrito los primeros caracteres del fichero.

Shell 1: Escribimos una línea y terminamos la ejecución del programa con @.

aaaaaaaaaaa
@

Shell 3 : Observamos el contenido del fichero fich.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   q   q   q   d   e   f   g   h   i   j   k   l   m   n   o   x
0000020   x   x   x   x   a   a   a   a   a   a   a   a   a   a
0000036


Shell 2: Terminamos la ejecución del programa.

@

Shell 1: Abrimos nuevamente el fichero con las opciones O_APPEND | O_WRONLY. La opción O_APPEND hace que las escrituras siempre se añadan al final del fichero, con independencia del puntero de posición. Escribimos una línea.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fich 002001
ccc

Shell 3: Observamos el contenido del fichero fich.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   q   q   q   d   e   f   g   h   i   j   k   l   m   n   o   x
0000020   x   a   a   a   a   a   a   a   a   a   a   a   c   c   c

Vemos que la información se añade, no se sobrescribe al principio del fichero.

Shell 1: Nos ponemos al principio del fichero, escribimos una línea y terminamos el programa.

< 
hhh
@

Shell 3: Observamos el contenido del fichero fich.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fich
0000000   q   q   q   d   e   f   g   h   i   j   k   l   m   n   o   x
0000020   x   a   a   a   a   a   a   a   a   a   a   a   c   c   c   h
0000040   h   h


Elimine el fichero fich.

Cambiar permisos y borrar

Una vez invocado el servicio open sobre un fichero, el fichero continúa abierto, aunque se borre o se cambien los permisos de acceso.

Veamos en primer lugar como aunque cambiemos los permisos de un fichero abierto, este sigue abierto y el programa que abrió el fichero puede seguir trabajando con él. Para ello, realice la siguiente secuencia.

Shell 1: Creamos un fichero con el mandato cat y le introducimos contenido.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ cat > fichero
abcdefghijk[Ctrl+D]          <<-- Fin de fichero

Shell 2: Veamos los permisos del fichero creado.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ls -l fichero
-rw-r--r--  1 alumno GrupoAlumno 11 Dec  6 19:10 fichero

Shell 2 :Vemos el contenido del fichero fichero.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fichero
0000000   a   b   c   d   e   f   g   h   i   j   k

Shell 1: Abrimos el fichero con opciones de O_WRONLY, y escribimos una línea, terminando con un [Enter].

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fichero 000001
xxxx[Enter]

Shell 2: Observamos el contenido del fichero fichero

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fichero
0000000   x   x   x   x   e   f   g   h   i   j   k

Shell 2: Cambiamos los permisos del fichero fichero, de forma que no se pueda escribir.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ chmod 400 fichero

Shell 2: Comprobamos que se han cambiado los permisos del fichero fichero

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ls -l fi*
-r--------  1 Alumno GrupoAlumno 11 Dec  6 19:14 fichero

Shell 2 :Abrimos el fichero con opciones de O_WRONLY, y vemos que el fichero no se puede abrir, ya que es de solo lectura.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fichero 000001
Robot_B: open: Permission denied.

Shell 1: Escribimos otra línea (terminando con [Enter]) y terminamos la ejecución con el carácter @.

yyy[Enter]
@

Observamos que no se produce ningún error y que el programa termina.

Shell 2: Observamos el contenido del fichero fichero

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a fichero
0000000   x   x   x   x   y   y   y   h   i   j   k

Vemos que el fichero ha sido modificado, aunque sus permisos eran ya de solo lectura.

Borre el fichero fichero.

Comprobemos ahora como cuando un proceso tiene abierto un fichero, puede seguir trabajando con él a pesar de que el fichero se borre. Para ello, realice la siguiente secuencia.

Shell 1: Abrimos el fichero fichero con permisos O_CREAT | O_WRONLY mediante el programa Robot_B y escribimos una línea.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Robot_B fichero 0000101
aaaaaaa

Shell 2: Borramos el fichero creado.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ rm fichero

Shell 2: Comprobamos que el fichero fichero ya no existe.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ls -l fichero
ls: fichero: No such file or directory

Shell 1: Escribimos un par de líneas (terminadas con [Enter]) y terminamos el programa Robot_B con el carácter @.

bbbbbb[Enter]
ccc[Enter]
@

Observamos que el programa ejecuta los servicios de write sin producir ningún tipo de error y termina correctamente.

Shell 2: Volvemos a comprobar que el fichero fichero ya no existe.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ls -l fichero
ls: fichero: No such file or directory

SERVICIO UMASK

El servicio umask sirve para modificar la máscara de creación de ficheros del proceso que lo solicita. No confundir con el mandato de Shell umask, que permite ver y modificar la máscara del Shell (que será heredada por los procesos hijos).

Analice el programa Enmascarado.c.

E-01.- #define MYNAME "Enmascarado"
E-02.- #include <sys/types.h>
E-03.- #include <sys/stat.h>
E-04.- #include <fcntl.h>
E-05.- #include <stdio.h>
E-06.- #include <stdlib.h>
E-07.- #include <unistd.h>
E-08.- int main(int argc, char * argv[])
E-09.- {
E-10.-    char * archivo = NULL;
E-11.-    int permisos = 0666, mascara = 0000;
E-12.-    int ret, ret1 = -1, ret2 = -1;
E-13.-    struct stat sts[1];
E-14.-    int fd;
E-15.- 
E-16.-    if (argc > 1)
E-17.-       archivo = argv[1];
E-18.-    if (argc > 2)
E-19.-       ret1 = sscanf(argv[2], "%o", &permisos);
E-20.-    if (argc > 3)
E-21.-       ret2 = sscanf(argv[3], "%o", &mascara);
E-22.-    if (!archivo || !ret1 || !ret2 || argc > 4) 
E-23.-    {
E-24.-       printf("Uso: "MYNAME" fichero [permisos [mascara]]\n");      exit(0);
E-25.-    }
E-26.- 
E-27.-    if (ret2 == 1) 
E-28.-    {
E-29.-       printf(MYNAME" Cambiando máscara a 0%03o\n", mascara);
E-30.-       mascara = umask(mascara);
E-31.-       if (mascara < 0) 
E-32.-       {
E-33.-          perror(MYNAME": umask()");         exit(1);
E-34.-       }
E-35.-       printf(MYNAME" La máscara anterior era 0%03o\n", mascara);
E-36.-    } 
E-37.-    else 
E-38.-    {
E-39.-       mascara = umask(0);
E-40.-       umask(mascara);
E-41.-       printf(MYNAME" La máscara actual es 0%03o\n", mascara);
E-42.-    }
E-43.-    
E-43.-    ret = stat(archivo, sts);
E-44.-    if (ret < 0) 
E-45.-    {
E-46.-       printf(MYNAME" Creando archivo \"%s\"\n", archivo);
E-47.-       printf(MYNAME" Aplicando permisos 0%03o\n", permisos);
E-48.-    } 
E-49.-    else 
E-50.-    {
E-51.-       printf(MYNAME" Truncando archivo existente \"%s\"\n", archivo);
E-52.-       printf(MYNAME" Permisos actuales del archivo 0%03o\n", sts->st_mode & ~S_IFMT);
E-53.-    } 
E-54.- 
E-55.-    fd = creat(archivo, permisos);
E-56.-    if (fd < 0) 
E-57.-    {
E-58.-       perror(MYNAME": creat()");	exit(1);
E-59.-    }
E-60.- 
E-61.-    ret = fstat(fd, sts);
E-62.- 
E-63.-    if (ret < 0) 
E-64.-    {
E-65.-       perror(MYNAME": fstat()");	exit(1);
E-66.-    }
E-67.- 
E-68.-    printf(MYNAME" Tamaño final del archivo %d\n", (int) sts->st_size);
E-69.-    printf(MYNAME" Permisos finales del archivo 0%03o\n", sts->st_mode & ~S_IFMT);
E-70.-    
E-71.-    return 0;
E-72.- }

Los permisos finales de un fichero se obtienen haciendo una operación AND lógico entre la mascara negada y los permisos especificados en la creación del fichero.

Supongamos que no existe el fichero F1.txt.




Compile y ejecute el programa con los siguientes argumentos:

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc Enmascarado.c -o Enmascarado
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./Enmascarado F1.txt 0352 0127



Borre el fichero F1.txt

FICHEROS CON HUECOS

Cuando se escribe un fichero no es necesario hacerlo de forma secuencial. Se puede escribir en cualquier posición del mismo, incluso dejando sin escribir tramos intermedios. Un hueco (hole) es un tramo de bytes intermedios que no han sido escritos. Su lectura devuelve 0. Si una agrupación completa no ha sido escrita (forma parte de un hueco), habitualmente no se reserva soporte físico (disco) para ella. Simplemente se contabiliza como hueco. Esto da lugar a ficheros cuyo tamaño real es mayor que el espacio físico que ocupa. Para comprobar este efecto, utilizaremos el programa CreaHueco.c.

CH-01.- #define MYNAME	"CreaHueco"
CH-02.- #include <sys/types.h>
CH-03.- #include <fcntl.h>
CH-04.- #include <stdio.h>
CH-05.- #include <unistd.h>
CH-06.- int main(int argc, char * argv[])
CH-07.- {
CH-08.-    int flags;
CH-09.-    off_t offset;
CH-10.- 
CH-11.-    if (!argv[1] || sscanf(argv[1], "%lu", &offset) != 1) 
CH-12.-    {
CH-13.-       printf("Uso: "MYNAME" offset >  fichero_creado_nuevo\n");
CH-14.-       printf("Uso: "MYNAME" offset >> fichero_para_alterar\n");
CH-15.-    } 
CH-16.-    else 
CH-17.-    {
CH-18.-       flags = fcntl(1, F_GETFL);
CH-19.-       if (flags < 0)
CH-20.-          perror(MYNAME": fcntl(GETFL)");
CH-21.-       flags &= ~O_APPEND;
CH-22.-       if (fcntl(1, F_SETFL, flags) < 0)
CH-23.-          perror(MYNAME": fcntl(SETFL)");
CH-24.- 
CH-25.-       if (lseek(1, offset, SEEK_SET) < 0)
CH-26.-          perror(MYNAME": lseek(SET)");
CH-27.- 
CH-28.-       if (write(1, "x", 1) < 0)
CH-29.-          perror(MYNAME": write(x)");
CH-30.-    }
CH-31.-    return 0;
CH-32.- }

Analice el programa CreaHueco. Este programa tiene los dos usos siguientes:

  • CreaHueco offset > fichero Si el fichero no existe se crea y si existe se trunca. El puntero del fichero se posiciona en el valor indicado por offset y se escribe una x. Si el valor de offset es mayor que el tamaño real del fichero se crea un hueco.
  • CreaHueco offset >> fichero. El puntero del fichero se posiciona en el valor indicado por offset y se escribe una x. Si el valor de offset es mayor que el tamaño real del fichero se crea un hueco.



Compile el programa CreaHueco.c y utilícelo para crear un fichero huecos.txt de tamaño 1 byte.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ gcc CreaHueco.c -o CreaHueco
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./CreaHueco 0 > huecos.txt

Mediante el mandato stat podemos obtener las características del fichero.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ stat huecos.txt
  File: 'huecos.txt'
  Size: 1                  Blocks: 8               IO Block: 131072 regular file
Device: fd05h/64773d       Inode: 616841           Links: 1
Access: (0644/-rw-r--r--)  Uid: (27182/  alumno)   Gid: ( 6000/  GrupoAlumno)
Access: 2005-12-05 05:35:21.000000000 +0100
Modify: 2005-12-05 08:36:58.000000000 +0100
Change: 2005-12-05 08:36:58.000000000 +0100

Observamos que el tamaño del fichero es 1. Por otro lado, el espacio físico es de 8 bloques de 512 bytes, que equivale a una agrupación de 4KB (según se vio con anterioridad).

Vamos a escribir otra x en la posición 4095, correspondiente al último byte de la primera agrupación. Analizamos los parámetros y el contenido.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ ./CreaHueco 4095 >> huecos.txt
alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ stat huecos.txt
  File: 'huecos.txt'
  Size: 4096               Blocks: 8               IO Block: 131072 regular file
Device: fd05h/64773d       Inode: 616841           Links: 1
Access: (0644/-rw-r--r--)  Uid: (27182/  alumno)   Gid: ( 6000/  GrupoAlumno)
Access: 2005-12-05 05:35:21.000000000 +0100
Modify: 2005-12-05 08:42:35.000000000 +0100
Change: 2005-12-05 08:42:35.000000000 +0100

Ahora el tamaño real del fichero es de 4096, aunque los bytes 1 a 4094 no han sido escritos. Con el mandato od observamos el contenido del fichero.

alumno@maquinaLinux:~/PracticasAnalisis/ModuloFicheros$ od -a huecos.txt
0000000   x nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
0000020 nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
*
0007760 nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul   x
0010000

Observamos que menos los bytes 0 y 4095 todos los demás son nulos. Esto significa que el sistema de ficheros se ha encargado de anular el contenido anterior de los bytes correspondientes, para que no se pueda leer el contenido anterior.

Repita el paso anterior pero con el mandato: ./CreaHueco 8191 » huecos.txt


Sabiendo que 220 = 1048576, ejecute: ./CreaHueco 1048576 > huecos.txt


Sabiendo que 230 = 1073741824, ejecute: ./CreaHueco 1073741824 >huecos.txt


Borre el fichero huecos.txt

 
docencia/asignaturas/sox/prv/practicas/analisis_so5/modulo_sf.txt · Última modificación: 2024/04/11 13:45 (editor externo)
 
Recent changes RSS feed Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki