La practica consiste en listar el contenido de un directorio dado, es
decir, el nombre de los ficheros y subdirectorios
que hay en el. Esto lo conseguiremos con la funcion recursiva listar (que
hemos creado).
Un directorio contiene siempre los subdirectorios . (el directorio actual)
y .. (el directorio padre del actual), pero
tambien puede contener ficheros y otros subdirectorios. Los subdirectorios
que esten dentro del directorio actual
tambien seran listados de igual manera que el directorio padre, a traves
de procesos (hijos) en los que se llama a
la funcion listar. Esto se consigue introduciendo la recursividad en la
funcion listar. De esta manera se logra que
haya varios procesos ejecutandose a la vez (concurrentemente).
Mediante el uso de memoria compartida se implementa la comunicacion
y sincronizacion de los procesos, que es
necesaria para que el proceso padre sepa si todos sus procesos hijos han
acabado de realizar su tarea (el listado
del subdirectorio). En caso contrario, el proceso padre esperara por los
procesos hijos.
Una vez se haya acabado de volcar en el fichero que especifiquemos todos
los contenidos de directorios y subdi-
rectorios, se realizara la ordenacion alfabetica de las entradas de este
fichero resultante(ponemos el resultado en
otro fichero). Para distinguir ficheros de directorios usaremos <>
englobando el nombre de un directorio, y [] englo-
bando el nombre de un fichero.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include "rshmem.h"
int pasar(char *); /* prototipos
de funciones */
listar( char *);
FILE *fp; /* puntero al fichero donde guardar el listado sin ordenar */
main(int argn, char *argv[]) {
static char directorio[MAXNAMLEN] ; /*
directorio a listar */
/* comprobacion de los argumentos del main */
if (argn == 1) {
strcpy(directorio, ".") ;
} else if (argn == 2) {
strcpy(directorio, argv[1]) ;
} else {
(void) fprintf(stderr, "Numero de argumentos
incorrecto\n");
exit (-1);
}
printf("El listado sin ordenar se volcara al fichero salida,el ordenado al fichero salida2\n");
if( (fp=fopen("salida","w")) == NULL
){
printf("No se puede abrir el fichero
salida\n");
exit(-4);
}
listar(directorio);
/* llamada a la funcion recursiva "listar" */
fclose(fp);
/* cierre del fichero salida */
system("sort -o salida2 salida"); /* ordenacion
alfabetica del fichero salida */
exit(0);
} /* fin main */
/****************** DECLARACION DE FUNCIONES ************************/
listar (char *directorio) {
char directorioact[256];
/* directorio actual que se recorre */
static char nombreCompleto[256] ;
/* entrada de directorio */
DIR *dir ;
/* puntero a directorio a recorrer */
struct dirent *entrada ; /*puntero
a estructura que contiene informacion sobre una entrada del directorio
*/
struct stat entrada_stat ; /* estructura que contiene
informacion sobre el directorio */
int *nprocfinalizados=NULL;
/* contador del numero de procesos hijos que han acabado */
int contador=0; /*
contador del num. de procesos hijos creados */
strcpy(directorioact,directorio);
/* busca el posible acceso al directorio */
if ((dir = opendir(directorio)) == NULL) {
(void) fprintf(stderr, "No existe el
directorio <%s>\n", directorio);
exit(-2) ;
}
/* crear zona de memoria compartida */
if ( !crearMemoria() )
fprintf(stderr,"error de crearMemoria\n");
nprocfinalizados=(int *)memoria;
(*nprocfinalizados)=0;
c: /* etiqueta de goto */
/* lectura de las entradas del directorio */
while ((entrada=readdir(dir)) != NULL) {
/* formacion del nombre completo de una entrada del directorio */
sprintf(nombreCompleto, "%s/%s", directorioact, entrada->d_name)
;
if (stat(nombreCompleto, &entrada_stat)) {
(void) fprintf(stderr,"No puedo comprobar
<%s>\n", nombreCompleto);
closedir(dir) ; /*
la entrada del directorio no se puede comprobar */
exit(-3);
}
if (pasar(nombreCompleto)){ /*
la entrada es el directorio padre ..*/
fprintf(fp,"<%s>\n", nombreCompleto) ; /*
o el directorio hijo . */
fflush(fp);
strcpy(nombreCompleto,directorioact);
goto c;
}
if (S_ISDIR(entrada_stat.st_mode) ) { /*
la entrada es directorio */
contador++;
fprintf(fp,"<%s>\n", nombreCompleto) ;
fflush(fp);
if ( 0==fork() ) {
/* creacion de un proceso hijo */
listar (nombreCompleto); /*
llamada a la funcion listar */
(*nprocfinalizados)+=1;
exit (0);
}
}
else {
/* la entrada es fichero */
fprintf(fp,"[%s]\n", nombreCompleto)
;
fflush(fp);
}
} /* del while */
closedir(dir) ;
while ( (*nprocfinalizados) != contador );
/* espera por los hijos */
/* eliminar la memoria compartida */
if ( !eliminarMemoria() )
fprintf(stderr,"error de eliminar memoria\n");
} /* fin listar */
int pasar(char *direc){ /* se ocupa de identificar
el directorio . y .. */
int n; /* numero
de caracteres distintos de \0 */
n=strlen(direc);
if(n==3) {
if ( !strcmp(&direc[1], "/.")
) return 1;
}
else {
if ( !strcmp(&direc[n-3], "/..") ) {
return 1;
}
if ( !strcmp(&direc[n-2], "/.") ) {
return 1;
}
}
return 0;
}
/**********************************************************
*
* NOMBRE : main
*SINOPSIS : int main ( int argn, char *argv[[)
* ARGUMENTOS :
* argv[1[==> nombre del directorio a listar
* SALIDA : el resultado del listado del directorio en un fichero ->salida
* el
resultado del listado ordenado alfabeticamente en fichero -> salida2
* DESCRIPCION :
* El programa recibe un nombre de directorio a listar como argumento. Si
el programa no recibe ningun
* argumento, el directorio a listar sera el actual (.). En cualquier otro
caso, el programa abandona la
* ejecucion. Luego se llama a la funcion listar que es la encargada de
realizar el trabajo de listado.
* Despues el listado del directorio sin ordenar situado en el fichero salida
se ordena y se guarda
* en otro fichero(salida2).
*
* VALOR DE RETORNO : 0 ==> si no ha habido
problemas
* ( al S.O. ) -1,-4
==> ha habido problemas **********************************************************/
/**********************************************************
*
* NOMBRE : listar
*SINOPSIS : listar ( char *directorio )
* ARGUMENTOS : * directorio==> nombre del directorio a listar
* SALIDA : el resultado de la traduccion, en un fichero
* DESCRIPCION :
* La funcion recibe el nombre del directorio a listar. Intenta acceder
a dicho directorio mediante la funcion
* opendir; si se tiene exito, esta funcion nos devuelve un puntero al directorio
(dir). Despues se crea una zona
* de memoria compartida, donde situaremos el contador del numero de procesos
hijos que han finalizado
* (nprocfinalizados). Este contador sera disminuido en una unidad por cada
proceso hijo cuando termine; asi
* el p.padre sabe si puede acabar el tambien (si todos los hijos
han acabado), o no. Luego, con la funcion
* readdir vamos recorriendo cada entrada (fich. o direct.)
del directorio actual, y formamos su nombrecompleto.
* Podemos tener 4 situaciones :
* - la entrada no se puede comprobar => cerramos el directorio,
y abandonamos.
* - la entrada es fichero => la volcamos al fichero de salida
* - la entrada es . o .. => la volcamos al fichero, pero
no la abrimos, leemos una nueva entrada (con goto)
* - la entrada es directorio distinto del . y del .. =>
la volcamos a fichero. Se incrementa el contador de procesos
* hijos creados. Creamos un proceso hijo,que
se encarga del listado de este subdirectorio mediante una
* llamada (recursividad) a listar. Se incrementa
el numero de ficheros finalizados (cuando acaba la llamada a listar).
* Una vez que el directorio ( con el que llamamos
a listar por 1ª vez) ha acabado de ser listado se cierra, y se espera
* hasta que sus subdirectorios acaben de ser
listados (esto ocurre cuando el nº de procesos finalizados es igual
al
* nº de procesos creados). Se elimina
la memoria compartida creada y se sale de la funcion.
*
* VALOR DE RETORNO : 0 ==> si no ha habido problemas
*
nº negativo ==> ha habido problemas * **********************************************************/
/**********************************************************
*
* NOMBRE : pasar
* SINOPSIS : int pasar ( char *direc )
* ARGUMENTOS :
*
direc==> nombre del directorio a comprobar
* DESCRIPCION :
* La funcion recibe como argumento el nombre de un directorio. Comprueba
si este directorio es el directorio actual .
* o el padre del directorio actual . Estas comprobaciones se realizan comparando
el final del nombre del directorio con
* las cadenas de caracteres \. y \..
* VALOR DE RETORNO : 0 ==> el directorio
no es el . ni el ..
*
1 ==> el directorio es el . o el .. **********************************************************/
Para ver como funciona el programa, usamos este arbol de directorios:
(para distinguir ficheros de directorios, ponemos en negrita los directorios)
DIR
|
-----------------------------------------------------------
|
|
|
|
|
|
|
DIR1 fic1
fic2 DIR2
fic3 fic4
fic5
|
|
--------------
------------------
| | |
| |
ficher1 ficher2
DIR4 archivo1 DIR5
|
-----------------
| |
|
DIR6
filea fileb
|
---------
| |
file1
file2
La salida ordenada, contenida en el fichero "salida2",
queda:
[dir/dir1/fichero1]
[dir/dir1/fichero2]
[dir/dir2/archivo1]
[dir/dir2/dir5/dir6/file1]
[dir/dir2/dir5/dir6/file2]
[dir/dir2/dir5/filea]
[dir/dir2/dir5/fileb]
[dir/fic1]
[dir/fic2]
[dir/fic3]
[dir/fic4]
[dir/fic5]
<dir/dir1>
<dir/dir1/.>
<dir/dir1/..>
<dir/dir2>
<dir/dir2/dir4>
<dir/dir2/dir4/.>
<dir/dir2/dir4/..>
<dir/dir2/dir5>
<dir/dir2/dir5/dir6>
<dir/dir2/dir5/dir6/.>
<dir/dir2/dir5/dir6/..>
<dir/dir2/dir5/.>
<dir/dir2/dir5/..>
<dir/dir2/.>
<dir/dir2/..>
<dir/.>
Para la realizacion de la practica se necesitan ficheros que contienen
funciones que se basan en el interface
de aplicacion de memoria compartida en UNIX, y que facilitan el trabajo.
/* rshmem.h Fichero de cabecera con definiciones y declaraciones para
usar memoria compartida.*/
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define ARRAY_SIZE 4000
#define MALLOC_SIZE 10000
#define SHM_SIZE 10000
#define SHM_MODE (SHM_R | SHM_W) /* read/write */
#define TRUE 1
#define FALSE 0
#ifdef RUTINAS_SHMEM
static int shmid; /* handler de memoria compartida
*/
char *memoria; /* puntero a zona de memoria compartida
*/
#else
extern char *memoria;
#endif
/* Prototipos de funciones de memoria compartida */
void origenTiempo();
void tiempoPasa();
int crearMemoria() ;
int eliminarMemoria() ;
#define TP tiempoPasa();
#define RUTINAS_SHMEM
#include "rshmem.h"
/* Crea memoria compartida.
* - el manejador de memoria es interno
* - manda mensajes de error por salida de error estándar.
*/
int crearMemoria() {
char *funcName = "crearMemoria";
if ((shmid=shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE))<0){
fprintf(stderr, "%s: error
de shmget()\n", funcName);
}
else if ((memoria=shmat(shmid, 0, 0)) == (void *) -1)
{
fprintf(stderr, "%s: error de
shmat()\n", funcName);
}
else {
return TRUE;
}
return FALSE;
}
/* Destruye la memoria compartida creada por crearMemoria() */
int eliminarMemoria() {
char *funcName = "eliminarMemoria";
if (shmctl(shmid, IPC_RMID, 0) < 0) {
fprintf(stderr,"%s: error de shmctl()\n",
funcName); return FALSE ;
}
else return TRUE ; }
/* Coloca una semilla en el temporizador del bucle de * tiempoPasa() */
void origenTiempo(){
srand((unsigned int) time(NULL)) ;
}
/* Rutina que hace pasar un poco de tiempo con un bucle * sencillo */
void tiempoPasa() {
unsigned int i;
int a=3;
/* Los parametros "50" y "2" dependen mucho de la velocidad
* de la computadora y de la configuracion del SO. Espero que * funcionen
bien */
for (i=rand()/50; i>0; i--) {
a = a%3 + i;
}