elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


 


Tema destacado: Doble factor de autenticación o verificación en dos pasos


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación C/C++ (Moderadores: Eternal Idol, Littlehorse)
| | |-+  [Curso]Curso de Comunicacion de Procesos en C, por Diabliyo !!! (UP 13 Dic )
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: [Curso]Curso de Comunicacion de Procesos en C, por Diabliyo !!! (UP 13 Dic )  (Leído 10,774 veces)
Diabliyo


Desconectado Desconectado

Mensajes: 1.427


M.S.I Angel Cantu


Ver Perfil WWW
[Curso]Curso de Comunicacion de Procesos en C, por Diabliyo !!! (UP 13 Dic )
« en: 5 Octubre 2005, 20:36 »

Hola:

Estoy realizando un curso de comunicacion de procesos en C, y pues me he desidido a postear aqui los avances :D.

Pido de favor que, cualquier aclaracion sobre el curso, aviso de errores de ortografia y mas delitos :P, sean avisados en otro POST que creare, referente a los errores, y arreglos para con el curso de comunicacion de procesos. Del mismo modo, acepto ideas, comentarios y criticas para aumentar el conocimiento de este curso que estoy desarrollando.

Mas que nada lo planteo asi, para que NO se mezclen los POST de ustedes, con los POST referentes al TUTO. ;)

El Enlace al POST para aclaraciones, consejos, comentarios. Aqui:

-----> http://foro.elhacker.net/index.php/topic,89319.0.html <-----

NOTA 1: si deseas resolver dudas, respecto a sus codigos practicos o dudas del lado teorico o de comprension misma, porfavor crear otro POST. Ya que este POST que pongo arriba, es solo para CONSEJOS, AVISOS, IMPLEMENTACIONES.

Planteo Dudas, Resolucion de Dudas, Planteo de Codigos Propios y dudas de mismod codigos, porfavor Crear su propio POST :D.

Ya estoy a punto de terminar el Curso, con el fin de entrar de lleno al: Analizis de Sockets en C ;).

byeeee


« Última modificación: 14 Diciembre 2005, 02:10 por Diabliyo » En línea

Diabliyo


Desconectado Desconectado

Mensajes: 1.427


M.S.I Angel Cantu


Ver Perfil WWW
--== Temario ==--
« Respuesta #1 en: 5 Octubre 2005, 20:47 »

  • 1- Procesos
  • 1.1- Teoria
  • 1.2. El Proceso ID
  • 1.3- Analisis de los Estados de un Proceso
  • 1.4. Creacion de un Proceso
  • 1.5. La Llamada   wait  al sistema
  • 1.6. La Llamada   exec  al sistema
[li]2- Senales[/li]
  • 2.1. Teoria
  • 2.2. Envio de Senales ( Teoria 2 )
  • 2.2.1. La Funcion kill();
  • 2.2.2. La Funcion raise();
  • 2.2.3. La Funcion alarm();
  • 2.2.4. La Mascara de Senales y Conjuntos de Senales
  • 2.2.5. Envio de Senales con SIGNAL()
[/list]
[li]3. Archivos Avanzados[/li]
  • 3.1 Teoria
  • 3.2 Directorios y Rutas de Acceso
  • 3.3 Busquedas de Rutas de Acceso y Variable de Ambiente
  • 3.4 Representacion de Archivos en Unix
  • 3.5 Representacion de Directorios y Enlaces o Ligas
  • 3.6 Representacion de Archivos con Identificadores
  • 3.7 Descriptores de Archivo
  • 3.7.1 Lectura y Escritura de Ficheros
  • 3.7.2 Entradas y Salidas sin Bloqueo
  • 3.7.3 Herencia de Descriptores de Archivo
[/list]
[li]4. Semaforos[/li]
[li]5. Hilos[/li]
[li]6. Sincronizacion de Hilos[/li]
[li]7. Agradecimientos[/li]
[li]8. Enlazes de Interes[/li]
[li]9. Nota del Autor[/li]
[/list]


« Última modificación: 14 Diciembre 2005, 01:52 por Diabliyo » En línea

Diabliyo


Desconectado Desconectado

Mensajes: 1.427


M.S.I Angel Cantu


Ver Perfil WWW
1. PROCESOS
« Respuesta #2 en: 5 Octubre 2005, 20:55 »

1.1 Teoria

Un Proceso es: un programa en ejecucion o la instancia de un programa funcionando en un ordenador.

Los Estados de un procesos son:
  • Nuevo
  • Ejecucion
  • Bloqueado
  • Listo
  • Terminado

*Nuevo
   Cuando se crea un nuevo proceso, es admitida por el S.O y esta en espera de ser utilizado.

*Ejecucion
   Cuando el proceso esta siendo utilizado.

*Bloqueado
   Cuando el procesos NO se puede ejecutar, hasta que presente algun suceso.

*Listo
   Cuando el proceso esta en espera de ser utilizado.

*Terminado
   Cuando el proceso es excluido por el S.O del Grupo de Procesos.
   


Cada procesos que se cree, tiene su propio espacio en memoria, por lo cual ahi varios puntos a con-
siderar en la creacion de un proceso, que son:

  • Asignar un Unico Identificador al Nuevo Proceso ( un ID )
  • Asignar Espacio para el Nuevo Proceso
  • Iniciar el Bloque de Control de Procesos
  • Establecer los Enlazes Apropiados
  • Crear o Ampliar otras Estructuras de Datos
   
Al ejecutar un programa el S.O automaticamente le asigna un ID a nuestro programa ( realiza operaciones
de creacion de proceso ), de este modo obtenemos un ID, que sera el proceso PADRE, que sera la instancia
de nuestro programa. Asi despues desde codigo con solamente invocar la funcion fork(); nosotros
crearemos procesos Hijos para nuestro procesos PADRE.

Ventajas/Aportaciones de la Creacion de un Proceso

  • Trabajo Interactivo y en Segundo Plano
  • Procesamiento Asincrono
  • Aceleracion de la Ejecucion
  • Estructuracion Modular del Programa



1.2 EL Proceso ID

Unix identifica el proceso mediante un entero unico denominado ID, para poder ver el ID de los procesos
Padres e hijo(s) se utilizan las funciones:

   
Código:
getpid(); ------>  pid_t getpid( void );
Vemos el ID del proceso que Creamos
   
   
Código:
getppid(); ------> pid_t getppid( void );
Vemos el ID del proceso Padre del Proceso que Creamos

Unix asocia cada proceso con un usuario en particulas, de este modo cada usuario posee un ID, este ID de
usuario lo podemos llamar mediante:

   getuid();  ----->  uid_t getuid( void );

Como vimos, existe el ID del usuario que es el que ejecuta el programa, pero de igual manera tambien
un ID de Usuario Efectivo, que viene siendo el que determina los privilegios que el proceso tiene para
el acceso de recursos. Para acceder a el ID de Usuario Efectivo se utiliza:

   geteuid();  ----->  uid_t geteuid( void );

Codigo: ps_001.c
Programa que imprime IDs de: proceso, padre, usuario y usuario efectivo

Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
system( "clear" );
printf( "ID del Proceso: %ld", (long)getpid() );
printf( "\nID del Proceso Padre: %ld", (long)getppid() );
printf( "\nID del Usuario en el Sistema: %ld", (long)getuid() );
printf( "\nID del usuario Efectivo en el Sistema: %ld", (long)geteuid() );
printf( "\n\nPulsa para salir..." );
getchar();
exit(0);
}

*-----------------------------------------------*
| En Ejecucion |
*-----------------------------------------------*
|   shell# ./ps_001 |
|   ID del Proceso: 134513568 |
|   ID del Procedo Padre: 7764 |
|   ID del Usuario en el Sistema: 1000 |
|   ID del Usuario Efectivo en el Sistema: 1000 |
*-----------------------------------------------*



1.3 Analisis de los Estados de un Proceso

Como mencionamos en el capitulo 1.1, los estados son:
  • Nuevo
  • Ejecucion
  • Bloqueado
  • Listo
  • Terminado
   
Ahora veremos una explicacion mas practica dentro del SO.

=== NUEVO ===
Cuando se crea un proceso nuevo, una vez creado el proceso, nuestro SO asigna un ID al
proceso, asigna espacio, etc, etc... y lo manda a la COLA DE PROCESOS LISTOS, osea en esa
cola  se colocal los procesos que estan listos o preparados para ser ejecutados.

=== LISTO ===
Una ves pasado el proceso a la COLA DE PROCESOS LISTOS, el proceso pasa al estado de LISTO
que es el momento en el que el proceso es ejecutado o puesto en uso.

=== EJECUCION ===
Una ves que es tomado de la COLA DE PROCESOS LISTOS, el procesos deja el estado de LISTO y
pasa al estado de EJECUCION, que es el momento en el que el proceso esta siendo utilizado
dentro del CPU.

=== BLOQUEADO ===
Durante la Ejecucion del proceso le surge la necesidad de esperar por algun suceso o evento,
en ese momento pasa al estado de BLOQUEADO, en el cual este estado depende de que se produzca
dicho suceso o evento para dejar este estado. Un proceso puede moverse al estado de BLOQUEADO
con solo hacer una llamda a la funcion: sleep();.

=== TERMINADO ===
Cuando el proceso es finalizado por le usuario o el SO. ( el mas facil de todos o_O  xD ).



1.4 Creacion de un Proceso

Unix crea un proceso atravez de la llamada ala funcion: fork();.

Un Proceso Padre ---> es el proceso que a sido iniciado y tiene un ID asignado por el SO.
Un Proceso Hijo ---> es el proceso copia del proceso padre y tiene un ID similar al Proceso Padre.

Un Proceso Hijo debe ser finalizado primero y despues finalizar su Proceso Padre, de este modo la
Operacion del Proceso es Correcta. Cuando esto ocurre al reves se toman los siguientes nombre a los
procesos y significativos:

Un Proceso Huerfano ---> es un proceso que a sido finalizado por el proceso PADRE, y es adoptado
   por el Demonio del Sistema que es: init, este Demonio finalizara dicho proceso. init es
   identificado como Proceso Padre con ID=1.
   
Un Proceso Zombi ---> es un proceso hijo que se ha quedado parado o bloqueado en la tabla de PROCESOS
   y espera la finalizacion de su proceso PADRE.
   
NOTA: es importante resaltar que un proceso hijo reside dentro de un Proceso Padre o Principal, y debemos
tomar en cuenta que un proceso hijo pude crear Otros Procesos Hijos, dentro del mismo Proceso.

La funcion fork(); realiza una llamada al SO donde copia la imagen en memoria que tiene el Proceso
Padre. El nuevo proceso recive una COPIA del espacio de direccion en memoria que tenia el Proceso
Padre. Los procesos continuan su ejecucion en la instruccion que esta despues del fork();.

Debe quedarnos claro que desde el momento en que EJECUTAMOS nuestro programa ( el que sea ) automa-
ticamente se crea un proceso para nuestro programa y un ID de dicho proceso, por lo cual al momento de
llamar a getpid() ANTES de la llamada a fork() podemos ver el ID de nuestro proceso PADRE
( osea el proceso que corresponde a nuestro programa principal ).


El fork(); es de tipo: pid_t.

   *fork(); ----->  pid_t fork( void );

fork(); devuelve un 0 al proceso hijo y un valor diferente de 0 al proceso padre.


Codigo: ps_002.c
Programa que crea un proceso Hijo e imprime el ID del proceso Padre e Hijo creado con fork();
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
system( "clear" );
fprintf( stderr, "Inica Programa con Proceso Padre PID: %ld", (long)getpid() );
if( fork() == 0 )
{
fprintf( stderr, "\nHe Creado el Proceso Hijo PID: %ld, Mi Padre PID: %ls", (long)getpid(), (long)getppid() );
sleep(1);
}
else
{
fprintf( stderr, "\nContinua el Proceso Padre: %ld", (long)getpid() );
sleep(1);
}
fprintf( stderr, "\n\nTermino el Proceso %ld", (long)getpid() );
exit(0);
}

*---------------------------------------------------------------*
| En Ejecucion |
*---------------------------------------------------------------*
|   shell# ./ps_002 |
|   Inicia Programa con Proceso Padre PID: 7737 |
|   He Creado el Proceso Hijo PID: 7739, Mi Padre PID: 7737 |
|   Continua el Proceso Padre: 7737 |
| |
|   Termino el Proceso 7739 |
| |
|   Termino el Proceso 7737 |
*---------------------------------------------------------------*

=== Explicacion ===
Al iniciar nuestro programa ( ejecutarlo ), automaticamente se crea un proceso para la instancia de nuestro programa dentro de la
PC, con esto automaticamente obtenemos un ID de Proceso ( esto sucede con cualquier programa que hemos ejecutado ), ahora como se
muestra en le codigo, se imprime "Inicia el Programa con Proceso Padre PID : 7737", donde 7737 es el ID que tiene nuestro programa
y lo consideramos como el PROCESO PADRE. Ahora viene la llamada a fork();, la cual vuelve a crear otro proceso pero con un ID
diferente, NOTESE que al crearse otro proceso imaginemos que volvemos a crear otro EJECUTABLE :D. Ahora como solo se creo el Nuevo
Proceso, el programa pasa a la parte de ELSE e imprime "Continua el Proceso Padre PID 7737". Ahora nuestro Proceso Hijo llega a la
parte de fork(); y ve que NO puede continuar a crear otro proceso, ya que los procesos HIJOS tienen 0 y los Procesos Padre tiene
un numero diferente de 1. Por lo tanto entra el IF e imprime "He Creado el Proceso Hijo PID 7739 y Mi Padre PID 7737", ahora nuestro
Proceso Padre ( programa principal ) llega ala antepenultima linea que es: "Termino el Proceso 7737", y finaliza, despues llega a la
misma posicion el Proceso Hijo ( por decirlo asi, el proceso IMAGEN o VIRTUAL ) e imprime: "Termino el Proceso 7739" y finaliza.

Algunas veces puede Inicia primero el Proceso Padre y despues el Proceso Hijo, o al reves, pero siempre finaliza primero el proceso
HIJO y despues el Proceso Padre :D.

De la misma manera al iniciar el programa como dije anteriormente, se crea el proceso y obtenemos un ID para nuestro programa en
ejecucion, despues llega a la parte del "if( fork() == 0 )" y se crea un proceso nuevo, en este caso lo podemos nombrar
como: "Proceso Hijo", de esta forma imaginemos que creamos un nuevo PROGRAMA ( osea ejecutamos en otra ventana ), y tenemos 2 procesos funcionado, el Proceso Padre ( nuestro programa principal ) y el Proceso Hijo ( el nuevo proceso realizado por la llamada a fork();,
de este modo conseguimos el Trabajo en Segundo Plano :D. Ya que interactucamos con dos procesos a la ves :D. No es complicado, simplemente
imaginemos que tenemos 2 programas, y en nuestro Proceso Padre ( programa principal ) ejecuta el fork(); y crea un Proceso Nuevo
(osea el Hijo) por lo tanto el fork(); nos retorna un 0, ya que creo un nuevo proceso y logicamente entra el IF e imprime:
"He Creado el Proceso Hijo PID: 7739, Mi Padre PID: 7737", despues entra el " sleep()"  y espera 1 segundo, en el transcurso de ese
segundo le da tiempo suficiente al padre para continuar con su codigo, que en  este caso sera el ELSE ;), y pues imprime:
"Continua el Proceso Padre: 7737", despues el proceso padre llamada a sleep(); y espera 1 segundo, durante ese segundo de espera, el proceso Hijo Continua su ejecucion y lo primero que ve es que, finaliza el IF y lega al exit(0); por lo tanto termina el
Proceso Hijo Primero, despues termina el segundo de espera del Proceso Padre y continua su codigo y ve que termina el ELSE y continua
el codigo y se encuentra el exit(0); y finaliza.

El sleep(); nos sirve para poder visualizar de una formas mas entendible el funcionamiento del programa, ya que al iniciar el programa
, lo primero que hace es mostrarte el inicio del proceso HIJO y del proceso PADRE, espera 1 segundo y realiza la finalizacion.

