1 - Universidad Autónoma de Madrid

Anuncio
Prácticas de Sistemas operativos
David Arroyo Guardeño
Escuela Politécnica Superior de la Universidad Autónoma de Madrid
Tercera Semana: Comunicación entre
procesos con Tuberı́as
1
Entregas
2
Introducción
3
Tuberı́as
Ejemplo 1
Ejemplo 2
Ejemplo 3
Ejemplo 4
Entregas
☠ Ejercicio 9
☠☠ Entrega antes de la sesión del próximo
jueves 19 de febrero
☠☠☠ Examen de la primera práctica
: jueves
19 de febrero
Comunicación entre procesos
Ø Formas elementales
7 Envı́o de señales
7 Uso de ficheros ordinarios
ptrace
7 padre −−−−→ hijo
Ø Tuberı́as
Ø Facilidades IPC del Unix System V
1
2
3
Semáforos
Memoria compartida
Colas de mensajes
Tuberı́as
Canal de comunicación entre dos procesos:
semi-dúplex
1 Tuberı́as con nombre: FIFOS
2 Tuberı́as sin nombre
Tuberı́as sin nombre I
#include <unistd.h>
int pipe(int fildes [2]);
fd[1]
Proceso
padre
fd1[1]
Proceso
hijo
Núcleo
(kernel)
fd[0]
fd1[0]
7 Sólo el proceso que hace la llamada y
sus descendientes pueden utilizarla
7 fildes: descriptores de fichero
3 Leemos (read) de fildes[0] (fichero de sólo
lectura)
3 Escribimos (write) en fildes[1] (fichero de
sólo escritura)
7 Tras fork/exec los hijos heredan los
descriptores de ficheros
3 Abrimos la tuberı́a en el padre
3 Padre e hijo comparten la tuberı́a
7 La tuberı́a es gestionada por el núcleo
3 La dota de una disciplina de acceso en hilera
3 Llamadas a read sobre la tuberı́a no
devolverán el control hasta que no haya
datos escritos por otro proceso mediante
write
# include <s t d i o . h>
# include < s t d l i b . h>
# include <e r r n o . h>
# include <u n i s t d . h>
# define MAX 256
main ( )
{
i n t tube [ 2 ] ;
char message [MAX ] ;
i f ( p i p e ( tube ) == −1){
p e r r o r ( ” pipe ” ) ;
exit (1) ;
}
p r i n t f ( ” W r i t i n g TO t h e f i l e w i t h d e s c r i p t o r #%d\n ” , tube [ 1 ] ) ;
w r i t e ( tube [ 1 ] , ” TEST ” , 5 ) ;
p r i n t f ( ” Reading FROM t h e f i l e w i t h d e s c r i p t o r #%d\n ” , tube [ 0 ] ) ;
read ( tube [ 0 ] , message , 5 ) ;
p r i n t f ( ”READ DATA : \”%s \ ” \ n ” , message ) ;
c l o s e ( tube [ 0 ] ) ;
c l o s e ( tube [ 1 ] ) ;
exit (0) ;
}
Ejemplo 2
fd[1]
Proceso
padre
Proceso
hijo
Núcleo
(kernel)
fd[0]
# include <s t d i o . h>
# include < s t d l i b . h>
# include <s t r i n g . h>
# define MAX 256
main ( )
{
i n t tube [ 2 ] ;
int pid ;
char message [MAX ] ;
i f ( p i p e ( tube ) == −1){
p e r r o r ( ” pipe ” ) ;
e x i t ( −1) ;
}
i f ( ( p i d = f o r k ( ) ) == −1) {
perror ( ” fork ” ) ;
e x i t ( −1) ;
} else i f ( p i d == 0 ) {
/ ∗ C i e r r a l a t u b e r i a de e s c r i t u r a porque no l a va a usar ∗ /
c l o s e ( tube [ 1 ] ) ;
while ( read ( tube [ 0 ] , message ,MAX)> 0 && strcmp ( message , ”END\n ” ) ! =
0)
p r i n t f ( ” \ n r e c e i v e r process . Message : %s\n ” , message ) ;
exit (0) ;
} else {
/ ∗ C i e r r a l a t u b e r i a de l e c t u r a porque no l a va a usar ∗ /
c l o s e ( tube [ 0 ] ) ;
while ( p r i n t f ( ” sender process . message : ” ) !=0 && f g e t s ( message ,
s i z e o f ( message ) , s t d i n ) ! = NULL && w r i t e ( tube [ 1 ] , message ,
s t r l e n ( message ) + 1 ) > 0 && strcmp ( message , ”END\n ” ) ! = 0 ) ;
exit (0) ;
}
}
Ejemplo 3
tubetx [1]
Proceso
padre
tuberx [1]
Proceso
hijo
Núcleo
(kernel)
tuberx [0]
tubetx [0]
Comunicación bidireccional I
main ( )
{
int tube tx [2] , tube rx [ 2 ] ;
int pid ;
char message [MAX ] ;
i f ( p i p e ( t u b e t x ) == −1
| | p i p e ( t u b e r x ) == −1){
p e r r o r ( ” pipe ” ) ;
e x i t ( −1) ;
}
i f ( ( p i d = f o r k ( ) ) == −1) {
perror ( ” fork ” ) ;
e x i t ( −1) ;
} else i f ( p i d == 0 ) {
close ( tube tx [ 1 ] ) ;
close ( tube rx [ 0 ] ) ;
while ( read ( t u b e t x [ 0 ] , message ,MAX)> 0
&& strcmp ( message , ”END\n ” ) ! = 0 ) {
p r i n t f ( ” \ n r e c e i v e r process . Message : %s\n ” , message ) ;
s t r c p y ( message , ”READY” ) ;
w r i t e ( t u b e r x [ 1 ] , message , s t r l e n ( message ) + 1 ) ;
Comunicación bidireccional II
}
exit (0) ;
} else {
close ( tube rx [ 1 ] ) ;
close ( tube tx [ 0 ] ) ;
while ( p r i n t f ( ” sender process . message : ” ) !=0
&& f g e t s ( message , s i z e o f ( message ) , s t d i n ) ! = NULL
&& w r i t e ( t u b e t x [ 1 ] , message , s t r l e n ( message ) + 1 ) > 0 &&
strcmp ( message , ”END\n ” ) ! = 0 ) {
do{
read ( t u b e r x [ 0 ] , message , MAX) ;
} while ( strcmp ( message , ”READY” ) ! = 0 ) ;
}
exit (0) ;
}
}
Duplicación de descriptores de ficheros
3 Si en la shell hacemos 2>&1
7 Por ejemplo, ¿qué ocurre si hacemos lo
siguiente?
$ls -la . fichero_inventado >
results.log 2>&1
Duplicación de descriptores de ficheros
3 Si en la shell hacemos 2>&1
7 Por ejemplo, ¿qué ocurre si hacemos lo
siguiente?
$ls -la . fichero_inventado >
results.log 2>&1
7 La salida estándar de error (descriptor de
fichero= 2) es redirigida al mismo sitio al que
se envı́a la salida estándar (descriptor de
fichero=1
Duplicación de descriptores de ficheros
3 Si en la shell hacemos 2>&1
7 Por ejemplo, ¿qué ocurre si hacemos lo
siguiente?
$ls -la . fichero_inventado >
results.log 2>&1
7 La salida estándar de error (descriptor de
fichero= 2) es redirigida al mismo sitio al que
se envı́a la salida estándar (descriptor de
fichero=1
7 La shell efectúa el redireccionamiento de la
salida estándar de error
1
2
Duplica el descriptor de fichero 2
Dicho descriptor ahora se refiere al mismo
3 fcntl → man fcntl
3 dup → man dup
3 dup2 → man dup2
Si la shell sólo ha abierto los ficheros con
descriptores 0,1, y el descriptor 2 se refiere al
programa en ejecución y no existen otros
descriptores
7 ¿Qué hace la siguiente instrucción?
newfd = dup(1);
7 ¿Cómo puedo asociar el descriptor 2 a
nuestro duplicado?
Si la shell sólo ha abierto los ficheros con
descriptores 0,1, y el descriptor 2 se refiere al
programa en ejecución y no existen otros
descriptores
7 ¿Qué hace la siguiente instrucción?
newfd = dup(1);
3 Crea el duplicado del descriptor 1 usando
el fichero con descriptor 3
7 ¿Cómo puedo asociar el descriptor 2 a
nuestro duplicado?
Si la shell sólo ha abierto los ficheros con
descriptores 0,1, y el descriptor 2 se refiere al
programa en ejecución y no existen otros
descriptores
7 ¿Cómo puedo asociar el descriptor 2 a
nuestro duplicado?
3 Primero cierro el fichero con descriptor 2 y
luego llamo a dup
close(2);
newfd = dup(1);
⇒ En el ejemplo de antes bastarı́a hacer
dup2(1, 2);
# include < s t d l i b . h>
# include <s t d i o . h>
char ∗cmd1 [ ] = { ” / b i n / l s ” , ”−a l ” , ” / ” , 0 } ; char ∗cmd2 [ ] = { ” / u s r / b i n /
t r ” , ” a−z ” , ” A−Z ” , 0 } ;
void run1 ( i n t tube [ ] ) ; void run2 ( i n t tube [ ] ) ;
i n t main ( i n t argc , char ∗∗ argv )
{
i n t pid , s t a t u s ;
i n t tube [ 2 ] ;
i f ( p i p e ( tube ) ==−1){
f p r i n t f ( s t d e r r , ” E r r o r en l a l i n e a %d d e l f i c h e r o %s\n ” , L I N E ,
FILE ) ;
e x i t ( EXIT FAILURE ) ;
}
run1 ( tube ) ;
run2 ( tube ) ;
c l o s e ( tube [ 0 ] ) ;
c l o s e ( tube [ 1 ] ) ; / ∗ I m p o r t a n t e : hay que c e r r a r l o s dos d e s c r i p t o r e s de
la tuberia ∗/
while ( ( p i d = w a i t (& s t a t u s ) ) ! = −1) / ∗ Esperar a que hayan terminado
todos l o s h i j o s ∗ /
f p r i n t f ( s t d e r r , ” E l proceso %d ha terminado y su estado de
f i n a l i z a c i o n es %d\n ” , pid , WEXITSTATUS( s t a t u s ) ) ;
e x i t ( EXIT SUCCESS ) ;
}
void run1 ( i n t tube [ ] ) / ∗ E j e c u t a r l a p r i m e r a p a r t e de l a t u b e r i a ∗ /
{
int pid ;
switch ( p i d = f o r k ( ) ) {
case 0 : / ∗ h i j o ∗ /
dup2 ( tube [ 1 ] , 1 ) ; / ∗ Cerramos e l d e s c r i p t o r 1 , l a s a l i d a estandar ,
que pasa a s e r l a s a l i d a de l a t u b e r i a ∗ /
c l o s e ( tube [ 0 ] ) ; / ∗ Este proceso no n e c e s i t a e l o t r o extremo de l a
tuberia ∗/
execvp ( cmd1 [ 0 ] , cmd1 ) ; / ∗ E j e c u t a r e l p r i m e r comando , cmd1 ∗ /
p e r r o r ( cmd1 [ 0 ] ) ; / ∗ Estamos a q u i s o l o s i ha habido algun f a l l o ∗ /
d e f a u l t : / ∗ E l padre no hace nada ∗ /
break ;
case −1:
perror ( ” fork ” ) ;
e x i t ( EXIT FAILURE ) ;
}
}
void run2 ( i n t t u b e r i a [ ] ) / ∗ Se e j e c u t a l a segunda p a r t e de l a t u b e r i a ∗ /
{
int pid ;
switch ( p i d = f o r k ( ) )
{ case 0 : / ∗ h i j o ∗ /
dup2 ( t u b e r i a [ 0 ] , 0 ) ; / ∗ Este extremo de l a t u b e r i a pasa a s e r l a
entrada estandar ∗/
c l o s e ( t u b e r i a [ 1 ] ) ; / ∗ Extremo de t u b e r i a que no n e c e s i t a e s t e proceso
∗/
execvp ( cmd2 [ 0 ] , cmd2 ) ; / ∗ Se e j e c u t a e s t e comando ∗ /
p e r r o r ( cmd2 [ 0 ] ) ; / ∗ Estoy a q u i s o l o s i ha p r o d u c i d o algun e r r o r ∗ /
d e f a u l t : / ∗ E l padre no hace nada ∗ /
break ;
case −1:
perror ( ” fork ” ) ;
exit (1) ;
}
}
Referencias
⇒ Francisco M. Márquez. Unix,
Programación Avanzada. Editorial:
Ra-Ma. 3a Edición. ISBN: 84-7897-603-5
Descargar