Patricio Poblete (ppoblete@dcc.uchile.cl) |
Unix provee una interfaz uniforme,en que todos los dispositivos se ven como archivos:
/home/ppoblete/x /dev/tty1
Al abrir un archivo en Unix, se recibe un número entero pequeño mayor o igual que cero, que se llama un file descriptor (fd). (Nótese que esto es distinto de lo que ocurre al abrir un archivo en la biblioteca standard de C, en que se recibe un file pointer.)
Este fd identifica al archivo abierto para todas la operaciones (leer, escribir, etc.).
Todo programa comienza con tres fds ya abiertos:
0 stdin 1 stdout 2 stderr
Las operaciones básicas de entrada/salida son:
nread = read(fd, buf, nbytes); nwritten = write(fd, buf, nbytes);
Cada llamada retorna el número de bytes transferidos. En el caso del write es un error que éste no sea igual al número pedido. En caso de fin de archivo se retorna un cero y, en caso de error, un -1.
copia.c |
/* Copia stdin -> stdout */ #include <stdio.h> #define BUFSIZE 4096 main() { char buf[BUFSIZE]; int n; while((n=read(0, buf, BUFSIZE))>0) write(1, buf, n); return 0; }
Estas funciones también se pueden usar para construir funciones de más alto nivel, como por ejemplo:
getchar1.c |
#include <stdio.h> int getchar() { char c; return (read(0, &c, 1)==1? (unsigned char)c : EOF); } main() { int c; while((c=getchar())!=EOF) putchar(c); }
Por motivos de eficiencia, es mejor no leer de a un byte, sino con un buffer:
getchar2.c |
#include <stdio.h> int getchar() { static char buf[BUFSIZ]; static char *p=buf; static int n=0; if(n==0) /* buffer vacío */ { n=read(0,buf, sizeof buf); p=buf; } return (--n>=0? (unsigned char)*p++ : EOF); } main() { int c; while((c=getchar())!=EOF) putchar(c); }
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> fd=open(path, flags, perms); /* flags puede ser O_RDONLY, O_WRONLY, O_RDWR */ /* Además se le puede superponer los flags siguientes: O_APPEND ir al final antes de cada write O_CREAT crea el archivo si no existe O_EXCL junto con O_CREAT, falla si el archivo ya existe O_TRUNC trunca el archivo a largo cero El parámetro perms se usa solo junto con O_CREAT */ close(fd);
La llamada
fd=creat(path, perms);crea el archivo con los permisos indicados y equivale a
fd=open(path, O_WRONLY|O_CREAT|O_TRUNC, perms);
owner group other r w x r w x r w xEjemplos:
0755 rwx para owner, rx para los demás 0666 rw para todos
my_cp.c |
/* Uso: my_cp from to */ #include <stdio.h> #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> void error(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "Error: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(1); } main(int argc, char *argv[]) { int f1, f2, n; char buf[BUFSIZ]; if(argc!=3) error("Use: my_cp from to"); if((f1=open(argv[1], O_RDONLY))==-1) error("Can't open %s", argv[1]); if((f2=creat(argv[2], 0666))==-1) error("Can't open %s", argv[2]); /* usando stats se pueden mantener los permisos */ while((n=read(f1, buf, BUFSIZ))>0) if(write(f2, buf, n)!=n) error("Write error on file %s", argv[2]); return 0; }
unlink(path);
lseek(fd, offset, how);Ejemplos:
lseek(fd, 0L, SEEK_EOF); /* para append */ lseek(fd, 0L, SEEK_SET); /* rewind */
lseek retorna la nueva posición dentro del archivo, o -1 si hubo un error.
Un pipe es un canal de comunicación entre dos procesos. Se comporta como una cola (FIFO) en donde lo que se escribe por un extremo se lee por el otro.
Para crear un pipe se hace
pipe(fds);en donde fds es un arreglo de tamaño 2. Después de ejecutar esta llamada, se obtienen 2 fds:
fds[0] permite leer fds[1] permite escribir
Este arreglo generalmente se entrega a dos procesos, uno de los cuales cierra de inmediato el fd de lectura y el otro cierra el fd de escritura.
El sistema garantiza que un write de tamaño menor o igual a PIPE_BUF es indivisible.
Los ejemplos de uso los veremos al estudiar el funcionamiento de los procesos en Unix.
Los directorios son archivos especiales que contienen listas de archivos en su interior. Para su manejo existen funciones especiales.
#include <direcnt.h> /* Esto define un "struct dirent" que incluye un char d_name[NAME_MAX]; */ DIR *dirp; dirp=opendir(dirname); /* NULL en caso de error */ struct dirent *d; d=readdir(dirp); /* entrega siguiente nombre de archivo en d->d_name, retorna NULL al final */ closedir(dirp); rewinddir(dirp); /* vuelve al inicio */ buf=getcwd(buf, size); /* guarda en buf pathname absoluto del directorio actual, retorna NULL si error */ status=chdir(path); /* 0 => OK, -1 => error */ mkdir(path, perms); rmdir(path); link(oldpath, newpath); /* no funciona para directorios */ unlink(path); rename(oldpath, newpath);
Para averiguar atributos de un archivo, se usa la funcion:
stat(path, buf); /* buf es de tipo "struct stat *" */
La estructura stat tiene campos tales como:
st_mode modo (=perms) st_ino inode st_dev device st_nlink número de links st_uid user id del owner st_gid grupo st_size tamaño del archivo en bytes st_atime dia y hora del último acceso st_ctime día y hora del último cambio (p.ej. perms) st_mtime día y hora de la última modificación
Hay macros para "testear"el modo:
S_ISDIR directorio? S_ISCHR char special device? S_ISBLK block special device? S_ISREG regular file? S_ISFIFO named pipe?
Los atributos se pueden modificar con:
chmod(path, perms); chown(path, owner, group);
También es posiblefalsificar el "time stamp" de un archivo, con la función utime (ver manual). ¿Hay un uso legítimo para esto?
Ejemplo: Listar directorios recursivamente.
listdir.c |
#include <stdio.h> #include <sys/types.h> #include <dirent.h> #include <sys/stat.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define MAX_PATH 1000 void listdir(int indent, char *name) { DIR *current_dir; struct dirent *this_entry; char cwd[MAX_PATH+1]; int i; struct stat status; /* imprimimos directorio actual */ for(i=1; i<=indent; ++i) printf(" "); printf("%s\n", name); if(stat(name, &status)!=0) error("Can't stat %s", name); if(!S_ISDIR(status.st_mode)) return; /* abrimos el directorio para lectura */ if((current_dir=opendir(name))==NULL) error("Can't open directory %s", name); if(getcwd(cwd, MAX_PATH+1)==NULL) error("Can't get cwd"); if(chdir(name)!=0) error("Can't chdir"); while((this_entry=readdir(current_dir))!=NULL) { if(strcmp(this_entry->d_name, ".")==0 || strcmp(this_entry->d_name, "..")==0) continue; listdir(indent+1, this_entry->d_name); } closedir(current_dir); chdir(cwd); } main() { listdir(0, "."); }