Codigo: ps_003.c
Programa que Ilustra un PROCESO HUERFANO, y vemos que es tomado por INIT, ver IDs :D
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
system( "clear" );
fprintf( stderr, "Inicia el Programa con Proceso Padre: %ld", (long)getpid() );

if( fork() == 0 )
{
fprintf( stderr, "\nInicia el Hijo con PID: %ld y Padre PID: %ld", (long)getpid(), (long)getppid() );
sleep(1);
fprintf( stderr, "\nNuevamente el Hijo PID: %ld, y Padre PID: %ld", (long)getpid(), (long)getppid() );
}
else
{
fprintf( stderr, "\nContinua el Padre PID: %ld", (long)getpid() );
fprintf( stderr, "\nAhora Finaizara el Padre con PID: %ld", (long)getpid() );
exit(0);
}
exit(0);
}

*-------------------------------------------------------*
| En Ejecucion |
*-------------------------------------------------------*
|  shell# ./ps_003 |
|  Inicia el Programa con Proceso Padre: 8764 |
|  Inicia el Hijo con PID: 8766 y Padre PID: 8764 |
|  Continua el Padre PID: 8764 |
|  Ahora Finalizara el Padre con PID: 8764 |
|  shell# |
|  Nuevamente el Hijo PID: 8766 y Padre PID: 1 |
*-------------------------------------------------------*

=== Explicacion ===
Bueno primero imprime el ID del proceso Padre, despues llama al " fork() " y crea al HIJO, continua con IF e imprime
"Inicia el Hijo con PID: 8766 y Padre PID: 8764", despues entra el sleep();, espera un segundo. En seguida entra el ELSE, e imprime "Continua el Padre PID: 8764" y finaliza el Proceso Padre ( nuestro programa principal ), ahora regresa el Proceso Hijo y por lo tanto el Proceso Hijo es tomado por init, y e imprime: "Nuevamente el Hijo PID: 8766 y Padr PID: 1" fuera de nuestro programa y dentro de la Shell :D.

Notese que una la cadena: "Nuevamente el Hijo PID: 8766 y Padre PID: 1" es mostrado despues de la finalizacion del programa,
dentro de la consola Unix :D. Ya que el procedimiento ese lo realiza init :D. ;)


Codigo: ps_004.c
Programa que crea una lista de Procesos
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
int i;

system( "clear" );
for( i=0; i<4; i++ )
{
if( fork() )
break;
fprintf( stderr, "\nProceso %i con PID: %ld\tPadre PID: %ld", i, (long)getpid(), (long)getppid() );
}
fprintf( stderr, "\n\nFinalizando Proceso PID: %ld", (long)getpid() );
sleep(1);
exit(0);
}

*-------------------------------------------------------*
| En Ejecucion |
*-------------------------------------------------------*
|   shell# ./ps_004 |
|   Proceso 0 con PID: 5393 Padre PID: 5391 |
|   Proceso 1 con PID: 5394 Padre PID: 5393 |
|   Proceso 2 con PID: 5395 Padre PID: 5394 |
|   Proceso 3 con PID: 5396 Padre PID: 5395 |
| |
|   Finzalizado Proceso PID: 5396 |
|   Finzalizado Proceso PID: 5395 |
|   Finzalizado Proceso PID: 5394 |
|   Finzalizado Proceso PID: 5393 |
|   Finzalizado Proceso PID: 5391 |
*-------------------------------------------------------*


=== Explicacion ===
Este programa crea una lista de procesos, en el que conforme se va creando procesos el proceso actual pasa a ser un Hijo y al
crear otro proceso, el proceso anterior pasa a ser Padre del Proceso Actualmente creado.
Prosiguiendo con el codigo, el programa infoca al fork(); siempre y cuando sea un proceso padre... if( fork() ), de este modo
creamos procesos padres en el que un procoso esta enlazado con otro, recordemos nuevamente que un proceso padre es independiente
del un proceso anterior, pero se enlazan los procesos ya que corresponden a un mismo programa, en el cual solo como diferenciador
se le asigna un numero de identificacion ( ID ) de un valor+1 del proceso anterior. ( Ejm: 5393, 5394m etc.., etc... ).
De esta forma creamos una LISTA de procesos xD. La finalizacion se realiza desde el Ultimo Proceso Creado hasta el Primer Proceso
Que es La Instancia o Proceso de Nuestro Programa en Ejecucion :D.


Codigo: ps_005.c
Programa que Crea Varios Procesos Hijos dentro de un mismo Proceso
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
int i;

system( "clear" );
for( i=0; i<5; i++ )
{
if( fork() == 0 )
{
fprintf( stderr, "\nProceso %i con PID: %ld\tPadre PID: %ld", (long)getpid(), (long)getppid() );
break;
}
}
fprintf( stderr, "\n\nFinalizado Proceso con PID: %ld", (long)getpid() );
sleep(1);
exit(0);
}


*--------------------------------------------------------*
| En Ejecucion |
*--------------------------------------------------------*
|   shell# ./ps_005 |
|   Proceso 0 con PID: 6493 Padre PID: 6491 |
|   Finalizado Proceso con PID: 6493 |
|   Proceso 1 con PID: 6494 Padre PID: 6491 |
|   Finalizado Proceso con PID: 6494 |
|   Proceso 2 con PID: 6495 Padre PID: 6491 |
|   Finalizado Proceso con PID: 6495 |
|   Proceso 3 con PID: 6496 Padre PID: 6491 |
|   Finalizado Proceso con PID: 6496 |
|   Proceso 4 con PID: 6497 Padre PID: 6491 |
|   Finalizado Proceso con PID: 6497 |
| |
|   Finalizado Proceso con PID: 6491 |
*--------------------------------------------------------*

=== Explicacion ===
Este Programa crea Hijos dentro de un mismo proceso. Debemos tomar en cuenta que para la creacion de Procesos Hijos, lo
diferenciamos cuando  fork();  devuelve 0, esto indica la creacion de un proceso hijo :D.
Siguiendo el codigo encontramos  if( fork() == 0 )  siempre y cuando se cree un proceso Hijo entrara el IF e imprimira
el PID del Proceso Hijo, despues Frena el FOR e imprime Finalizado el Proceso con PID:, espera 1 segundo, tiempo justo para
que el programa pueda crear otro proceso Hijos y terminarlo conforme se pidan :D.
Al final solo finaliza nuestra Istancia/Proceso Padre Principal xD.



1.5 La Llamada    wait   al Sistema

La llamada al sistema  wait  detiene al proceso hasta que sus procesos hijo termine o se detenga, wait() retorna un valor positivo en caso que su proceso hijo haya finalizado correctamente, dicho valor positivo corresponde al ID del Proceso Hijo, y retorna un valor -1 en caso que se haya presentado algun error, suspencion o interrupcion inesperada. Cuando wait() retorna un -1 setea a " errno " con un valor que mas abajo se explican.

Se debe utilizar una de las siguientes funciones.

Código:
*---------------------------------------------------------------*
| #include <sys/wait.h> |
| #include <sys/types.h> |
| |
| pid_t wait( int *estado ); |
| pid_t waitpid( pid_t pid, int *estado, int opcion ); |
*---------------------------------------------------------------*

estado ---> es un puntero a un valor entero.

Caracteristicas de wait:
  • Wait regresa de inmediato si el proceso no contiene procesos hijos o si el hijo termina o se detiene.
  • Wait regresa debido a la terminacion de un hijo.
   
Valores de errno:
  • ECHILD --> indica que no existen procesos hijos a los cuales esperar
  • EINTR --> indica que la llamada/espera fue interrumpida por un senal.
   
Para analizar de una mejor manera el estado de un proceso hijo correspondiente al valor devuelvo al estado se utiliza
POSIX, en el cual especifica los siguiente macros:

   *WIFEXITED( int estado ) --> El Hijo termino normalment, el estado devuelto es 0.
   *WEXITSTATUS( int estado ) --> Devuelve el estado del proceso, lo normal es 0.
   *WIFSIGNALED( int estado ) --> El Hijo termino debido a una senal no atrapada.
   *WTERMSIG( int estado ) -->
   *WIFSTOPPED( int estado ) -->
   *WSTOPSIG( int estado ) -->

Codigo: ps_006.c
Programa que Ilustra un poco el uso del wait() xD
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

   int main()
{
pid_t proceso;
int estado;

system( "clear" );
if( (proceso= fork()) == -1 )
{
fprintf( stderr, "\nFork Fallo" );
exit(1);
}
else if( proceso == 0 )
fprintf( stderr, "\nSoy el Proceso Hijo PID: %ld.", (long)getpid() );
else if( wait( &estado ) != proceso )
fprintf( stderr, "\nUna Senal debio interrumpir la espera de la finalizacion" );
else fprintf( stderr, "\nSoy el Proceso Padre PID: %ld y Proceso Hijo PID: %ld", (long)getpid(), (long)proceso );
exit(0);
}

=== Explicacion ===

Bueno ene ste caso no podemos dar un resultado en ejecucion, ya que ahi varios resultados posibles, pero aki los mencionare
todos los posibles y el porke :D, conforme el codigo se lee, va sucediendo asi:

1. Si fork() falla entonces retorna una valor -1 y muestra: " Fork Fallo ".

2. En caso que fork() realize el proceso con exito, imprime al Hijo y entra el wait(), el wait para el proceso padre hasta
que el proceso hijotermine, ene se mometno retorna le valor positivo a wait() ( que es el ID dle proceso Hijo ), y continua
el codigo he imprime el texto del Padre... en Conclusión seria asi:

Código:
*-----------------------------------------------------------------------*
| Soy el Proceso Hijo PID: 2240 |
| Soy el Proceso Padre PID: 2238 y Proceso Hijo PID: 2240 |
*-----------------------------------------------------------------------*

3. Se crea el proceso Hijo y por consecuencia se imprime el proceso Hijo, ahora entra wait() y si sucede alguna interrupcion
o error en la espera entonces imprime el fprintf() frl wait() :D. En ejecucion seria asi:

Código:
*-----------------------------------------------------------------------*
| Soy el Proceso Hijo PID: 2240 |
| Una Senal debio interrumpir la espera de la finalizacion |
*-----------------------------------------------------------------------*

4. Ahora ahi otra opcion, y esta ilustra una de las variantes de la creacion de procesos :D, que es: " La velocidad relativa
de un proceso ", esto consiste en que tanto el proceso padre y/o el proceso hijo, ejecuten con mas rapidez sus instrucciones,
en alguno casos se puede imprimir primero el padre y despues el Hijo, y en otros ( que es el mas frecuente ) se imprime el
Proceso Hijo y al final el Padre, pero esto varia dependiendo la velocidad del proceso ( aki no tomes encuenta la potencia
que tenga tu Ordenador :D ). Bueno basandose en lo explicado, en alguno casos se creara el Proceso Hijo y antes de llegar
al seguno condicional el Proceso Padre llegara rapidamente al tercer condicional ( que es donde se comprueba el wait() ), en
este momento posiblemente si surge algun error imprimira el fprintf() del wait() y despues el fprintf() del Hijo y finalizara
el programa. En la ejecucion seria asi:

Código:
*-----------------------------------------------------------------------*
| Una Senal debio interrumpir la espera de la finalizacion |
| Soy el Proceso Hijo PID: 2240 |
*-----------------------------------------------------------------------*


La Siguiente Instruccion reinicia un proceso hasta que finalize dicho proceso, asi el Proceso Padre pase a finalizar.

Código:
*---------------------------------------------------------------*
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| int estado; |
| pid_t proceso; |
| |
| while( proceso!=wait( &estado ) ); |
*---------------------------------------------------------------*

Codigo: ps_007.c
Programa que Ilustra el uso de los POSIX, para determinar la razon de la terminacion del Proceso Hijo
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/type.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
int estado;
pid_t proceso;

if( (proceso=fork()) == 0 )
{
fprintf( stderr, "\nSoy el Proceso Hijo PID: %ld.", (long)getpid() );
exit(0);
}
else if( wait(&estado)==proceso || wait(&estado)!=proceso )
{
if( !estado )
fprintf( stderr, "\nEl Proceso Hijo PID: %ld a finalizado bien.", (long)proceso );
if( WIFEXITED(estado) )
{
fprintf( stderr, "\nEl Proceso Hijo PID: %ld termino normalmente" );
fprintf( stderr, " con un estado: %i", (long)proceso, WEXITSTATUS(estado) );
}
if( WIFSIGNALED(estado) )
fprintf( stderr, "\nEl Proceso Hijo termino debido a una senal no atrapada." );
}
else fprintf( stderr, "\nSoy el Proceso Padre PID: %ld\tProceso Hijo PID: %ld", (long)getpid(), (long)proceso );
exit(0);
}

*-----------------------------------------------------------------------*
| En Ejecucion |
*-----------------------------------------------------------------------*
| shell# ./ps_007 |
| Soy el Proceso Hijo PID: 4946 |
| El Proceso Termino Normalmente |
| El Proceso 4946termino normalmente, su estadod evuelto es: 0 |
*-----------------------------------------------------------------------*

== Explicacion ==
Este codigo resulta un poco mas detallado, a la hora de saber el PORQUE ? se detuvo o finalizo el proceso inesperadamente,
como anteriormente vimo, el wait() nos ayuda a saber el estado de un proceso, asi podemos determinar si finalizo BIEN o
si finalizo debido a una senal o interrupcion, pero mas NO sabemos la razon exacta del porke o una mejor vicion del PORQUE
finalizo de esa forma. Con una pequena implementacion del wait() junto con los POSIX podemos tener una respuesta un poco
mas detallada y concreta sobre el PORQUE de la finalizacion o interrupcion de nuestro Proceso.

Con forme al Codigo ps_009.c podemos notar que despues del IF del hijo, el siguiente condicional es donde se SUPONE debemos
poner el codigo del PADRE, pero ponemos el codigo de wait() ( ya que asi se maneja :D ), y el condicional este nos indica que
entrara dicho condicional aunque el proceso haya finalizado BIEN o MAL, una ves entrando se escriben las siguientes sintaxis que
podemos ver en el codigo del uso de los POSIX como: WIFEXITED y WIFSIGNALED, los cuales evaluaran el PORQUE finalizo el Proceso, de este modo tedremos una mejor idea mas concreta del PORQUE :D.



1.6 La Llamada    exec   al Sistema

La Llamada exec al sistema permite trasladar al proceso con un modulo ejecutable nuevo ;). De esta forma un proceso hijo ( o los que tu desees ) podra ejecutar codigo diferente al de su Proceso Padre. La manera de utilizar el exec es dejar que el Proceso Hijo ejecute el exec para el nuevo programa, mientras que el Padre continua con la ejecucion del codigo original.

Existen 6 formas o variaciones para pasar los argumentos de linea de comando y el ambiente, de igual forma la ruta de acceso
( path o directorio ) y el nombre del ejecutable. A continuacion mencionare las 6 formas o variantes:

Estas 3 formas permiten pasar los argumentos de linea de comando en forma de lista y son utiles si se conoce el numero de argumentos de
linea de comando.

Código:
*-------------------------------------------------------------------------------------------------------------------------------*
| execl( const char *ruta_del_ejecutable, const char *arg0, const char *argn, char * /*NULL*/ ); |
| execlp( const char *ruta_del_ejecutable, const char *arg0[], const char *argn, char * /*NULL*/, char *const envp[] ); |
| execle( const char *file, const char *arg0, const char *argn, char * /*NULL*/ ); |
*-------------------------------------------------------------------------------------------------------------------------------*

Estas 3 formas permiten pasar los argumentos de linea de comando en forma de arreglo o vector de argumentos xD.

Código:
*-----------------------------------------------------------------------------------------------*
| execv( const char *ruta_de_ejecutable, char *const argv[] ); |
| execvp( const char *file, char *const argv[] ); |
| execve( const char *ruta_del_ejecutable, char *const argv[], char *const envp[] ); |
*-----------------------------------------------------------------------------------------------*

Vemos un sencillo ejemplo :P.

Codigo: ps_008.c
Programa que ilustra execl(), creando un proceso hijo y ejecutando el comando: ls -l ( listar archivos en forma de lista )
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
pid_t proceso;
int estado;
char cad[30];

system( "clear" );
if( ( proceso=fork() ) == -1 )
{
fprintf( stderr, "\nError al Crear el Proceso.\n" );
exit(1);
}
else if( proceso==0 ) /*aqui el hijo*/
{
fprintf( stderr, "\nSoy el Proceso hijo con PID: %ld.\n", (long)getpid() );
fprintf( stderr, "Y voy a realizar un ls -l en la shell.\n" );
fprintf( stderr, "\nDonde kieres hacer \"ls\" ?: " );
scanf( "%s", cad );

if( execl( "/bin/ls", "ls", cad, NULL )<0 )
{
fprintf( stderr, "\n\nFallo la Ejecucion del comando \"ls\"\n" );
exit(1);
}
}
else if( wait(&estado)!=proceso )
fprintf( stderr, "\nSe Presento una Senal antes de la Finalizacion del Proceso\n" );
exit(0);
}

*---------------------------------------------------------------*
| En ejecucion |
*---------------------------------------------------------------*
| shell# ./ps_008 |
| Soy el Proceso Hijo con PID: 5590 |
| Y voy a realizar un ls -l en la shell |
| |
| Donde kieres hacer "ls" ?:  /home |
| user_name_folder |
*---------------------------------------------------------------*

== Explicacion ==

Aqui utilizaremos un poco nuestra nueva funcion ;)... execl(); que mas bien, viene siendo algo parecido a la funcion:
system();, solo que un poco mejor la funcion "execl()", no explicare el porke ya que solo es un tuto de introduccion y no me
abaracre a explicar mucho otras funciones para ahorrar hojas :D ;).

Ok, empezamos a leer el codigo, y vemos el primer condicional, este condicional se cumple a menos que NO se haya podido realizar
el Proceso, de este modo fork(); regresara un -1 como error xD. Continuando con el 2do condicional, este entra cuando el hijo
funcionando o en circulacion el Proceso Hijo, una ves entre el Condicional nos imprimira los 3 fprintf(); y esperara que le
introduscamos alguna cadenita sin espacios :D ( scanf(); ), de este modo segun mi ejemplo, introducimos: "/home" y ENTER.

Ahora ejecuta la funcion execl(); con 4 parametro, que explicare :D:
"/bin/ls" ---> es el directorio del ejecutable "ls"
"ls" --> el comando de ese ejecutable que pusimos en el parametro 1
cad ---> es un argumento alterno o argn que es ejecutado en seguida del ls, osea vendria quedando: ls cad ( ls /home )
NULL ---> es solo un termino NULO.

En caso de error la funcion execl(); retorna un valor menor que 0.

Antes de Pasar a ver un siguiente ejemplo de la utilizacion de "execv()", explicare el funcionamiento de las 3 funciones de "execl(); con parametros establecidos y el funcionamiento.

execl( "/bin/ls", "ls", cad, NULL );
Esta funcion tiene como punto principal el primer PARAMETRO, que es la ruta del ejecutable principal ( en este caso ls ), y la
caracteristica principal es que esta funcion busca el nombre del programa con la ruta especificada o relativa al directorio de
trabajo.

execlp( "/bin/ls", "ls", cad, NULL );
Esta funcion tiene los mismo parametros que "execl()", solo que esta funcion busca el directorio del ejecutable a llamar o utilizar
en la shell utilizando la variable de entorno PATH como de la misma manera que lo hace la SHELL al realizar un comando.

execle( "/bin/ls", "ls" , cad, NULL );
Esta funcion tiene los mismo parametros que "execl()", solo que el primer parametro es tipo file... este puede ser un 2do programa
en C o una bach script a ejecutar dentro de nuestro codigo, y que tambien tiene la caracteristica de que usando "execle()", el nuevo
programa hereda el ambiente del Proceso Padre ;).

Codigo: ps_009.c
Programa que ilustra el uso de execlp(), creando un proceso y realizando ls desde un bash script ;)
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
pid_t proceso;
int estado;
char cad[30];

system( "clear" );
if( ( proceso=fork() ) == -1 )
{
fprintf( stderr, "\nError al Crear el Proceso.\n" );
exit(1);
}
else if( proceso==0 ) /*aqui el hijo*/
{
fprintf( stderr, "\nSoy el Proceso hijo con PID: %ld.\n", (long)getpid() );
fprintf( stderr, "Y voy a realizar un ls -l en la shell.\n" );
fprintf( stderr, "\nDonde kieres hacer \"ls\" ?: " );
scanf( "%s", cad );

if( execlp( "/directorio/del/script/test.sh", "1", cad, NULL )<0 )
{
fprintf( stderr, "\n\nFallo la Ejecucion del comando \"ls\"\n" );
exit(1);
}
}
else if( wait(&estado)!=proceso )
fprintf( stderr, "\nSe Presento una Senal antes de la Finalizacion del Proceso\n" );
exit(0);
}

Codigo: test.sh   ( recuerda darle permisos de ejecucion :D )
*-----------------------------------------------------------------------*
|    Script de Bash que utilizas en el programa en C ;) |
*-----------------------------------------------------------------------*
| #!/bin/bash |
| |
| ls -l $1;   #$1 es el 1er argunmento que se le pasa al script |
| exit;     #por medio del programa en C ;) |
*-----------------------------------------------------------------------*

*-----------------------------------------------*
| En ejecucion |
*-----------------------------------------------*
| shell# ./ps_009 |
| Soy el Proceso Hijo con PID: 5590 |
| Y voy a realizar un ls -l en la shell |
| |
| Donde kieres hacer "ls" ?:  /home |
| user_name |
*-----------------------------------------------*

== Explicacion ==

Pues el programa se ejecuta, el caso que NO se peuda crear el Proceso Hijo, fork(); retorna un -1 e imprime el primer fprintf();, de lo contrario crea el Proceso hijo y el 2do condiciona entra e imprime los 3 fprintf();, despues espera que le pases una cadena
sin espacios ( osea: /home ), despues ejecuta la funcion "execlp()" y aqui la funcion en caso de error retorna un valor menor que
0. Pero en caso de EXITO realiza lo siguiente: En el primero para metro manda llamar al ejecutable y envia un 1, osease pasas desa-
persivido el parametro 1, ya que el SCRIPT hara el ls ;), despues el parametro 3 ( osea argn ), le envias la cadena que seria: "/home"
y mas o menos realizara esto: "./test.sh /home".

Ahora explicare el sencillisimo bash script, que solamente realiza una llamada a "ls" y le pasa el primer argumento ( osea /home ), que en este caso lo identifica como: " $1 ". Y por consecuencia el primer parametro viene siendo " /home " ;). Despues finaliza el script
con una llamda a exit; :D.

Como ultimo condicional examina la terminacion del Proceso Hijo con una llamda a wait() :D.

Ahora explicare la utilizacion de los siguientes 3 tipos de exec() que utilizan un array en donde guardan el argumento, la exlicacion sera conforme al ejemplo que encontraran mas abajo ;):


execv( "/bin/ls", &argv[0] );
Esta funcion toma 2 parametro que son: la ruta del ejecutable y como segundo parametro un argumento.

execvp( "direccion/del/script/test.sh", &argv[0] );
Este ejecutable toma como primer parametro la ruta del archivo tipo FILE alterno, en nuestro ejemplo utilizamos un bash script :D,
y como segundo parametro un argumento.

execve( "/bin/ls", &argv[0], envp[] );
Esta funcion es igual que execv(), sol que tiene un 3er argumento que sirve para especificar el ambiente del proceso creado.

Codigo: ps_010.c
Programa que ilustra el uso de execv(), creando un proceso y haciendo ls a un directorio :D
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main( int argc, char *argv[] )
{
pid_t proceso;
int estado;

system( "clear" );
if( ( proceso=fork() ) == -1 )
{
fprintf( stderr, "\nError al Crear el Proceso.\n" );
exit(1);
}
else if( proceso==0 ) /*aqui el hijo*/
{
if( execv( "/bin/ls", &argv[0] )<0 )
{
fprintf( stderr, "\n\nFallo la Ejecucion del comando \"ls\"\n" );
exit(1);
}
}
else if( wait(&estado)!=proceso )
fprintf( stderr, "\nSe Presento una Senal antes de la Finalizacion del Proceso\n" );
exit(0);
}

*---------------------------------------*
| En Ejecucon |
*---------------------------------------*
| shell# ./ps_010 /home |
| user_name |
*---------------------------------------*

== Explicacion ==

Pues bueno, es un 90% parecido a los demas jajajaj :D, lo unico es que execv() utiliza un array, y ahi guarda los argumentos
conforme al ejemplo de nuestro codigo es, pasar el primer argumento ( arg[0] ) y se le realiza: ls al argumento pasado :D.

En este caso quedaria asi: execv( "/bin/ls", &argv[0] ), execv(); solo utiliza 2 parametro, como ya habiamos explicado :D.


Codig: ps_011.c
Programa que ilustra el uso de execvp(), creando un proceso, y realizando ls por medio de un Bash Script ;)
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main( int argc, char *argv[] )
{
pid_t proceso;
int estado;

system( "clear" );
if( ( proceso=fork() ) == -1 )
{
fprintf( stderr, "\nError al Crear el Proceso.\n" );
exit(1);
}
else if( proceso==0 ) /*aqui el hijo*/
{
if( execvp( "/home/diabliyo-to/CPROGS/test.sh", &argv[0] )<0 )
{
fprintf( stderr, "\n\nFallo la Ejecucion del comando \"ls\"\n" );
exit(1);
}
}
else if( wait(&estado)!=proceso )
fprintf( stderr, "\nSe Presento una Senal antes de la Finalizacion del Proceso\n" );
exit(0);
}

Codigo: test.sh   ( recuerda darle permisos de ejecucion :D )
*-----------------------------------------------------------------------*
|    Script de Bash que utilizas en el programa en C ;) |
*-----------------------------------------------------------------------*
| #!/bin/bash |
| |
| ls -l $1;   #$1 es el 1er argunmento que se le pasa al script |
| exit;     #por medio del programa en C ;) |
*-----------------------------------------------------------------------*

*---------------------------------------*
| En Ejecucon |
*---------------------------------------*
| shell# ./ps_011 /home |
| user_name |
*---------------------------------------*

== Explicacion ==
Pues nuevaente los mismo que el ejemplo "ps_010.c" solo que la funcion: execvp(); realiza ls al por medio del bash script,
al programa en C, se le pasa un argumento ( argv[0], que es /home ), despues este es pasado al script ( $1 ), y se
realiza el ls dentro del directorio ;).
En línea

Diabliyo


Desconectado Desconectado

Mensajes: 1.427


M.S.I Angel Cantu


Ver Perfil WWW
2. SENALES
« Respuesta #3 en: 5 Octubre 2005, 21:18 »

2.1 Teoria

Una Senal es un notificador por software para un proceso, de que ha ocurriodo cierto suceso o evento.

Una Senal es generada cuando ocurre un evento, que fue el que causo la senal.

El Tiempo de Vida de una Senal es el tiempo o intervalo en el que se produjo la senal y su finalizacion.

Una Senal esta "pendiente" cuando es generada pero todavia no es terminada.

Para que un Proceso pueda atrapar una senal, debe antes haber ejecutado el "Manejador de Senal", para poder activar
el Manejador de Senal se tiene que hacer una llamada a "sigaction". Despues esta misma llamada a dos tipos de
Manejadores que son: "SIG_DFL" o "SIG_IGN".

SIG_IGN --> indica que debe realizarse una accion establecida al momento de recivir la senal.
SIG_DFL --> indica que la senal debe ignorarse.

La accion que se tome al momento de recivir una senal, esta dependera de 2 factores.

1- Tener activado el Manejador de Senal
2- La Mascara de Senal del Proceso

La "Mascara de Senal" contiene una lista de las senales que en determinado momento se encuentran blockeada. Osea es
una cola de senales :D.

Las Senales presentan dos "estados" ( por decirlo asi ), que es: blockeado e ignorada.

Senal Blockeada
Las Senales Blockeadas, son tomadas por el Manejador y puestas en la cola de Senales, osea las ponemos pendientes.

Senal Ignorada
Las Senales Ignoradas son senales desechadas automaticamente.

Podemos enviar una Senal de Blockeo cambiando con "sigprocmask" la Mascara de la Senal del Proceso ;).



2.2 Envio de Senales ( Teoria 2 )

Las Senales tienen nombres simbolicos, y dichos nombres siempre inician con la palabra "SIG", estos nombres los podemos
encontrar definidos en el archivo: "signal.h".

Observar: /usr/lib/bcc/include/signal.h   :D Ahi encontraras aun mas :D.

A continuacion una Tabla de los nombres de las senales junto con su descripcion :D.

Código:
*-----------*-------------------------------------------------------------------*
|  Simbolo  | Significado |
*-----------*-------------------------------------------------------------------*
|  SIGABRT  |   Terminacion anormal como la inciada por abort() |
|  SIGALRM  |   Senal de espera como la iniciada por alarm() |
|  SIGFPE   |   Error en operacion aritmetica como en la division por cero |
|  SIGHUP   |   Colgado (muerto) de la terminal de control (proceso) |
|  SIGILL   |   Instruccion de hardware no valida |
|  SIGINT   |   Senal de atencion interactiva |
|  SIGKILL  |   Terminacion (no se puede atrapar o ignorar) |
|  SIGPIPE  |   Escritura en un entubamiento sin lectores |
|  SIGUIT   |   Terminacion interactiva |
|  SIGSEGV  |   Referencia no valida a memoria |
|  SIGTERM  |   Terminacion |
|  SIGUSR1  |   Senal 1 definida por el usuario |
|  SIGUSR2  |   Senal 2 definida por el usuario |
*-------------------------------------------------------------------------------*

POSIX.1 tambien define un grupo opcional de senales para "control de trabajos". Los "Interpretes" utilizan estas senales
para controlar la interaccion entre los "procesos de primero y segun plano".

Si una implantacion soporta el control de trabajos ( _POSIX_JOb_CONTROL ), entonces tambien debe dar soporte a las "senales de
control de trabajo de POSIX.1
".

Código:
*-----------------------------------------------------------------------------------------------*
| Tabla de Senales de Control de Trabajo de POSIX.1 |
*-----------*-----------------------------------------------------------------------------------*
|  Simbolo  | Significado |
*-----------*-----------------------------------------------------------------------------------*
|  SIGCHLD  |   Indica terminacion o suspension del proceos hijo |
|  SIGCONT  |   Continuar si esta detenido ( efectuado cuando se genera ) |
|  SIGSTOP  |   Senal de Alto ( no se puede atrapar o ignorar ) |
|  SIGTSTP  |   Senal de Alto Interactiva |
|  SIGTTIN  |   Proceso de Segundo Plano que intenta leer la terminal de control |
|  SIGTTOU  |   Proceso de Segundo Plano que intenta escribir en la terminal de control |
*-----------*-----------------------------------------------------------------------------------*



2.2.1 La Funcion kill();

Las senales se generan mediante el comando kill();, este comando esta disponible como un funcion en C y como comando Bash para
la Shell ( interprete de comandos ), veamos las opciones que tiene el comando kill en la shell :D..

Código:
*-----------------------------------------------*
| Utilizacion de kill en la Shell |
|    Tomado del MAN de Linux ;) |
*-----------------------------------------------*
| kill [ -s SIGNAL | -SIGNAL ] PID... |
| kill -l [SIGNAL]... |
| kill -t [SIGNAL]... |
*-----------------------------------------------*

Ahora antes de un ejemplo, necesitamos crear un proceso, y esto lo haremos abriendo solamente la Terminal, despues veremos el
ID del Proceso que se asigno a la terminal, despues desde un programita en C, le enviaremos una senal por medio de la funcion
kill(); al ID del Proceso y veremos magicamente como desaparece ;) :P....

Pero antes debemos conocer el comando "ps" de la Shell, veremos algunas opciones de "ps" :D.


ps [-][OPCION(es)]
Comando que nos permite ver los o nuestros procesos :D y utiliza la siguientes sintaxis:

Código:
*---------------------------------------------------------------*
| OPCION | DESCRIPCION |
*--------*------------------------------------------------------*
|   -A   |   Ver Todos los Procesos |
|   -a   |   Ver Todos los Procesos de seguridad ( tty ) |
|    T   |   Ver los Procesos de la Terminal |
|    r   |   Ver los Procesos que se encuentran corriendo |
|   -u   |   Ver Procesos de Usuario Efectivo por ID |
*---------------------------------------------------------------*

Para ver mas opciones utilizen: shell# ps --help.

Existen 2 simbolos ya mencionados en la tabla de " nombres de senales " y son: SIGUSR1 y SIGUSR2, con estos dos simbolos podemos enviar senales con ayuda de la funcion kill(); y terminar procesos, la sintaxis de kill(); es:

Código:
*-------------------------------------------------------*
| #include <sys/types.h> |
| #include <signal.h> |
| |
| int kill( pid_t procesoID, int senal ); |
*-------------------------------------------------------*

La funcion kill(); envia dos parametro que son:

pid_t procesoID --> es el numero de identificacion del proceso a enviarle un tipo de senal
int senal --> es la senal tomada de las tablas ya mencionadas :D
En Error, kill(); regresa un -1.

Antes de realizar el ejemplo, abramos una terminal y hagamos: ps u, aqui veremos el ID de Proceso que ocupa nuestra Terminal,
ahora abramos una segunda terminal y vayamos a la PRIMER terminal y volvamos a repetir el comando y conoceremos el otro ID de
Proceso que tiene nuestra nueva terminal, el fin de esto es finalizar alguna de las dos terminales desde nuestro programa ;).

Codigo: ps_012.c
Programa que ilustra el uso de kill();
Código:
#include <stdio.h>
   #include <stdlib.h>
   #include <sys/types.h>
   #include <signal.h>

int main()
{
pid_t num;

system( "clear" );
printf( "Veamos los Procesos Activos:\n" );
getchar();
system( "ps u" );
printf( "\nProceso a Finalizar: " );
scanf( "%i", &num );
if( kill( num, SIGUSR1 ) == -1 )
perror( "No se Puedo Finalizar el Proceso" );
else printf( "\nProceso Finalizado con Exito :D\n" );
getchar();
return 0;
}

*-------------------------------------------------------------------------------*
|     En Ejecucion |
*-------------------------------------------------------------------------------*
| shell# ./ps_012 |
| |
| Veamos los Procesos Activos |
| USER PID %CPU %MEM VSZ RSS TTY COMMAND |
| user 4722 0.0 0.2 3228 1736 pts/38 /bin/bash |
| user 5051 0.0 0.2 3232 1736 pts/39 /bin/bash |
| user 5962 0.0 0.0 1232 304 pts/39 ./ps_012 |
| user 5964 0.0 0.0 2164 688 pts/39 ps u |
| |
| Proceso a Finalizar: 4722 |
| |
| Proceso Finalizado con Exito :D |
*-------------------------------------------------------------------------------*

== Explicacion ==
Teniendo dos terminales abiertas y conociendo sus IDs de ambas, tomamos una terminal y corremos el programa
ahora nos pondra los Procesos Existentes, que en este caso seran:

-Terminal 1
-Terminal 2
-Programa en Ejecucion
-llamada a ps u

Ahora ponemos el ID del proceso a finalizar y en el caso que lo escribamos BIEN, pues lo finalizara y nos dira:
"Proceso Finalizado con Exito :D", de lo contario: "No se puede Finalizar el Proceso".

La Senal que usamos para finalizar el proceso es: SIGUSR1, o tambien podemos usar SIGUSR2, de todos modos funciona.



2.2.2 La Funcion raise();

La Funcion raise(); permite que un proceso envie una senal, la funcion solo ocupa un paremtro que es: el numero de la senal.
Y su sintaxis es:

Código:
*-------------------------------------------------------*
| #include <signal.h> |
| |
| int raise( int sig ); |
*-------------------------------------------------------*

Cada ves que se presione una tecla del teclado, se geenra automaticamente una interrupcion por hardware que es manejada por
el controlador de dispositivo del teclado. Existen dos caracteres, INTR y QUIT, que hacen que el controlador de dispositivo
envie una senal a los procesos de primer plano.

Un usuario puede enviar la senal SIGINT a los procesos de primer plano mediante la introduccion del caracter INTR, este
caracter es a menudo CTRL-C.

Un usuario puede enviar la senal SIGQUIT a los procesos de primer plano mediante la introduccion del caracter QUIT, este
caracter es a menudo CTRL-|.

De este modo la terminal interpreta a CTRL-C como el caracter INTR, osea genera la senal SIGINT y del mismo modo
interpreta a CTRL-| como el caracter QUIT, osea genera la senal SIGQUIT.

Otro extra seria: CTRL-Z que genera una senal SIGSTOP y CTRL-Y genera una senal SIGCONT.


Codig: ps_013.c
Programa que ilustra el uso de raise() de forma muy sencilla
Código:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

int main()
{
char _op;
pid_t proceso;

system( "clear" );
printf( "-Interrumpir." );
printf( "\n-Salir." );
printf( "\n\n:: " );
scanf( "%c", &_op );
switch( toupper(_op) )
{
case 'I':
if( (proceso= fork()) == 0 )
{
fprintf( stderr, "\nSoy el Proceso Hijo PID: %ld\n", (long)getpid() );
raise( SIGUSR1 );
}
else
fprintf( stderr, "\nSoy el Proceso Padre.\n" );
break;
}
getchar();
return 0;
}

*-----------------------------------------------*
| En Ejecucion |
*-----------------------------------------------*
| shell# ./ps_013 |
| |
| -Interrumpir |
| -Salir |
| :: |
| |
| Soy el Proceso Padre |
| Soy el Proceso Hijo PID: 5357 |
*-----------------------------------------------*

== Explicacion ==
Bueno es ovio todo el codigo jajaj :D, pero explicare lo que ahi dentro del "case 'I':", se crea el proceso hijo y al entrar
el condicional del Proceso Hijo, envia una senal tipo SIGUSR1, a kien ??... logico :d, al Padre, osea un Proceso invocado
envia una senal al Proceso que lo invoco, osea... Hijo envia senal al Padre y asi sucesivamente.

En este ejemplo vimos el envio de la senal SIGUSR1 que es un ejemplo demasiado sencillo, pero podemos usar otras senales, que
en este caso seria en el programa final ;), donde podremos usar otros tipos de senales como las que se listan en la Tabla de Senales.



2.2.3 La Funcion alarm();

La funcion alarm(); permite que un Proceso envie una senal, la funcion ocupa un parametro que es en el formato de segundos.

La sintaxis es:

Código:
*-----------------------------------------------------------------------*
| #include <unistd.h> |
| |
| unsigned int alarm( unsigned int segundos ); |
*-----------------------------------------------------------------------*

La funcion alarm(); realiza el envio de una senal SIGALRM.

Codigo: ps_014.c
Programa que ilustra la funcion alarm() de una forma muy clara ;)
Código:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
char nombre[20];

system( "clear" );
alarm( 3 );
printf( "NOTA: tienes 3 segundos para poner tu nombre, sino se finaliza el programa\n\n" );
printf( "Como te llamas: " );
scanf( "%s", nombre );
getchar();
return 0;
}

*---------------------------------------------------------------------------------------*
|     En Ejecucion |
*---------------------------------------------------------------------------------------*
| shell# ./ps_014 |
| |
| NOTAL tienes 3 segundos para poner tu nombre, sino se finalza el programa |
| |
| Como te llamas: Temporizador |
*---------------------------------------------------------------------------------------*

== Explicacion ==
Como vemos alarm() establece un intervalo de tiempo para una vez cumplido el intervalo envia una senal tipo SIGALRM que
realiza la finalizacion del Proceso, en el ejemplo pues se ve demasiado claro, ya que si en un intervalo de 3 segundos,
si el usuario NO pone el nombre, pues finaliza el tiempo y el programa, de lo contrario normalmente.

La diferiencia es que si finaliza por medio de la alarma, pues imprime: TEMPORIZADOR, y si escribe el nombre ( osea que
finalize bien ), entonces solamente termina el programa con el return 0, como normalmente lo hacemos.

Como ultimo dato importante explicare los diferentes cambios que se realizan al llamar a alarm(); varias veces.

Cada ves que se invoque la funcion alarm() se establece un intervalo de tiempo, pero si llamamos a alarm(); por segunda vez,
entonces suceden 2 cosas:

1- Si llamamos a alarm(); por segunda ves sabiendo que el primer alarm() a terminado, pues el segundo alarm(); no tendra efecto.
2- Si llamamos a alarm(); por segunda ves sabiendo que el primer alarm() aun NO ha terminado, entonces alarm(); es reinicializado al nuevo valor que se le dio por segunda vez, y la primer llamada que se le realizo a alarm(); queda deshabilitada y solo contara la nueva llamada ( osea la segunda ).

En conclusion cada ves que se llame a alarm();, siempre y cuando las llamadas anteriores NO han terminado su intervalo de tiempo,
pues el alarm() sera reinicializado con el nuevo valor en segundos. Osea alarm(); no apila las peticiones.

Si llamas a alarm(); con un valor 0 en segundos, entonces la llamada a alarm se cancela, lo mismo si deseas que alarm(); en determinado momento se deshabilite :D.



2.2.4 La Mascara de Senales y Conjuntos de Senales

Bueno primeramente seria bueno que repasaran nuevamente el tema Tema: ---- 2.1 Teoria de Senales ---

Solo resaltare algunas cosas importantes que son:

- un proceso puede impedir el deposito de una senal, mediante el Blockeo de esta.
- las senales blockeadas no afectan el comportamiento del proceso, solo hasta que son depositadas
- la mascara de senal, contiene una cola de senales blockeadas
- la mascara de la senal es de tipo: sigset_t

Un Proceso puede blockear una senal mediante una previa llamada a: "sigprocmask" de su mascara de senal.
Un Proceso puede estar expuesto a ignorar las senales o blockearlas, esto lo hacemos con una llamada al manejador de senales ( sigaction ),
enviando uno de los dos metodos: SIG_IGN ( realizar una accion con la senal ) o SIG_DFL ( las senales deberan ignorarse ).

Mencionare las funciones necesarias para realizar una operacion con una senal, yasea de tipo Blockeo o Desblockeo.

Código:
*---------------------------------------------------------------*
| #include <signal.h> |
| |
| int sigemptyset( sigset_t *set ); |
| int sigfillset( sigset_t *set ); |
| int sigaddset( sigset_t *set ); |
| int sigdelset( sigset_t *set ); |
| int sigismember( const sigset_t *set, int signo ); |
*---------------------------------------------------------------*

sigemptyset( sigset_t *set )
---> inicia un conjunto de senales, de modo que no contenga senales.

sigfillset( sigset_t *set )
---> inicia un conjunto de senales, de mood que contenga todas las senales.

sigaddset( sigset_t *set )
---> agrega una senal especifica al conjunto.

sigdelset( sigset_t *set )
---> quita una senal del conjunto.

sigismember( const sigset_t *set, int signo )
---> comprueba si una senal el miembro del conjunto, devuelve 1 si existe o 0 cuando no exista y -1 en caso de error.


Un Proceso de igual forma puede modificar o examinar su mascara de senal con la funcion:

Código:
*-------------------------------------------------------------------------------*
| #include <signal.h> |
| |
| int sigprocmask( int como, const sigset_t *set, sigset_t *oset ); |
*-------------------------------------------------------------------------------*

int como
---> entero que indica la forma en como se va a modificar la mascara de senal, las tres formas pueden ser:

  • SIG_BLOCK
[il]anade una coleccion de senales que se encontraran blockeadas en ese momento.[/il]
   [li]SIG_UNBLOCK[/li]
      [il]borra una coleccion de senales que se encuentran blockeadas en ese momento.[/il]
   [li]SIG_SETMASK[/li]
      [il]especifica de una coleccion de senales dadas, las que seran blockeadas.[/il]
[/list]

const sigset_t *set
--->apunta al conjunto de senales que seran usadas para la modificacion.

sigset_t *oset
--->apunta hacia la direccion de una senal de tipo sigset_t que guarda el conjunto de senales que estaban blockeadas antes de
    la llamada a "sigprocmask();".
   
En los parametros de "sigprocmask();" los parametros segundo o tercero pueden ser NULL.

-Cuando el 2do parametro es NULL, el 3er parametro proporcionara la mascara que se esta utilizando
-Cuando el 3er parametro es NULL, el 2do parametro proporcionara la mascara que se esta utilizando, y la variable de *oset
 no tendra valor alguno.
-Si ocurre error alguno "sigprocmask()" devuelve -1 y activa "errno()", en caso de exito devuelve 0.



2.2.5 Envio de Senales con SIGNAL();

La funcion "signal();" realiza el envio de una senal asociada con una accion. Su sintaxis es:

Código:
*-----------------------------------------------*
| #include <signal.h> |
|
| int signal( int senal, accion ); |
*-----------------------------------------------*

int senal
Es la senal a enviar, segun la tabla de senales.

accion
Puede ser bien una llamada a una funcion o de caso contrario el tipo de trato que se le dara a la senal.

Codigo: ps_015.c
Programa que envia una senal al proceso hijo ( SIGINT CTRL-C ), despues detalla la senal recivida por el proceso, conforme a POSIX ;)
Código:
#include <stdio.h>
   #include <stdlib.h>
   #include <signal.h>
   #include <sys/types.h>
   #include <unistd.h>
   #include <sys/wait.h>

int main()
{
pid_t proceso;
int estado;

system( "clear" );
if( (proceso= fork()) == 0 )
{
fprintf( stderr, "\n\nSoy Proceso Hijo %ld.", (long)getpid() );
sleep(2);
fprintf( stderr, "\n\nDe nuevo Proceso Hijo ID: %ld.\n\n", (long)getpid() );
/*exit(0);*/
}
else
{
signal( SIGINT, SIG_IGN );
while( waitpid( proceso, &estado, 0 ) != proceso );
fprintf( stderr, "\n\nSoy Proceso Padre %ld e Hijo %ld.", (long)getpid(), (long)proceso );
if( WIFSIGNALED(estado) )
fprintf( stderr, "\n\nEl Proceso Hijo a Recivido la Senal %d\n\n", WTERMSIG(estado) );
if( WIFEXITED(estado) )
fprintf( stderr, "\n\nEstado de Salida del Hijo %d\n\n", WEXITSTATUS(estado) );
}
getchar();
return 0;
}

*-------------------------------------------------------*
|     En Ejecucion |
*-------------------------------------------------------*
| shell# ./ps_015 |
| Soy el Proceso Hijo 5024 |
| De nuevo Proceso Hijo ID 5024 |
| |
| ( si teclead CTR-C ) |
| Soy el Proceso Padre 5022 e Hijo 5024 |
| El Proceso Hijo a Recivido la Senal 2 |
| |
| ( si tecleas ENTER ) |
| Soy el Proceso Padre 5022 e Hijo 5024 |
| Estado de Salida del Hijo 0 |
*-------------------------------------------------------*

=== Explicacion ===
Entra el proceso hijo, muestra si ID, y espera 2 segundos e imprimira el siguiente "fprintf();".
Ahora corre el proceso Padre, y envia SIGINT (senal interactiva) como senal, y accion de tipo: SIG_IGN
( ignorar senal ). Esto nos permite enviar una senal de tipo CTRL-C al proceso HIJO, terminar el proceso hijo, pero NO terminar
nuestro programa :D. Osea, ignora CTRL-C de cierta forma que finaliza el proceso, pero el programa sigue corriendo :D.

Ahora el proceso padre llega al "while();" y segun el "waitpid();" frenara al padre hasta que el proceso hijo FINALIZE, o bien
reciva una senal de interrupcion o finalizacion, asi el proceso Padre se imprimira, mostrara su ID y por ultimo detallara
el estado en que finalizo el proceso hijo.

Si presionamos ENTER solo dira: "Estado de Salida del Hijo 0".
Si envias el comando: CTRL-C ( senal interactiva ), finalizara el proceso pero NO el programa y como detalle de la finalizacion
del proceso imprimira: "El Proceso Hijo a Recivido la Senal 2".
En línea

Diabliyo


Desconectado Desconectado

Mensajes: 1.427


M.S.I Angel Cantu


Ver Perfil WWW
3. ARCHIVOS AVANZADOS
« Respuesta #4 en: 5 Octubre 2005, 21:24 »

3.1 Teoria

Los Archivos son una de las caracteristicas mas importantes de UNIX, ya que se pueden realizar llamadas de E/S por medio de los
"Descriptores de Archivos", estos abarcan:

  • -Terminales
  • -Discos
  • -Cintas
  • -Audio
  • -Comunicacion por Red
  • -etc....

Esto hace un enfasis a lo que dicen por ahi: "Unix es un Archivo".

Otra caracteristica es su independiencia de los Dispositivos, ya que utilizando las llamadas a Archivos se logra un control de una
Interfaze de Dispositivo.

Un Dispositivo Periferico es una pieza de hardware conectada a un ordenador.

Una Pequena lista de Dispositivos Perifericos son:

  • -Discos (CD-ROMs )
  • -Cintas
  • -Pantallas
  • -Teclados
  • -Impresoras
  • -Ratones
  • -Interfaces de Red
  • -etc....

Estos Dispositivos son manejados por los Programas de los Usuarios, estos programas realizan el Control de E/S, estos mismo programas
hacen llamadas a ciertos programas del Sistema Operativos, denominados: Controladores de Dispositivo ( Device Driver ).

En base a esto, cada Programador debe aprender un conjunto de llamadas para el control de Dispositivo, Unix a simplificado del mismo modo la Interfaze de Dispositivo, al proporcionar un acceso uniforme a muchos Dispositivos a traves de 5 llamadas que son:

  • -open
  • -close
  • -read
  • -write
  • -ioctl

Todos los Dispositivos en Unix estan representados por Archivos denominados "Archivos Especiales", los cuales estan localizados en: /dev



3.2 Directorios y Rutas de Acceso

Unix Organiza los Archivos y Programas en directorios dentro del Disco Duro ( HDD ), estos arcvhios/programas/directorios, tiene una ruta de acceso a ellos, y estos los Organiza mediante una Organizacion de Arbol, el cual se relacionan con el Archivo a Buscar.

La forma de Arbol que adopta la Organizacion de Archivos es Unix es:

Código:
/ <-- raiz
|
-[dir_A]
  |--[dir_B] <-- carpeta
       |--archivo_01.txt
  |--archivo_02.txt
  |--archivo_03.txt
-[dir_C] <-- carpeta
  |-- archivo_04.txt

/
Es el nodo principal o raiz del S.O. en la Organizacion de Archivos.

Apartir de aqui podremos formar una ruta de acceso para llegar a un archivo.

Por ejemplo:
Si queremos llegar al archivo: "archivo_01.txt" su ruta es:"/dir_A/dir_B/" y su sintaxis en la shell ( linea de comandos ) es:

Código:
*---------------------------------------*
| shell# cd /dir_A/dir_B |
*---------------------------------------*

Recordemos que una "Ruta de Acceso" es la direccion para llegar a un dato o archivo. Esta direccion puede ser corta o larga, no ahi atajos sino un acceso rapido siempre y cuando sepas su ruta exacta.

Los comandos "cd", "ls" y "pwd" nos seran de utilidad, ya que sus funciones son:

cd
Accesa o Sale de/a un directorio.

ls
Lista los archivos de un directorio o de donde estes posicionado.

pwd
Nos dice la hubicacion que tenemos o donde estamos posicionados.

La funcion getcwd(); nos devuelve la ruta de acceso del directorio de trabajo activo.

Código:
*---------------------------------------------------------------*
| #include <unistd.h> |
| extern char *getcwd( char *buf, size_t size ); |
*---------------------------------------------------------------*

size_t size
Especifica la longitud maxima del nombre de la ruta de acceso que puede emplear quien la llama.

char *buf
Si "buf" no es NULL, getcwd(); copia el nombre en "buf".
Si el nombre es mayor que "size" getcwd(); devuelve -1.
Si getcwd(); tiene exito, devuelve el apuntador al nombre de la ruta de acceso.

Codigo: ps_016.c
Imprime la ruta de acceso del directorio de trabajo activo
Código:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define MAX 250

int main()
{
char ruta[MAX];

system( "clear" );
if( getcwd( ruta, MAX ) == NULL ) )
{
perror( "Error al Buscar la Ruta de: \"%s\"." );
exit(0);
}
printf( "Directorio Actual es: %s", ruta );
getchar();
return 0;
}

*-------------------------------------------------------*
|     En Ejecucion |
*-------------------------------------------------------*
| shell# cd /home/mi_user/bin |
| shell# ./ps_016 |
| Directorio Actual es: /home/mi_user/bin |
*-------------------------------------------------------*

== Explicacion ==
Bueno la explicacion de codigo es.
Declaramos una Array de Cadena: "char ruta[MAX];", donde MAX esta definido con una dimension de 250. Despues sigue el codigo
y llega el primero codificional, donde realiza la Resolucion de la Ruta de Acceso, que como vemos, antes de correr el programa, accesamos a
"/home/mi_user/bin", y despues ejecutamos el archivo: ./ps_016, donde como resultado nos devuelve la Ruta de Acceso o posiciona donde estamos en la shell. :D. Los parametro que pasamos a getcwd(); fueron, "ruta", que es la variable ( array cadena ) donde se almacenara la direccion y MAX que es la dimension maxima del buffer ( ruta ).


A continuacion veremos algunas funciones relacionadas con el manejo de Directorios, son:

Código:
*-------------------------------------------------------*
| #include <stdio.h> |
| #include <dirent.h> |
| |
| DIR *opendir( const char *filename ); |
| struct dirent *readdir( DIR *dirp ); |
| void rewinddir( DIR *dirp ); |
| int closedir( DIR *dirp ); |
| long telldir( DIR *dirp ); |
| void seekdir( DIR *dirp, long loc  ); |
*-------------------------------------------------------*

Código:
*-------------------------------------------------------*
| DIR *opendir( const char *filename ); |
*-------------------------------------------------------*
Proporciona un identificador de bloque al directorio para las funciones de Directorio. Null en caso de error.

Código:
*-------------------------------------------------------*
| struct dirent *readdir( DIR *dirp ); |
*-------------------------------------------------------*
Devuelve un apuntador a una estructura que contiene la informacion sobre la siguiente entrada del Directorio. Devuelve NULL al llegar al final del Directorio.

Código:
*-------------------------------------------------------*
| void rewinddir( DIR *dirp ); |
*-------------------------------------------------------*
Para posicionar el apuntador al Inicio del Directorio.

Código:
*-------------------------------------------------------*
| int closedir( DIR *dirp ); |
*-------------------------------------------------------*
Cerrar el identificador.

Código:
*-------------------------------------------------------*
| long telldir( DIR *dirp ); |
*-------------------------------------------------------*
Devuelva el desplazamiento dentro del Directorio de la Entrada en Uso.

Código:
*-------------------------------------------------------*
| void seekdir( DIR *dirp, long loc ); |
*-------------------------------------------------------*
Reposiciona las entradas en uso del Directorio, a la posicion especifica por "loc".

NOTA: las funciones telldir(); y seekdir(); no son parte de POSIX.

Ahora veremos unos ejemplos :D.

Codigo: ps_017.c
Programa que imprime la lista de Archivos contenidos en un Directorio
Código:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>

int main()
{
DIR *dirp;
char dir[80];
struct dirent *direntp;

system( "clear" );
scanf( "%s", dir );
if( (dirp= opendir( dir ) )==NULL )
{
fprintf( stderr, "\n\nNo se pudo abrir \"%s\", Error: %s\n", argv[1], stderr( errno ) );
exit(1);
}
while( (direntp= readdir( dirp ) ) !== NULL )
printf( "\n%s", direntp->d_name );
printf( "\nFin del Programa..." );
closedir( dirp );
exit(0);

*---------------------------------------*
| En Ejecucion |
*---------------------------------------*
| shell# ./ps_017 |
| Directorio: / |
| . |
| .. |
| bin |
| dev |
| etc |
| lib |
| mnt |
| opt |
| tmp |
| sys |
| var.... etc.. |
| Fin del Programa... |
*---------------------------------------*

== Explicacion ==
Este programa imprime la lista de los directorios/archivos contenidos en un DIRECTORIO xD. Describiremos las partes importabtes del codigo, empezando por el "DIR *dirp;", esta variable es de tipo DIR para usarla junto con la funcion "opendir();", despues se declara una variable para la estrcutura "struct dirent *direntp;" que la obtenemos de "dirent.h", llegamos a la linea donde nos pide el nombre dle directorio, y es guardada en la variable "char dir[80];", despues se le pasa el contenido a la funcion opendir(), "dirp= opendir(dir);", y esta misma devuelve un identificador que sera utilizado en las funciones de Manejo de Directorios. En seguida lee el directorio llamando a "direntp= readdir(dirp);", esta funcion devuelve un puntero a la estructura que contiene el apuntador y es imprimida su informacion llamando al miembro: "direntp->d_name". Y listo, finaliza el programa cerrando el identificador "closedir(dirp);".



3.3 Busquedas de Rutas de Acceso y Variable de Ambiente

Bueno antes de empezar :D, les adelanto que este tema no tiene codigo :P xD, es mas teorico y de conocimiento de tu sistema unix, de cierta forma puede ser un buen tip o solo un buen conocimiento sobre como busca unix los comandos y lo llama, aqui mismo entra la variable de ambiente que es: "PATH". Asi que si desiden brincarlo, pues perfecto :D, no ahi problema ;).

Como todos sabemos, Unix es un sistema que se basa en comandos ( por lo general, aunque cuenta con modo grafico ), y por lo general los usuarios que utilizan UNIX, lo utilizan mas el modo consola ( shell ), y cuando llamamos a programas en la linea de comandos, como por ejemplo:"ls" ( lista archivos ), nosotros llamamos directamente desde la shell a "ls", mas NO lo llamamos desde su ubicacion exacta, que seria( "/usr/bin/ls" ). Aqui es donde entra las Busquedas de Rutas de Acceso de manera sistematica, el sistema unix busca el programa en las rutas contenidas en la variable de ambiente "PATH", utilizando cada uno de ellos. "PATH"  contiene los nomrbes completos de las rutas de acceso de los directorios mas importantes, separados por dos puntos.

Código:
*-----------------------------------------------------------------------*
| /usr/bin:/etc:/usr/local/bin:/usr/ccs/bin:/home/username/bin: |
*-----------------------------------------------------------------------*

Segu nuestra el contenido de la variable de ambiente "PATH", cuando ejecutemos por ejemplo: "ls", va ir a buscar primero al directorio: "/usr/bin", en caso que NO se encuentre ahi, entonces pasa al siguiente directorio: "/etc" y asi sucesivamente. Si tienes cursiosidad por saber la ubicacion de algun programa, solo escribe en la shell: "which programa".

Ejemplo:
Código:
*-------------------------------*
| shell# which ls |
| /usr/bin/ls |
*-------------------------------*

Ahora sabemos que la mayoria de los programas de unix estan contenidos en: "/usr/bin", osea que, si por ejemplo hacemos un programa cualquiera y le ponemos: "mi_programa", despues este ejecutable lo pasamos a: /usr/bin.... Con esto nos evitariamos la molesta de llamar a nuestro programa desde su direccion exacta que era ( "./home/user/carpeta/mi_programa" ), asi solamente en la shell escribimos: mi_programa y neustro programa correra de inmediato ;). Cada ves que actuelicemos nuestro codigo fuente del programa y lo compilemos, ese EJECUTABLE lo copiamos/sustituimos en "/usr/bin" y tendremos nuestro programita actualizado y funcional dentro de la SHELL como cualquier otro programa.

Ejecucion de Nuestro programa antiguamente:
Código:
*-------------------------------------------------------*
| shell[ /home/user/codigos/ ]# ./mi_programa |
*-------------------------------------------------------*

Ejecucion de Nuestro programa contenido originalmente en "/usr/bin"
Código:
*-------------------------------------------------------*
| shell[ /cualquierdireccion ]# mi_programa |
*-------------------------------------------------------*

Si desean saber las rutas a las que accede su variable de ambiente PATH, solo basta hacer esto:

Código:
*---------------------------------------------------------------*
| shell# echo $PATH |
| /usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin: |
| /usr/bin:/bin:/usr/X11R6/bin:/usr/games: |
| /opt/www/htdig/bin:/usr/lib/java/bin: |
| /usr/lib/java/jre/bin:/opt/kde/bin:/usr/lib/qt/bin: |
| /usr/share/texmf/bin |
*---------------------------------------------------------------*

Y de este modo ustedes desiden donde situar nuestro script o programa en C, para ocuparlo futuramente :D.



3.4 Representacion de Archivos en Unix

Unix maneja 2 tipo de archivos que son:

  • Archivos Ordinarios
  • Archivos Especiales
  • Archivos Especiales por Caracter
  • Archivos Especiales por Bloque
  • Archivos Especiales FIFO
[/list]

*Archivos Ordinario
Estos son archivos comunes realizados por el usuario, archivos de trabajo, documentos, etc...

*Archivos Especiales
Estos archivos representan dispositivos, pero existen 3 derivados que son:

*Archivos Especiales por Caracter
Estos archivos especiales representan dispositivos como terminales.

*Archivos Especiales por Bloque
Estos archivos representando dispositivos de disco.

*Archivos Especiales FIFO
Estos archivos se utilizan para la comunicacion entre procesos.

Unix representa y diferencia los archivos por medio de un inodo, un inodo es una estructura que contiene informacion acerca de un archivo, informacion como:

  • tamano
  • localizacion
  • propietario
  • fecha de creacion
  • permisos
  • fecha de modificacion

Dato importante :D, tomemos en cuenta que tanto archivos como directorios tienen con ellos asociado un inodo, mas adelante veremos sus diferencias y como los reconoce UNIX.

Para recuperar la informacion contenida en un inodo de un archivo, utilizaremos a sistema stat();

Código:
*---------------------------------------------------------------*
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| int stat( const char *path, struct stat *buf ); |
| int fstat( int fildes, struct stat *buf ); |
| int lstat( const char *path, struct stat *buf ); |
*---------------------------------------------------------------*

La Estructura "struct stat" contiene los mienbros siguientes:

Código:
*---------------------------------------------------------------------------------------*
| Nombre | Descripcion |
*---------------------------------------------------------------------------------------*
|  mode_t st_mode |  Modo del Archivo |
|  ino_t st_ino |  Numero de inodo |
|  dev_t st_dev |  ID del Dispositivo que contiene una entrada de directorio |
| |  para este archivo |
|  dev_t st_rdev |  ID del dispositivo, solo para archivos especiales de carac- |
| |  ter o de bloke |
|  nlink_t st_nlink |  Numero de Ligas |
|  uid_t st_uid |  ID de Usuario, del propietario del Archivo |
|  gid_t st_gid |  ID de Grupo, del Grupo del Archivo |
|  off_t st_size |  Tamano del Archivo en bytes |
|  time_t st_atime |  Fecha del Ultimo Acceso |
|  time_t st_mtime |  Fecha de la Ultima Modificacion |
|  time_t st_ctime |  Fecha del Ultimo Cambio al estado del Archivo, en segundos |
|  long st_blksize |  Tamano preferencial dle bloque de E/S |
|  long st_blocks |  Numero de Bloques. "st_blksize" asignado |
*---------------------------------------------------------------------------------------*

Código:
*---------------------------------------------------------------*
| int stat( const char *path, struct stat *buf ) |
*---------------------------------------------------------------*
Funcion para abrir directorio unicamente ( no archivos )

Código:
*---------------------------------------------------------------*
| int fstat( int fildes, struct stat *buf ) |
*---------------------------------------------------------------*
Funcion para abrir archivos y directorios.

Código:
*---------------------------------------------------------------*
| int lstat( const char *path, struct stat *buf ) |
*---------------------------------------------------------------*
Funcion para abrir ligas simbolicas, esta funcion NO es parte de POSIX

Codigo: ps_018.c
Programa que realiza la apertura de un directorio
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>

int main( int argc, char *argv[] )
{
struct stat buf;
system( "clear" );

if( argc != 2 )
{
fprintf( stderr, "Uso: %s nombre_de_archivo\n", argv[0] );
exit(1);
}

if( stat( argv[1], &buf ) == -1 )
{
fprintf( stderr, "No Se Puede Tomar el Archivo %s.\nError: %s\n", argv[1], strerror(errno) );
exit(1);
}

if( buf.st_mode & S_IFDIR )
{
printf( "\"%s\" es un Directorio, y sus descripciones son:", argv[1] );
printf( "\n\n>Numero de Inodo: %i", buf.st_ino );
printf( "\n>Numero de Ligas que contiene: %i", buf.st_nlink );
printf( "\n>ID del Propietario que lo Creo: %i", buf.st_uid );
printf( "\n>Numero de Blockes Asignado: %i", buf.st_blocks );
printf( "\n>Tamano del Archivo: %i bytes", buf.st_size );
printf( "\n>Ultima Modificacion: %s", ctime( &buf.st_mtime ) );
}
else printf( "%s No es un Directorio...", argv[1] );
getchar();
return 0;
}

*-----------------------------------------------------------------------*
| En Ejecucion |
*-----------------------------------------------------------------------*
| shell# ./ps_018 /home/dir_creado |
| "dir_creado" es un directorio, y sus descripciones son: |
| |
| >Numero de Inodo: 12669294 |
| >Numero de Ligas que contiene: 2 |
| >ID de propietario que lo creo: 0 |
| >Numero de Blockes Asignados: 2 |
| >Tamano del Archivo: 4096 bytes |
| >Ultima Modificacion: Sat Oct 1 12:48:54 2005 |
*-----------------------------------------------------------------------*

=== Explicacion ===
La forma de utilizacion es: "./pas_018 directorio".Segun el docido, "struct stat *buf" es una variable a la estructura stat para tomar sus distintos miembros ( segun la tablita ya vista ). En caso de errores para el primer condicional o segundo. En el segundo condicional "if( stat( arg[1], &buf ) == -1 )", si el nombre pasado como argumento NO existe entonces se cumple en este mismo condicional se le pasan dos parametros a stat(), que son: "nombre_del_directorio" y estructura que sera rellenada por medio de buf. en el Tercer condicional "if( buf.st_mode & S_IFDIR )" es solo para comprobar si el modo del archivo a abrir es un fichero o directorio, en este caso "S_IFDIR" comprueba que sea un directorio xD :P. En caso de exito pues muetra los printf()s :D, y el ultimo condicional, pues cuando el archivo pasado como argumento NO cumplo con ningun otro condicional, ya que es ovio que NO es un directorio, sino un fichero xD.

Si deseas hacer un ejemplo, pero para abrir ficheros, entonces modifica las lineas siguientes:

Código:
if( stst( argv[1], &buf ) == -1 )
|  por |
if( fstat( argc, &buf ) == -1 )

Y pues NO es necesari el:  if( buf.st_mode & S_IFDIR )

Ni tampoco necesitas el ultimo condicional "else()", ya que "fstat()" abre tanto ficheros como directorios :D.



3.5 Representacion de Directorios y Enlaces o Ligas

En Unix un directorio es un archivo que contiene una serie de nombres e inodos que se corresponden.

Un Enlace o Liga es una asociacion entre el nombre de un archivo y su inodo, UNIX tiene dos tipos de enlaces o ligas y son:

  • Ligas Duras.
  • -Ligas Simbolicas ( o suaves ).

Las Ligas Duras, estas ligan directamente lso nombres de los archivos con inodos.

Las Ligas simbolicas ( o suaves ) utilizan el archivo como un apuntador a otro nombre de archivo.

Con el comando "ln" o la llamada al sistema "link", los usuarios pueden crear ligas duras adicionales para archivos.



3.6 Representacion de Archivos con Identificadores

Dentro de la Programacion en C, los archivos estan designados por apuntadores de archivo o por descriptores de archivo.

Los Designados por Apuntadores de Archivo, utilizan la Biblioteca estandar de E/S para ANSI C, como son:

  • fopen
  • fclose
  • fscanf
  • fprintf
  • fread
  • fwrite

Y sus Identificadores para los Apuntadores de Archivo de la entrada, salida y el errr estandares son:

  • stdin
  • stdout
  • stderr

Y estan definidos en stdio.h

Los Designados por Descriptores de Archivo, utilizan la Biblioteca estandar de E/S de UNIX, somo son:

  • open
  • close
  • read
  • write
  • ioctl

Los Identificadores para los Descriptores de Archivo de la entrada, salida y error estandar son:

  • STDIN_FILENO
  • STDOUT_FILENO
  • STDERR_FILENO

Y estan definidos en unistd.h

Por lo tanto los Apuntadores y Descriptores proporcionan Nombres Logicos o Identificadores para llevar acabo la entrada y salida independiente del Dispositivo. Asi del mismo modo un fopen() o un open() proporcionan:

  • Una asociacion entre un archivo o dispositivo fisico y
  • El Identificador Logico utilizado en el Programa.

Y como es ovio, en Archivos AVANZADOS, se trata el uso de Descriptores de Archivos :D.

En open() el identificador es un indice en una tabla de descriptores de archivo.
En fopen() el identificado es un apuntador hacia una estructura de archivo.





3.7 Descriptores de Archivo

La llamada al sistema open(), asocia un descriptor de archivo con un archivo o dispositivo fisico.

Código:
*-------------------------------------------------------*
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| int open( cont char *path, int oflag, ... ); |
*-------------------------------------------------------*

const char *path
Es el archivo o dispositivo fisico al que le asociaremos un descriptor de archivo.

int oflag
Los valores POSIX.1 para oflag estan definidos en fcntl.h, e incluyen:

Código:
*-----------------------------------------------*
|     NOMBRE |    DESCRIPCION |
*-----------------------------------------------*
|  -O_RDONLY |  Solo Lectura |
|  -O_WRONLY |  Solo Escritura |
|  -O_RDWR |  Solo Lectura y/o Escritura |
|  -O_APPEND | |
|  -O_CREAT |  Crearlo |
|  -O_EXCL | |
|  -O_NOCTTY | |
|  -O_NONBLOCK | |
|  -O_TRUNC | |
*-----------------------------------------------*

Se tiene que especificar exactamente una de las banderas o tambien puede especificar varias banderas para obtener un efecto combinado, utilizando el OR ( | ).

...
Este tercer parametro extra, solo se peude activar cuando el campo "oflag" tiene o es O_CREAT, de estemodo se incluira este tercer parametro extra que es de tipo "mode_t" y es el que especifica los Permisos para el Archivo como un valor mode_t.

En Unix los Privilegios posibles son:

  • read (lectura), representado por una:  r
  • write (escritura), representado por una:  w
  • execution (ejecucion), representado por una:  x

Y estos permisos, son aplicados a estos tres tipos:
  • Usuario ( el propietario ).
  • Grupo ( un grupo, qu esta constituido por un grupo de usuarios ).
  • -Otros ( los dema usuarios que NO son miembros de algun grupo, o no son del sistema ).

Estos privilegios estan especificados por separado con la siguiente mascara que esta especificada en POSIX.1:

Código:
<----- Usuario -----><------ Grupo ------><------- Otros ------>
*------*------*------*------*------*------*------*------*------*
|  r   |  w   |  x   |  r   |   w  |  x   |  r   |  w   |  x   |
*------*------*------*------*------*------*------*------*------*

Estos nombres estan definidos en sys/stat.h y a coninuacion veremos una tabla de los nombres simbolicos y su significado:

Código:
*-----------------------------------------------------------------------------------------------*
|  NOMBRE   | DESCRIPCION |
*-----------------------------------------------------------------------------------------------*
|  S_IRUSR  |  bit de permiso de lectura para el usuario ( propietario ) |
|  S_IWUSR  |  bit de permiso de escritura para el usuario ( propietario ) |
|  S_IXUSR  |  bit de permiso de ejecucion para el usuario ( propietario ) |
|  S_IRWXU  |  bit de permiso de lectura, escritura y ejecucion para el usuario ( propietario ) |
*-----------------------------------------------------------------------------------------------*
|  S_IRGRP  |  bit de permiso de lectura para el grupo |
|  S_IWGRP  |  bit de permiso de escritura para el grupo |
|  S_IXGRP  |  bit de permiso de ejecucion para el grupo |
|  S_IRWXG  |  bit de permiso de lectura, escritura y ejecucion para el grupo |
*-----------------------------------------------------------------------------------------------*
|  S_IROTH  |  bit de permiso de lectura para otros |
|  S_IWOTH  |  bit de permiso de escritura para otros |
|  S_IXOTH  |  bit de permiso de ejecucion para otros |
|  S_IRWXO  |  bit de permiso de lectura, escritura y ejecucion para otros |
*-----------------------------------------------------------------------------------------------*
|  S_ISUID  |  fijar el ID del usuario al momento de la ejecucion |
|  S_IDGID  |  fijar el ID del grupo al momento de la ejecucion |
*-----------------------------------------------------------------------------------------------*

La llamada al sistema close();, realiza el cierre de un Descriptor de Fichero.
Código:
*---------------------------------------*
| #include <unistd.h> |
| |
| int close( fd ); |
*---------------------------------------*

Acontinuacion veremos un ejemplo sencillo, que ilustre open() y close().

Codigo: ps_019.c
Programa que ilustra el uso de OPEN() y CLOSE(). Si existe se abre, sino solo se crea
Código:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
int mifd;
mode_t modo= S_IRUSR | S_IRGRP | S_IROTH;

if( mifd= open( "/root/datos.dat", O_RDONLY | O_CREATE, modo ) )
printf( "Archivo Abierto con exito.." );
else printf( "Problmas para abrir el archivo..." );
close( mifd );
getchar();
return 0;
}

== Explicacion ==
Bueno pues no tiene mucha ciencia xD, simplemente open() tiene 3 parametros que son: ubicacion del archivo *.dat, modo en que se tratara el archivo ( Lectura Solamente y/o Se Creara ), y como incluimos el modo O_CREAT, por lo tanto se tiene que introducir un 3er parametro que define los privilegios que tendran los 3 tipos de usuarios para con el archivo, y por los cuales establecimos los modos de: Lecutra Para Usuario Propietario, Lectura para el Gurpo, Lectura para Otros Usuarios. En caso de error nos dira: "Problemas para abrir el archivo...". Se cierra el descriptor y finaliza el programa.



3.7.1 Lectura de Ficheros

La llamada a la funcion read(), devuelve la longitud de la cadena leida del descriptor en un valor entero, su sintaxis es la siguiente:

Código:
*---------------------------------------------------------------*
| #include <unistd.h> |
| |
| size_t read( int fd, void *buffer, size_t buffer ); |
*---------------------------------------------------------------*

  • int fd
  • es el descriptor de archivo

  • void *buffer
  • es un puntero al buffer donde contendremos la info leida del descriptor

  • size_t buffer
  • es el tamano del buffer

Veremos un codigo......

Codigo: ps_020.c
Programa que ilustra el uso de read()
Código:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define N 100 /*tamano de nuestro buffer*/

/*Prototipos*/
void buf_destripado( char *buf );

void buf_destripado( char *buf )
{
while( *buf!='\0' )
{
if( *buf=='\n' )
{
printf( "\\n" );
printf( "\n" );
}
else printf( "%c", *buf );
*buf++;
}
}

int main()
{
int mifd, resaca;
char buf[N];

system( "clear" );
if( !(mifd=open( "/home/user/dato.dat", O_RDONLY ) ) )
{
printf( "Problemas para leer el fichero..." );
close( mifd );
getchar();
exit(1);
}
printf( "\n\nLeyendo Archivo...." );
printf( "\nTamano del Buffer: %i bytes.", sizeof( buf ) );
dato= read( mifd, &buf, N );
printf( "\nContenido:\n-------------------\n" );
buf[dato]='\0'; /*quito el resaque, acorto buffer y dejo el mensaje limpio*/
printf( "%s", buf );
printf( "\n-------------------\n\nBuffer Ocupado: %i bytes", dato );
printf( "\n\nDestripado del Buffer..." );
printf( "\n------------------\n" );
buf_destripado( buf );
printf( "\n------------------\n" );
close( mifd );
printf( "\n\nFin del Programa....\n" );
getchar();
return 0;
}

*-----------------------------------------------*
| En Ejecucion |
*-----------------------------------------------*
| shell#./ps_020 |
| |
| Leyendo Archivo.... |
| Tamano del Buffer: 100 bytes. |
| Contenido: |
| ------------------- |
| Hola Angel |
| Que Onda, esto va bien |
| ------------------- |
| |
| Buffer Ocupado: 33 bytes |
| |
| Destripado del Buffer... |
| ------------------ |
| Hola Angel\n |
| Que Onda, esto va bien |
| ------------------ |
| |
| Fin del Programa.... |
*-----------------------------------------------*

== Explicacion ==
Se realiza la apertura del archivo en modo de Solo Lectura, se realiza la llamada a read() la cual consigo lleva 3 parametros que son: el Descriptor, el Buffer, y el Tamano del Buffer que tenemos para guardar el archivo, recordemos que el archivo a leer NO debe sobrepasar el tamano que le destinamos al buffer ( el 3er parametro ), sino solo leera hasta el Tamano Especificado, del mismo modo read() retorna el numero de bytes leidos ( pero lee hasta maximo el 3er parametro establecido ). Despues Realizo 2 operaciones que son: Imprimir el Contenido del Archivo y Mostrar el Salto o Saltos que tiene el fichero, y se cierra el fichero.

La llamada a la funcion write() escribe al descriptor abierto con permiso de escritura y retorna el numero de bytes escritos en el fichero, del buffer donde tenemos contenidos los datos a escribir con write(). Su sintaxis es:

Código:
*---------------------------------------------------------------*
| #include <unistd.h> |
| |
| size_t write( int fd, void *buf, size_t buf ); |
*---------------------------------------------------------------*

Para una mejor comprencion veremos un par de ejemplos.

Codigo: ps_021
Programa que permite leer y escribir en un fichero
Código:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define N 1024

int main()
{
char buf[N], buf2[N];
int bytesenv, bytesrecv, fd_in;
mode_t modo= S_IRWXU | S_IRGRP | S_IROTH;

system( "clear" );
if( !(fd_in=open( "/root/datos.dat", O_RDWR | O_CREAT, modo ) ) )
{
printf( "Problemas para Abrir Archivo.\nError: %s", strerror( errno ) );
getchar();
exit(1);
}
printf( "Estado del Buffer: %i bytes", sizeof( buf ) );
printf( "\nContenido del Fichero:\n---------------\n" );
bytesrecv= read( fd_in, &buf, N ); /*read() retornara el numero de bytes leidos*/
buf[bytesrecv]='\0';
printf( "%s", buf );
printf( "\n---------------\n" );
printf( "Buffer Utilizado Para Lectura: %i bytes", sizeof( bytesrecv ) );
printf( "\nBuffer de Lectura: %i bytes", strlen( buf ) );
printf( "\n\nTexto a Introducir: " );
fgets( buf2, N, stdin );
buf2[strlen(buf2)+1]='\n';
buf2[strlen(buf2)+1]='\0';
printf( "\n\nEstado de Buffer 2: %i bytes", sizeof( buf2 ) );
printf( "\nBuffer Utilizado para Escribir: %i bytes", strlen( buf2 ) );
bytesenv= write( fd_in, &buf2, strlen( buf2 ) ); /*write() retornara el numero de bytes escritor*/
printf( "\nSe Escribieron %i bytes con exito.", sizeof( bytesenv ) );
printf( "\n\nFin del Programa..." );
close( fd_in );
getchar();
return 0;
}

*-------------------------------------------------------*
| En Ejecucion |
*-------------------------------------------------------*
| shell# ./ps_021 |
| Estado del Buffer: 1024 bytes |
| Contenido del Fichero: |
| --------------- |
| hola como estas |
| |
| --------------- |
| Buffer Utilizado Para Lectura: 4 bytes |
| Buffer de Lectura: 16 bytes |
| |
| Texto a Introducir: muy bien |
| |
| |
| Estado de Buffer 2: 1024 bytes |
| Buffer Utilizado para Escribir: 9 bytes |
| Se Escribieron 4 bytes con exito. |
| |
| Fin del Programa... |
*-------------------------------------------------------*

== Explicacion ==
Usaremos 2 buffers, BUF es donde almacenaremos los bytes del archivo y BUF2 es donde guardaremos los datos a escribir.
Primeramente realizamos la apertura dle fichero en modo de Lectura y Escritura ( O_RDWR ) y en el caso que NO exista se creara con permisos de Lectura, Escritura y Ejecucion para Usuarios y de Lectura para el Grupos y Otros, se lee el fichero y se reciven los bytes leidos en "bytesrecv", despues se imprime el los bytes leidos.

NOTA: la sintaxis: "buf[bytesrecv]='\0';" inserta el Fin de Linea a los datos del archivo, asi evitamos se impriman datos o residuos indeseados de la memoria.

Despues se pide la cadena nueva a introducir al fichero y, pues cabe mencionar que NO usamos: gets( buf2 ); ya que no es recomendable, usaremos fgets( char *buf, int buf_size, FILE *stream ), en "stream" incluimos la entrada estandart (stdin).
En seguida le realizamos una modificacion a nuestra cadena a introducir, le agregaremos un SALTO DE LINEA AL FINAL y el Fin de Linea :D, asi al escribirse en el fichero nos brincaremos al proximo renglon con el fin de que en un futuro que se vulva a realizar la misma u otra operacion podamos interactuar con el siguiente renglon :D y no tener que hacer llamdas especiales para agregar un salto de linea (\n) al fichero ;). Y por ultimo escribirmos los datos y la funcion write() nos retornara los bytes escritos ( bytesenv ).

Ahora como veremos otro ejemplo bueno xD, este nos ensena la forma de abrir dos descriptores y leer un archivo y copiarlo a otra posicion... seraya con COPY & PASTE con descriptores :D.

Codigo: ps_022.c

Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

#define N 1048576
/*
buffer de 1MB( 1024kb -->1048576bytes )
Solo recuerda que se escrube en bytes ( asi como esta en el #define :D )

--------- Tabla de Dimensiones en Ficheros-------------
1024bytes == 1KB
1024kb == 1MB
1024mb == 1GB
1024gb == 1tb  etc.....
*/

int main( int argc, char *argv[] )
{
int fd_in, fd_out, bytesenv, bytesrecv;
mode_t modo= S_IRWXU | S_IRGRP | S_IROTH;
char buf[N];

system( "clear" );
if( argc!=3 )
{
fprintf( stderr, "Uso: %s <archivo_origen> <archivo_destino>\n", argv[0] );
exit(1);
}
if( !( fd_in=open( argv[1], O_RDONLY ) ) )
{
fprintf( stderr, "No Se Puedo abrir el archivo \"%s\"\nError: %s", argv[1], strerror( errno ) );
exit(1);
}
if( !( fd_out=open( argv[2], O_WRONLY | O_CREAT | O_EXCL, modo ) ) )
{
fprintf( stderr, "\nNo Se Puede crear el fichero \"%s\"\nError: %s", argv[2], strerror( errno ) );
exit(1);
}
bytesrecv= read( fd_in, &buf, N );
printf( "\n---Tabla de Peso en Ficheros ---\n" );
printf( "1KB --> 1024 bytes\n" );
printf( "1024KB --> 1MB" );
printf( "\n\nEl Fichero \"%s\" Pesa: %.2fkb ( %i bytes )", argv[1], ((float)bytesrecv/1024), bytesrecv );
printf( "\nA Continuacion se copara el fichero la ubicacion deseada...." );
bytesenv= write( fd_out, &buf, bytesrecv );
printf( "\n\nSe Creo una copia del fichero \"%s\" de %.2fkb...", argv[2], ((float)bytesrecv/1024) );
printf( "\n\nFin del Programa....." );
close( fd_in );
close( fd_out );
getchar();
return 0;
}

*-----------------------------------------------------------------------------------------------*
| En Ejecucion |
*-----------------------------------------------------------------------------------------------*
| shell# ./ps_022 /home/miarchivo.tar /home/dir/miarchivo.tar |
| |
| ---Tabla de Peso en Ficheros --- |
| 1KB --> 1024 bytes |
| 1024KB --> 1MB |
| |
| El Fichero "/home/miarchivo.tar" Pesa: 64.90kb ( 66457 bytes ) |
| A Continuacion se copiara el fichero la ubicacion deseada.... |
| |
| Se Creo una copia del fichero "/home/dir/miarchivo.tar" de 64.90kb... |
| |
| Fin del Programa..... |
*-----------------------------------------------------------------------------------------------*

== Explicacion ==
Creo que no es tan necesaria, pero bueno :D.... :P

Pues como ya sabemos el archivo a leer es pasado como Argumento 1 y el archivo de Escritura Destino es pasado como Argumento 2, en base a esto se lee el Argumento 1 y la funcion "read()" al leer retorna el peso del archivo expresado en un valor de tipo ENTERO y que viene siendo en bytes :D. Lo mismo "write()", retorna el peso del archivo Escrito expresado en un valor de tipo ENTERO y que viene siendo en bytes :P. Ahora, al momento de leer ( read() ), el peso lo guardamos en "bytesrecv" y el contenido en "buf", ahora al momento de escribir ( write() ) como 2do parametro ponemos lo datos ( buf ) y el peso de los datos o bien el tamano del fichero ( bytesrecv ) y write() nos retornara un valor que contendra "bytesenv", con este valor podemos asegurar si la infomacion se escribio correctamente :D ( ya que debe retornar el mismo valor que contiene "bytesrecv" ;) ). Y al final ya tenemos nuestro archivo copiado en otro directorio. o_O

Veremos otro codigo que ilustra la forma de realizar: lectura, escritura e insercion de texto en el fichero, pero en esta ocacion solo utilizaremos 2 argumentos, el de la OPCION y el FICHERO de entrada :D.

Codigo: ps_023.c
Programa que realiza copia de ficheros con desciptores
Código:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#define N 1024 /*Buffer de 1MB*/

int main( int argc, char *argv[] )
{
char buf[N], buf2[N]; //buffers
int fd, fd_out, bytesrecv, bytesenv; //descriptor y 2 varables para saber los bytes recividos y enviados

mode_t modo= S_IRWXU | S_IRGRP | S_IROTH;

system( "clear" );
if( argc!=3 )
{
printf( "\nUso: %s <opcion> <path/archivo>\n\n", argv[0] );
printf( "\n[OPCION]\n" );
printf( "-l\tLeer Fichero del Path." );
printf( "\n-c\tCopiar Fichero del <Path1> al <Path2>." );
printf( "\n-r\tEliminar Fichero Path." );
printf( "\n-e\tEscribir en el Fichero.\n\n" );
exit(1);
}

if( !(fd= open( argv[2], O_RDWR | O_CREAT, modo )) )
{
fprintf( stderr, "Problemas para abrir el fichero: %s", strerror(errno) );
exit(1);
}

if( !strcmp(argv[1], "-l") )
{
bytesrecv= read( fd, &buf, N );
buf[bytesrecv]='\0';
printf( "Fichero: %s", argv[2] );
printf( "\nTamano del Fichero: %.2fMB(%i bytes)", ((float)bytesrecv/1024), bytesrecv );
printf( "\nContenido\n" );
printf( "******************************\n" );
printf( "%s", buf );
printf( "******************************\n" );
}
if( !strcmp(argv[1], "-r") )
{
remove( argv[2] );
printf( "Fichero \"%s\" Eliminado.", argv[2] );
}
if( !strcmp(argv[1], "-e") )
{
bytesrecv= read( fd, &buf, N );
buf[bytesrecv]='\0';
printf( "\n----------------------- BUFFERS -----------------------\n" );
printf( "Buffer de Lectura Disponible: %.2fMB(%i bytes)", ((float)N/1024), N );
printf( "\nBuffer del Fichero Leido: %.2fMB(%i bytes)", ((float)bytesrecv/1024), bytesrecv );
printf( "\n--------------------------------------------------------\n" );
printf( "\n\nContenido:\n" );
printf( "******************************\n" );
printf( "%s", buf );
printf( "******************************\n" );
printf( "\n\n--------------- Datos a Insertar -----------------\n\n" );
fgets( buf2, N, stdin );
buf2[strlen(buf2)+1]='\n';
buf2[strlen(buf2)+1]='\0';
bytesenv= write( fd, &buf2, strlen(buf2) );
printf( "\n----------------------- BUFFERS -----------------------\n" );
printf( "Buffer de Escritura Disponible: %.2fMB(%i bytes)", ((float)N/1024), N );
printf( "\nBuffer del Fichero Escrito: %.2fMB(%i bytes)", ((float)bytesenv/1024), bytesenv );
printf( "\n--------------------------------------------------------\n" );
}
if( !strcmp( argv[1], "-c") )
{
if( !(bytesrecv= read( fd, &buf, N )) )
{
fprintf( stderr, "\nProblemas para Leer el Fichero: %s\n", strerror(errno) );
exit(1);
}
printf( "\n----------------------- BUFFERS -----------------------\n" );
printf( "Buffer de Lectura Disponible: %.2fMB(%i bytes)", ((float)N/1024), N );
printf( "\nBuffer del Fichero Leido: %.2fMB(%i bytes)", ((float)bytesrecv/1024), bytesrecv );
printf( "\n--------------------------------------------------------\n" );

printf( "Archivo \"%s\", sera copiado con el nombre de?: ", argv[2] );
fgets( buf2, N, stdin );
buf2[strlen(buf2)-1]='\0'; //elimina el salto de linea del buffer :D '\n', sino se guardaria: "nombre\n"

if( !(fd_out= open( buf2, O_CREAT | O_RDWR , modo )) )
{
fprintf( stderr, "\nProblema para crear el archivo: %s: %s", buf2, strerror(errno) );
exit(1);
}
if( !(bytesenv= write( fd_out, &buf, bytesrecv )) )
{
fprintf( stderr, "\nProblemas para Escribir el Archivo: %s\n", strerror(errno) );
exit(1);
}
printf( "Se Escribieron: %.2fMB(%i bytes)", ((float)bytesenv/1024), bytesenv );
close(fd_out);
}
close( fd );
getchar();
return 0;
}

== Explicacion ==
Pues realiza casi lo mismo que el codigo anterior, solo que este utiliza argumentos :D, y raliza:

  • Lectura ( solo lee y muestra informacion/contenido del archivo
  • Copia el fichero en otro con el nombre que quieras :D
  • Elimina un fichero
  • Escribe datos en un fichero existente ( vaya, Anida )

Lo unico relevante e interesante es que observen bien la forma en que se le pasa el NOMBRE del fichero a crear, al segundo Descriptor de Fichero ( fd_out ), lo que se realiza ANTES de la llamada a dicho OPEN(), de ese modo NO cometamos el error de guardar el nombre mal ( creyendo nosotros que hiria bien ;) ). Por ultimo solo ver como se utiliza READ(), WRITE() y REMOVE().


Código:
*--------------------------------------------------------------*
| #include <sys/ioctl.h> |
| |
| int ioctl( int fd, int request, /* args */ ); |
*--------------------------------------------------------------*

La funcion ioctl() nos proporciona un medio para obtener informacion sobre el estado de un dispositivo o para establecer opciones de control para el mismo.

En caso de exito "ioctl()" retorna un 0 y en caso de error un -1 y setea a "errno" con un valor de los siguientes:

Código:
*-----------------------------------------------------------------------------------------------*
|  Nombre  | Descripcion |
*-----------------------------------------------------------------------------------------------*
|  EBADF   |  fd no es un descriptor valido. |
|  EFAULT  |  /*args*/ preferencia a una zona inaccesible. |
|  ENOTTY |  fd no esta asociado con un dispositivo especial de caracteres. |
|  ENOTTY |  La peticion especificada no se aplica a la clase de objeto que |
| |  referencia el descriptor fd. |
|  EINVAL   |  Peticion /*args*/ no es valido. |
*-----------------------------------------------------------------------------------------------*

Otras funciones interesantes a ioctl() son:
Código:
*-----------------------------------------------------------------------------------------------*
| #include <sys/io.h> /*para glibc*/ |
| #include <unistd.h> /*para libc5*/ |
| |
| int ioperm( unsigned long desde, unsigned long num, int encender ); |
*-----------------------------------------------------------------------------------------------*

La funcion ioperm() establece el bit de permiso de acceso a los Puertos para el proceso para "num" bytes, empezando desde la direccion del puerto "desde", al valor "encender". Su utilizacion requiere permisos de "root".
Devuelve 0 en caso de exito y un -1 en caso de error, y setea a "errno" con un valor.

Solo se pueden especificar los primeor puertos 0x3fff puertos de E/S, para utilizar mas puertos se debe utilizar iopl(). Dato importante tambien es que los permisos se Hereda en un fork(), pero si estan en un exec().

Código:
*--------------------------------------------------*
| #include <sys/io.h> /*para glibc*/ |
| #include <unistd.h> /*para libc5*/ |
| |
| int iopl( int nivel ) |
*--------------------------------------------------*

La funcion iopl() cambia el nivel del privilegio de E/S del Proceso en Curso, La funcion iopl() tiene que emplearse cuando se desea acceder a los puertos que estan mas haya del rango 0x3ff. Ademas los permisos son heredados por fork() y exec().

La funcion iopl() devuelve 0 en caso de exito y devuelve -1 en caso de error, y setea a "errno" con un valor apopiado:

Código:
*---------------------------------------------------------------------------*
|  Nombre  | Descripcion |
*---------------------------------------------------------------------------*
|  EINVAL   |  nivel es mayor que 3 |
|  EPERM    |  el usuario no es el super-usuario(root) |
*---------------------------------------------------------------------------*



3.7.2 Entradas y Salidas sin Bloqueo

Antes de empezar con el temita :D. Quiero recalcar que es muy importante poner atencion y entender bien este tema ya que los siguientes temas se basaran mucho en este, por la razon de que empezaremosa  ver codigos donde realizaremos aperturas de descriptores de archivos desde nuestros Procesos :D. Asi que debemos dejar claras algunas cosillas en este tema :D.

Cuando un procesos intenta leer de un bufferm este lo bloquea, hasta esperar a que la entreda de lectura este disponible para leer, osea, esperara en el "read()". Dicho de otro modo seria que, imaginemos que tenemos 2 Procesos, ene ste caso si intentamos leer un fichero ( abierto previamente ), dependiendo quien lo haga primero, uno de los dos Procesos tiene que esperar a que el Proceso que esta leyendo, termine, y asi continua a realizar la lectura el Procesos que sigue, asi continuamente deben ir Esperando a que se desocupe la entrada para poder realizar la lectura ( read() ).

El Bloque de E/S presneta un problema para un proceso, ya que peude darse el caso que un proceso este vigilando varias entradas para realizar una lectura. El Proceso no tiene la oportunidad de saber que Entrada es la que llega primero, ya que si al intentar leer de un buffer resulta que este esta ocupado, puede darse el caso en que el Proceso se vea Congelado Indefinidamente.

En este punto entra el Emple de E/S sin Bloqueo, que nos ayuda a resolver el Problema de que un Procesos espera para poder Leer, o bien evitar congelamientos Indeifinidos. Para resolver este problema solo es cuestion de configurar las Banderas de Control apropiadas, de esta forma el read() puede intentar realizar una lectura, y si se esta ocupada la entrada, el read() no se vera en la necesidad de bloquear ( esperar o congelarse ), solamente regresara de inmediato ya que NO se encontro disponible la entrada :D.

Para llevar acabo la operacion de E/S sin Bloqueo se debe utilizar la bandera O_NONBLOCK asociada con el Descriptor de Archivo. Del mismo modo la llamada a fcntl() realiza modificaciones a las Banderas al estar ya asociadas con un objeto que tiene un Descriptor de Archivo. ;)

Código:
*-----------------------------------------------------------------------*
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| int fcntl( int fd, int comando, long arg|struct flock *lock ); |
*-----------------------------------------------------------------------*

La funcion fcntl() realiza mucha soperaciones sobre los Descriptores de Archivos dependiendo el "comando" que se le introduzca :D ( pero recordemos que nosotros solo modificaremos banderas, para evitar Bloqueos :D ).

    int fd
    [li]Es el Descriptor de Fichero asociado.[/li]

    int comando
    [li]Es el comando a introducir para realizar una operacion sobre el Descriptor de Archivo, sus comandos son:[/li]

    Código:
    *------------*----------------------------------------------------------------------------------*
    |  COMANDO   | DESCRIPCION |
    *------------*----------------------------------------------------------------------------------*
    |  F_DUPFD   |  Busca el descriptor de fichero disponible de menor, mayor o igual que "arg" |
    |      |  y lo convierte en una copia de "fd", al crearse los fd's, estos no comparten la |
    |      |  bandera close-on-exec (cerrar-la-ejecutar), en la copia estara desactivada. |
    |      |  En caso de exito se retorna el nuevo descriptor(fd). |
    |      | |
    |  F_GETFD   |  Lee la bandera close-on-exec. Si el bit FD_CLOEXEC es 0, el fichero permanece |
    |      |  abierto durante exec, en caso contrario se cerrara. |
    |      | |
    |  F_SETFD   |  Asigna el valor de la bandera close-on-exec, al valor especificado por el bit |
    |      |  FD_CLOEXEC de arg. |
    |      | |
    |  F_GETFL   |  Lee las Banderas del Descriptor de Archivo ( todas, segun hayan sido puestas |
    |      |  open() ), |
    |      | |
    |  F_SETFL   |  Asignas las Banderas del Descriptor al Valor asignado en arg. Estas pueden ser |
    |      |  O_APPEND, O_NONBLOCK o O_ASYNC. Recordar que las banderas ya puestas por open() |
    |      |  no se veran afectadas :D. |
    |      | |
    |  F_GETLK   |  Devuelve la estructura flock que nos impide obtener el candado o establece el |
    |      |  campo l_type del candado F_UNLCK sino hay obstruccion. |
    |      | |
    |  F_SETLK   |  El candado esta cerrado( cuando l_type es F_RDLCK o F_WRLCK ), o estara abierto |
    |      |  ( cuando l_type es F_UNLCK ). Si el candado esta siendo utilizado por alguien |
    |      | mas entonces devolvera un -1 y setea a errno con el codigo: EACCES, EAGAIN. |
    |      | |
    |  F_SETLKW  | Es como F_SETLK solo que en ves de devolver un error, este esperara hasta que |
    |      | el candado se abra.Si se recive una senal mientras fcnt() esta funcionando |
    |      | este se interrumpe. |
    |      | |
    |  F_GETOWN  | Obtiene el Id del Proceso o el Grupo de Procesos que actualmente esta |
    |      | reciviendo las senales de SIGIO y SIGURG para los eventos sobre el Descriptor. |
    |      | |
    |  F_SETOWN  | Establece el ID de Proceso o el Grupo de Procesos que estaran reciviendo las |
    |      | senales de SIGIO y SIGURG para los eventos sobre el Descriptor. |
    |      | |
    |  F_GETSIG  | Obtiene la senal cuando la entrada o la salida son posibles. Un valor 0 |
    |      | significa que se envia SIGIO, cualquier otro valor es la senal enviada en su |
    |      | lugar y se dispone del manejador de senales si se instala con SA_SIGINFO. |
    |      | |
    |  F_SETSIG  | Establece la senal enviada cuando la entrada o la salida son posibles. Para |
    |      | lso valores, pues igual como dije en F_GETSIG xD. |
    *------------*----------------------------------------------------------------------------------*

    long arg|struct flock *lock
    [li]Es el valor a enviar, puede ser del tipo ARGUMENTO o ESTRUCTURA, dependiendo la funcion a realizar :D. Mas informacio en la tablita de arriba ;).[/li][/list]

    La funcion fcntl() en caso de exito retornara un valor que cuyo exito dependera del COMANDO que se utiliza y el valor que se espera, ejemplo:

      F_DUPFD
      [li]El valor del nuevo Descriptor.[/li]

      F_GETFD
      [li]El valor de la Bandera.[/li]

      F_GETFL
      [li]El valor de la Bandera.[/li]

      F_GETOWN
      [li]El valor del Propietario del Descriptor.[/li]

      F_GETSIG
      [li]Valor de la Senal Enviada, cuando la lectura o la escritura son posibles, o 0 para el comportamiento tradicional de SIGIO.[/li]

      CUALQUIER OTRA ORDEN
      [li]Cero[/li][/list]

      Y en caso de Error se devolvera un -1 y setara a errno con un valor:
      « Última modificación: 13 Diciembre 2005, 22:27 por Diabliyo » En línea

      Diabliyo


      Desconectado Desconectado

      Mensajes: 1.427


      M.S.I Angel Cantu


      Ver Perfil WWW
      3. ARCHIVOS AVANZADOS(2da Parte)
      « Respuesta #5 en: 13 Diciembre 2005, 22:28 »

      Código:
      *---------------------------------------------------------------------------------------*
      | Valores con que se seteara a ERRNO |
      *-----------*---------------------------------------------------------------------------*
      |   VALOR   | DESCRIPCION |
      *-----------*---------------------------------------------------------------------------*
      |  EACCES   |  La operacion esta prohibida por candados mantenidos por otros procesos |
      |     | |
      |  EAGAIN   |  La operacion esta prohibida porque el fichero ha sido asociado a memoria |
      |     |  por otro proceso. |
      |     | |
      |  EDEADLK  |  Se ha detectado que el comando F_SETLKW provocaria un interbloqueo. |
      |     | |
      |  EFAULT   |  lock esta fuera de su espacio de direcciones accesible. |
      |     | |
      |  EBADF    |  fd no es un Descriptor de Ficheor Abierto. |
      |     | |
      |  EINTR    |  El comando F_SETLKW ha sido interrumpido por una senal. |
      |     | |
      |  EINVAL   |  Para F_DUPFD, el valor arg es negativo o mayor que el valor maximo |
      |     |  permitido. Para F_SETSIG , arg no es un numero de senal permitido. |
      |     | |
      |  EMFILE   |  Para F_DUPFD, el proceso ya ha llegado al numero maximo de descriptores |
      |     | |
      |  ENOLCK   |  Demaciados candados de segmentos abiertos, la tabla de candados esta |
      |     |  llena o ha fallado un protocolo de candados remoto. |
      |     | |
      |  EPERM    |  Se ha intentado limpiar la bandera O_APPEND sobre un fichero que tiene |
      |     |  el atributo "solo anadir". |
      *-----------*---------------------------------------------------------------------------*

      Veamos un pequeno codigo :D.

      Codigo: ps_024.c
      Programa que agrega banderas O_NONBLOCK para realizar E/S sin bloqueo
      Código:
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <fcntl.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <errno.h>
      #include <string.h>

      /*
      BANDERA VALOR
      O_WRONLY   1
      O_RDONLY   2
      O_RDWR   2
      O_NONBLOCK 2048
      */

      int main()
      {
      int fd, banderas;

      system( "clear" );
      if( !(fd= open( "test.txt", O_RDWR )) )
      {
      fprintf( stderr, "\nProblemas para Abrir el Fichero: %s\n", strerror(errno) );
      exit(1);
      }
      printf( "\nSeteando Banderas a \"O_NONBLOCK\".....\n" );

      if( !(banderas= fcntl( fd, F_GETFL, 0 )) ) //Aqui tomamos las banderas y las contendremos en "banderas"
      {
      fprintf( stderr, "\nProblemas para Tomar Banderas: %s\n", strerror(errno) );
      exit(1);
      }

      printf( "\nValor BANDERAS: %i\n", banderas ); //veamos el valor de las banderas tomadas

      banderas |= O_NONBLOCK; //se realiza un OR bit a bit y anida O_NONBLOCK en las banderas

      printf( "\nValor Banderas con O_NONBLOCK: %i\n", banderas ); //veamos el nuevo valor ;)

      if( fcntl( fd, F_SETFL, banderas )==-1 ) //agregamos las banderas de nuevo, pero ya con O_NONBLOCK Activado ;)
      {
      fprintf( stderr, "\nProblemas para Agregar Banderas Modeadas: %s\n", strerror(errno) );
      exit(1);
      }
      else printf( "\n\nSe Instalo la Banderas O_NONBLOCK para realizar E/S Sin Bloqueo :D.\n" );

      printf( "\nQuitando Banderas...\n" );
      if( !(banderas=fcntl( fd, F_GETFL, 0 )) ) //tomando Banderas de Nuevo
      {
      fprintf( stderr, "\nProblemas para tomar Banderas BLOQUEADAS: %s\n", strerror(errno) );
      exit(1);
      }

      printf( "\nValor de Banderas Bloqueadas: %i\n", banderas );

      banderas &= ~O_NONBLOCK; //quitamos banderas realizando un AND bit a bit

      printf( "\nValor de Banderas Limpiadas: %i\n", banderas );

      if( fcntl( fd, F_SETFL, banderas )==-1 )
      {
      fprintf( stderr, "\nProblemas para Agregar Banderas Limpiadas: %s\n", strerror(errno) );
      exit(1);
      }
      printf( "\nSe han Limpiado las Banderas y Quedado como al inicio :D.\n" );
      close(fd);
      getchar();
      return 0;
      }

      *-------------------------------------------------------------------------------*
      | En Ejecucion |
      *-------------------------------------------------------------------------------*
      | shell# ./ps_024 |
      | Seteando Banderas a "O_NONBLOCK"..... |
      | |
      | Valor BANDERAS: 2 |
      | |
      | Valor Banderas con O_NONBLOCK: 2050 |
      | |
      | Se Instalo la Banderas O_NONBLOCK para realizar E/S Sin Bloqueo :D. |
      | |
      | Quitando Banderas... |
      | |
      | Valor de Banderas Bloqueadas: 2050 |
      | |
      | Valor de Banderas Limpiadas: 2 |
      | |
      | Se han Limpiado las Banderas y Quedado como al inicio :D. |
      *-------------------------------------------------------------------------------*

      == Explicacion ==
      Una ves realizada la apertura del archivo y obtenido el Descriptor de Fichero (fd), cabe resaltar que en open() usamos 2 banderas, expresadas en una sola, osea O_RDWR es 1 bandera, pero son 2 en realidad: Lectura(1) y Escritura(2) (espero y se entienda :D), de este modo ahora procedemos a capturar las banderas en la variable "banderas" utilizando la funcion "fcntl()" con el comando F_GETFL, y como resultado, la funcion "fcntl()" retorna un 2, que significan las 2 banderas, despues a continuacion vemos la sintaxis que usaremos para realizar un OR bit a bit a las banderas para anidarle el O_NONBLOCK, que es:

      Código:
      banderas |= O_NONBLOCK;

      Ahora volvemos a checar el valor y nos retorna 2050 y pues solo es cuestion de observar la mini-tablita de valores que incluyo en el codigo como comentario entre el "int main()" y las "#include <>". Enseguida anidamos las banderas al Descriptor utilizando nuevamente a fcnt() y enviando 3 parametros que son: fd(descriptor), F_SETFL(comando), banderas(variable con O_NONBLOCK anidado), listo :D.

      Ahora procederemos a limpiar las banderas ;), volvemos a tomar las banderas utilizando a fcntl() con el comando F_GETFL, despues utilizaremos esta siguiente snetencia para eliminar la banderas O_NONBLOCK, haremos un AND:

      Código:
      baneras &= ~O_NONBLOCK;

      Ahora agregamos la modificacion llamando nuevamente a fcntl() con el comando F_SETFL y el argumento ( banderas ).

      Ya con esto podremos activar la E/S sin Bloqueo, de esta manera evitaremos que nuestros procesos ( o bien varios programas ) se congelen, o se esten ahi detenidos esperando a que el descriptor ( ya sea que esta asociado a un fichero o dispositivo ) se desocupe :d, nuestro Programa o Proceso regresara de inmediato del READ xD.





      3.7.3 Herencia de Descriptores de Archivo

      Este tema es cortito, pero aqui aprenderemos un poco de la Herencia de Ficheros conforme a los Procesos Padre e Hijos ;).

      Como ya aprendimos, fork() crear un proceso hijo que hereda muchas cualidades del Proceso Padres, dichas cualidades pueden ser el ambiente, el contexto del proceso padre, los estados de las senales, la tabla de Descriptores de Archivos, etc...  El punto importante aqui es La Tabla de Descriptores de Archivos. Una explicacion breve y logica es que la Tabla de Descriptores de Archivos es un conjunto de registros donde se contienen los Sistemas de Archivos en donde cada registro es apuntado por cada uno de los registros de un Proceso ( posilemente sea complicados, pero mas abajo veran un ejemplo dibujado :P ). Esta tabla es heredada a los Procesos Hijos, pero aqui entra algo curioso, La Direccion a donde apuntan :D, puede que el Padre y el Hijo Apunten a la Misma direccion en la Tabla de Descriptores de Archivo, pero pude que no :d, oviamente para esto ahi una explicacion :D, la cual es simple, basica y sencilla, veamos un pequeno codigo.

      Codigo: ps_025.c
      Programa que Hereda un Descriptor de Archivo al Proceso Hijo
      Código:
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <unistd.h>
      #include <string.h>
      #include <errno.h>

      int main()
      {
      pid_t proceso;
      char buf[1024];
      int fd, estado;

      system( "clear" );
      if( !(fd=open( "test.txt", O_RDWR )) ) //abrimos el archivo
      {
      fprintf( stderr, "\nProblemas para Abrir el Fichero: %s\n", strerror(errno) );
      exit(1);
      }
      if( (proceso= fork()) == -1 )
      {
      fprintf( stderr, "\nProblema para crear el Proceso: %s\n", strerror(errno) );
      exit(1);
      }
      else if( proceso==0 )
      {
      read( fd, &buf, sizeof(buf) ); //leemos el archivo
      printf( "\nHola Soy el Proceso Hijo ID: %d\n", (long)getpid() );
      printf( "**********************************\n" );
      printf( "%s", buf ); //imprimimos contenido
      printf( "\n**********************************\n\n" );
      printf( "\nPulsa para salir del proceso hijo..." );
      getchar();
      }
      else
      {
      while( wait(&estado)!=proceso ); //esperamos al proceso hijo para poder continuar
      printf( "\nHola Soy el Proceso Padre ID: %d\n", (long)getpid() );
      sleep(3); //esperamos 3 segundos
      }
      close(fd); //cerramos el descriptor
      exit(0); //salimos
      }

      Continuara... ;)
      « Última modificación: 14 Diciembre 2005, 01:50 por Diabliyo » En línea

      Páginas: [1] Ir Arriba Respuesta Imprimir 

      Ir a:  

      Mensajes similares
      Asunto Iniciado por Respuestas Vistas Último mensaje
      Curso PHP a distancia
      PHP
      [u]nsigned 1 1,143 Último mensaje 24 Noviembre 2010, 23:47
      por bomba1990
      AYUDA PDF DE CURSO PYTHON
      Dudas Generales
      grishapatiño 1 3,670 Último mensaje 14 Diciembre 2010, 06:22
      por grishapatiño
      Realizar curso de videojuegos
      Foro Libre
      crazykenny 6 1,440 Último mensaje 21 Mayo 2011, 21:03
      por crazykenny
      Problema con SetUnhandledExceptionFilter y Curso
      Ingeniería Inversa
      x64core 2 1,236 Último mensaje 9 Octubre 2012, 18:12
      por PeterPunk77
      Curso para Photoshop
      Diseño Gráfico
      chocola 3 4,823 Último mensaje 10 Junio 2013, 09:06
      por Peybool
      Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines