notas de programaci´on de sistemashilario_sm/slide/notas-prog-sist.pdf · edificio 135, 14 sur y...

205
BENEM ´ ERITA UNIVERSIDAD AUT ´ ONOMA DE PUEBLA Facultad de Ciencia de la Computaci´on Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on de Sistemas Hilario Salazar Mart´ ınez& Mariano Larios G´omez BUAP Puebla-M´ exico Versi´ on 1.2 verano 2008

Upload: others

Post on 25-Feb-2021

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

BENEMERITA UNIVERSIDAD

AUTONOMA DE PUEBLA

Facultad de Ciencia de la Computacion

Edificio 135, 14 Sur y Av. San Claudio,

Ciudad Universitaria

Puebla, Pue. C.P. 72570

Notas de Programacion de Sistemas

Hilario Salazar Martınez & Mariano Larios Gomez

BUAP Puebla-MexicoVersion 1.2

verano 2008

Page 2: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Indice general

Indice de figuras VI

Indice de Tablas VIII

I Aspectos Basicos 1

1. Parametros main 2

2. Archivos en UNIX 4

2.1. Que son ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.2. Tipos de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.3. Creacion de archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.4. Los directorios mas comunes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.5. El directorio HOME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.6. Elementos de la lınea de comandos (que, como y quien) . . . . . . . . . . . . . . . . . . . . . . . 6

2.7. Comandos utiles con archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.7.1. ls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.7.2. find . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.7.3. more . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.7.4. cat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.7.5. pwd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.7.6. cd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.7.7. mkdir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.7.8. rmdir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.7.9. who . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.7.10. cp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.7.11. mv(move) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.7.12. rm (remove) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.7.13. echo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.7.14. grep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.7.15. sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.7.16. Ejecucion en background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.7.17. Las entradas/salidas estandar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.7.18. Redireccion de las entradas/salidas estandar . . . . . . . . . . . . . . . . . . . . . . . . . . 14

i

Page 3: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2.7.19. Restricciones de las redirecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3. Autorizacion de acceso a archivos 15

3.1. Categorias de usuarios en UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

II Apuntadores y arreglos 17

4. Que es un apuntador? 18

5. Para que usar punteros? 21

6. Artimetica de Punteros 25

7. Cadena de caracteres 28

8. Estructuras 31

8.1. Referenciando elementos estructurados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

8.2. Declarando un puntero de estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

8.3. Usando punteros de estructura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

9. Puntero a Matrices 38

10.Asignacion dinamica de memoria 41

11.Puntero a funciones 44

III Archivos 48

12.Archivos 49

12.1. I/O archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

12.2. sprintf y sscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

12.3. Peticion del estado del flujo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

12.4. E/S de bajo nivel o sin almacenamiento intermedio . . . . . . . . . . . . . . . . . . . . . . . . . . 51

IV NCURSES 54

V llamadas al sistema y gestion de procesos 55

13.Funcion system 56

14.La familia exec 58

15.Atributos de proceso 60

15.1. Estados de un proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

15.2. Creacion de un proceso(fork) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

15.3. Terminacion de procesos (exit y wait) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

15.3.1. exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

ii

Page 4: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

15.3.2. wait . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

15.4. Identificadores de proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

15.5. Ejemplo terminacion normal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

15.6. Ejemplo de terminacion anormal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

15.7. Ejemplo usando llamadas al sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

VI Senales 70

16.Concepto de senal 71

16.1. Como utilizar la funcion Kill . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

16.2. Tratamiento de senal(signal) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

16.3. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

VII Comunicacion Interprocesos (IPC) 79

17.Condiciones de competencia 80

18.Conceptos basicos IPC 85

18.1. El comando ipcs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

18.1.1. Salida del comando ipcs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

18.2. El comando ipcrm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

18.3. Pipes UNIX Semi-duplex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

19.Pipe sin nombre 89

19.1. Creacion de un ((pipe)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

19.2. Escritura en un ((pipe)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

19.3. Cierre de un ((pipe)) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

19.4. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

20.Pipes con nombre 94

20.1. Creacion de una FIFO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

20.2. Operaciones con FIFOs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

20.3. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

20.4. Acciones bloqueantes en un FIFO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

21.Colas de mensajes 98

21.1. Crear y abrir una cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

21.2. Enviar a una cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

21.3. Recibir desde la cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

21.4. Como eliminar cola de mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

21.5. Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

22.Semaforos 109

22.1. Caracterısticas de los semaforos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

22.2. Semaforos generales o contadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

iii

Page 5: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

22.3. Semaforos Binarios I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

22.4. Semaforos sincronizadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

22.5. Semaforos Binarios II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

22.5.1. Peticion de semaforo (semget()) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

22.5.2. Operaciones P y V (semop()) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

22.5.3. lock de un semaforo P(S) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

22.5.4. Unlock de un semaforo V(S) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

22.5.5. Destruir un semaforo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

22.6. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

23.Memoria compartida 127

23.1. llamada shmget() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

23.2. llamada shmat() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

23.3. llamada shmdt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

23.4. llamada shmctl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

23.5. Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

24.Sockets 138

24.1. Obteniendo informacion: los resolvers y otras llamadas . . . . . . . . . . . . . . . . . . . . . . . . 138

24.1.1. Obteniendo datos del host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

24.1.2. Obteniendo datos de los servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

24.1.3. Obteniendo el nombre del host local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

24.1.4. Conviertiendo diferentes formatos de direccion . . . . . . . . . . . . . . . . . . . . . . . . 143

24.2. Comunicacion con sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

24.3. Dominios y direcciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

24.4. Estilos de comunicacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

24.5. Protocolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

24.6. Las llamadas de sistema para la creacion y uso de sockets . . . . . . . . . . . . . . . . . . . . . . 147

24.6.1. Creacion del socket: socket( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

24.6.2. Escuchando en el puerto listen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148

24.6.3. Aceptando una llamada accept() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148

24.6.4. Conectandose al puerto connect() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

24.6.5. Recepcion de mensajes: read(), recv() y recvfrom() . . . . . . . . . . . . . . . . . . . . . . 149

24.6.6. Enviando mensajes: write(), send() y sendto() . . . . . . . . . . . . . . . . . . . . . . . . 150

24.6.7. Cerrando la comunicacion close() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

24.7. La comunicacion cliente-servidor con sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

24.8. Aspectos a considerar en el diseno de un servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

24.8.1. Servidor serial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

24.8.2. Servidor padre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

24.8.3. Aspectos a considerar en el diseno de un cliente . . . . . . . . . . . . . . . . . . . . . . . 154

24.9. Comunicacion con SOCKETPAIRS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

24.10.Comunicacion orientada conexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

24.10.1.El servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

24.10.2.El cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

iv

Page 6: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

24.11.Comunicacion orientada no conexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

24.11.1.El servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

24.11.2.El cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

24.12.Ejemplo sobre datagrama en el dominio UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

24.13.Ejemplo sobre datagrama en el dominio INTERNET . . . . . . . . . . . . . . . . . . . . . . . . . 163

24.13.1.Receptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

24.13.2.Emisor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

24.13.3.Ejecucion del ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

24.14.Conexion en el dominio INTERNET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

24.14.1.Cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

24.14.2.servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

24.14.3.Ejecucion de Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171

24.15.Conexion monoproceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172

24.15.1.Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174

24.16.Conexiones simultaneas (multiprocesos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

24.16.1.Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

VIII Creacion de demonios 181

25.Proceso para la creacion de un demonio 182

25.1. Demonios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

25.2. Como crear un demonio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

25.3. Como comunicarse con un demonio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

25.3.1. Como leer un archivo de configuracion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

25.3.2. Como anadir manipulacion de senales a un demonio . . . . . . . . . . . . . . . . . . . . . 188

IX Driver 190

26.Proceso para la creacion de un driver 191

26.1. El primer driver: carga y descarga del driver en el espacio de usuario . . . . . . . . . . . . . . . . 191

26.2. El driver “Hola mundo”: carga y descarga del driver en el espacio de kernel . . . . . . . . . . . 191

26.3. El driver completo “memoria”: parte inicial del driver . . . . . . . . . . . . . . . . . . . . . . . . 192

26.4. El driver “memoria”: conexion de dispositivos con sus ficheros . . . . . . . . . . . . . . . . . . . . 193

26.5. El driver “memoria”: eliminando el modulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

26.6. El driver “memoria”: abriendo el dispositivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195

Bibliografıa 196

v

Page 7: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Indice de figuras

1.1. Representacion de los argumentos argc y argv. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

4.1. Representacion de la Memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

4.2. Memoria reservada para una variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.3. Uso del operador & . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

5.1. Resultados listado 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.2. Copia del valor de pi en vpi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.3. Cambio de valor de vpi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.4. Resultados listado 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

5.5. Paso por referencia de pi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5.6. Asignacion de 0 al contenido de la memoria apuntado por vpi . . . . . . . . . . . . . . . . . . . 24

6.1. Incremento de punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

6.2. Resultados listado 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

7.1. Resultados listado 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

8.1. Resultados del listado 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

9.1. Representacion de matriz bidimensional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

11.1. Resultado listado 20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

14.1. execlp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

15.1. Estados de un proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

15.2. Procesos y llamadas al sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

17.1. Spooler de impresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

17.2. Visualizacion de dos procesos estan al mismo tiempo en su SC . . . . . . . . . . . . . . . . . . . . . . 81

17.3. Proceso activando y desactivando interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

18.1. Ejemplo del comando ipcs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

18.2. Ejemplo del comando ipcrm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

19.1. Comunicacion unidirecional utilizando una tuberia . . . . . . . . . . . . . . . . . . . . . . . . . . 89

19.2. Tuberia entre dos procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

vi

Page 8: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

21.1. suma de dos vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

22.1. Los semaforos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

22.2. Tipos de semaforos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

22.3. Estructura del semaforo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

22.4. Estructura del semaforo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

22.5. Relacion estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

22.6. Definicion de semop() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

22.7. operaciones semop(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

22.8. Resultado del ejemplo 1 (semaforos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

23.1. memoria compartida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

23.2. pasos para accesar a una memoria compartida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

23.3. parametros de entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

23.4. resultado ejemplo memoria compartida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

24.1. Ejemplo de un resolver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

24.2. conexion con sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

24.3. Pasos para crear una conexion con sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

24.4. Pasos a seguir para establecer una comunicacion en modo conexion . . . . . . . . . . . . . . . . . 157

24.5. Pasos a seguir para establecer una comunicacion en modo no conexion . . . . . . . . . . . . . . . 158

24.6. resultado emisor-receptor sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

vii

Page 9: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Indice de Tablas

3.1. Permiso de usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.2. Ejemplo de permiso de usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

16.2. Senales usadas por Kill . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

18.1. Archivos cabecera y llamadas al sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

22.1. operaciones de sem op . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

24.1. Secciones de procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

viii

Page 10: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte I

Aspectos Basicos

1

Page 11: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 1

Los argumentos argc, argv y env de

main()

Se usan dos argumentos especiales predefinidos argv y argc para recibir los argumentos de la linea de ordenes.

Prototipo:

main(int argc, char *argv, char env);

Donde:

argc Mantiene el numero de argumentos de la lınea de ordenes y es un entero. Siempre

es al menos uno ya que el nombre del programa se erige en el primer argumento.x

argv Es un puntero a un array de puntero de caracteres. Esto es, argv,

el puntero de un array de cadenas.

env Se usa para acceder a los parametros del entorno.

Ejemplo 1:

#copiar fichero1 fichero2

argc toma el valor de 3, y los contenidos de argv son argv[0]=’’compiar’’, argv[1]=’’fichero1’’ y

argv[2]=’’fichero2’’. Como vemos argv[0] contiene el nombre delprograma.

Ejemplo 2:

/* nombre.c */

#include <stdio.h>

#include <process.h>

int(int argc, char *argv[])

if(argc!=2)

printf("le falta especificar su nombre \n");

exit(1);

printf("Hola %s",argv[1]);

2

Page 12: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 1.1: Representacion de los argumentos argc y argv.

return 0;

Si se llama nombre y su nombre es Javier, entonces se deberia teclear nombre Javier para ejecutar el programa.

La salida deberia ser hola Javier.

#nombre Javier

Hola Javier

#

3

Page 13: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 2

Archivos en UNIX

2.1. Que son ?

Informacion asociada a un nombre

consideracion como una serie de bytes:

• Caracteres ASCII

• Codigo maquina

Almacenados en un disquete, disco duro, o banda

2.2. Tipos de archivos

1. Ordinarios

Contiene una serie de bytes (ASCII,binarios,ambos)

Estructura interna definida por el usuario

2. Directorios

3. Especiales perifericos, (descriptores)

a) Indican a UNIX cual driver se debe utilizar

b) Le proporcionan al driver las opciones a utilizar

c) Se encuentran dentro del directorio /dev

2.3. Creacion de archivos

Pueden ser creados por:

• un editor (ed, vi, emacs, etc ...)

• la redireccion de la salida estandar de un programa

#ls -l tarea >salida.txt

• algunos comandos (cp, sort, cc, mkdir)

La administracion de los archivos es efectuada por UNIX y es totalmente transparente al usuario.

4

Page 14: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Los archivos son generalmente escritos sobre disco.

Cada archivo esta asociado con el menos un nombre

2.4. Los directorios mas comunes

/bin contiene los principales comandos

/etc contiene los comandos y los archivos utilizados para el mantenimiento y administracion del sistema

/lib contiene librerıas y programas de los compiladores

/tmp contiene los archivos temporales los usuarios pueden usarla como zona de trabajo

/dev contiene los archivos especiales de descripcion de perifericos

/usr es, en teorıa, un sistema de archivos completo. Contiene los comandos locales, algunas librerias, los

comandos de usuario, los archivos de correo, etc. los directorios de los usuarios pueden encontrarse en ese

directorio

/usr/bin contiene los comandos complementarios

/usr/ucb contiene los comandos especificos BSD 4.x

/usr/lib contiene las librerias y los programas suplementarios.

Tambien contiene los programas de administracion de las impresoras y comunicacion

2.5. El directorio HOME

Cada usuario tiene un directorio HOME

Directorio creado por el administrador del sistema, generalmente es creado derntro del directorio /usr,

/u o /udd

Despues del “login”el usuario se encuentra dentro de este directorio, (que se convierte en su directorio de

trabajo)

Normalmente, el usuario trabajara en los directorios que se encuentran dentro del directorio HOME

Para posicionarse en el basta con teclear:

#cd <ret>

Una forma de accesarlo desde cualquier directorio es a traves del caracter “∼´´, por ejemplo:

# pwd

/home/usr/too/tareas

# cp ~/bin/xvi

5

Page 15: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2.6. Elementos de la lınea de comandos (que, como y quien)

El comando: que hacer?

• Es la primera palabra de la lınea

• Corresponde al nombre de un archivo ejecutable

Las opciones: como hacerlo?

• Siguen al comando (separados por espacios)

• Generalmente precedidos por un ’-’ (a veces por un ’+’)

$ls -/

$date +%d%m%y

Los argumentos: sobre quien actuar? Generalmente uno, o varios, nombres de archivos

$cat capitulo

$archivo nuevo

$ls -l tarea

2.7. Comandos utiles con archivos

2.7.1. ls

despliega los nombres de los archivos que se encuentran dentro del directorio actual.

Sintaxis: ls [opciones]

algunas opciones:

-a lista los archivos que comienzan con un “.”

-l listado en formato largo

-d si el argumento es un directorio lista el nombre del archivo directorio y no su contenido

-s da el tamano de los archivos en kilo-bytes

-u despliega la ultima hora de acceso en lugar de la ultima hora de modificacion

-t acomoda los archivos en funcion de la hora de la ultima modificacion

-i imprime el numero de referencia (i-node) de los archivos

-C lista los archivos en columnas

-g muestra el propietario del grupo de un archivo en un formato largo

Nota las opciones pueden estar mezcladas:

# ls -al

Ejemplo :

6

Page 16: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#ls -l

total 14464

-rwxr-xr-x 1 root root 624 dic 12 16:26 biblio.bib

-rwxr-xr-x 1 root root 141558 dic 10 10:41 buap.bmp

-rwxr-xr-x 1 root root 164694 dic 10 10:41 calendario2006.BMP

-rwxr-xr-x 1 root root 0 dic 16 09:50 comandos.txt

-rwxr-xr-x 1 root root 4410054 dic 14 18:40 Dibujo.bmp

-rwxr-xr-x 1 root root 1021622 ene 14 2006 ejemplo.bmp

-rwxr-xr-x 1 root root 23215 jul 28 2007 fdl.tex

drwxr-xr-x 8 root root 16384 dic 10 12:02 GnuWin32

Despliega la siguiente informacion

• El tipo de archivo:

- normal

d directorio

c especial(perifericos en modo caracter)

• las autorizaciones de acceso de los archivos:

r lectura

w escritura

x ejecucion

• Numero de ligas

• Propietario

• Tamano

• Fecha y hora de la ultima modificacion

• Nombre del archivo o directorio

2.7.2. find

Busca archivos y carpetas

Sintaxis: find [camino] [expresion]

Descripcion: Con find podemos realizar todo tipo de busqueda de archivos y directorios en todo el sistema

de archivos y atendiendo una serie de caracterısticas y patrones de archivos y directorios que se quiere

localizar. Tambien nos permite ejecutar un comando a cada archivo localizado. Por defecto se toma el

directorio actual y la opcion dentro de la expresion de -print.

Cuando tenemos varias expresiones se aplica el operador logico Y. Por lo que se deben satisfacer todas las

expresiones.

Opciones:

camino: Lista de directorios separados por comas donde queremos que se realize la busqueda. Esta se

ralizara recursivamente en todos los subdirectorios de los directorios de comienzo que se especifiquen.

7

Page 17: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

expresion: Muestra los tamanos en bytes.

-name ”archivo”: Busca el archivo cuyo nombre coincida con el que se encierra entre comillas, se

puede usar los comodines > y *.

-type t: Busca archivos cuyo tipo coincida con el indicado:

b: Especial de bloques.

c: Especial de caracteres.

d: Directorio.

p: Pipe.

l: Ligadura simbolica

s: Socket.

f: Fichero normal.

-print Muestra los nombres de los archivos que va encontrando en la salida estandar.

Solo se ha mostrado algunas de las expresiones, las que consideramos mas interesantes, de las muchas

que soporta el comando.Para mas informacion consule el manual del sistema (#man find).

Ejemplo: find / -name documento.txt -print

Busca a partir del directorio raız todos los archivos que se llamen documento.txt y los muesta en

pantalla.

2.7.3. more

Despliega el contenido de un archivo parandose entre cada pantalla

Sintaxis more nombre-archivo

Ejemplo

# more .cshrc

2.7.4. cat

Copia uno o varios archivos hacia la salida estandar (la pantalla por default)

Sintaxis: cat nombre-archivo

Ejemplo

#cat archivo.txt

2.7.5. pwd

Despliega el camino de acceso del directorio actual(donde se encuentra dentro del sistema de archivos).

Despliega el nombre de un directorio nunca el de un archivo

Sintaxis: pwd

Ejemplo

8

Page 18: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

$ pwd

/home/dic/rogomez/Perso

$

2.7.6. cd

permite cambiar de directorio

Sintaxis: cd [nombre-directorio]

cd sin nombre lo posiciona en el directorio HOME

Ejemplo:

#cd tareas

2.7.7. mkdir

creacion de directorios

Sintaxis: # mkdir directorio [directorio]

Ejemplos:

2.7.8. rmdir

Borra directorios

Sintaxis: #rmdir directorio [directorio]

rmdir no borrara el directorio si este no se encuentra vacio

Ejemplo:

#rmdir tareas

2.7.9. who

Despliega los usuarios conectados

Ejemplo:

#who

root :0 Dec 16 09:12

root pts/0 Dec 16 09:12

root pts/1 Dec 16 09:21

root pts/2 Dec 16 09:23

9

Page 19: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2.7.10. cp

copia una archivo ordinario

Sintaxis:

#cp archivo1 archivo2

#cp archivo [archivos] directorio

ejemplos

#cp arch1 arch2

#cp arch1 direc

#cp arch1 direc/arch2

#cp arch1 arch2 arch3 direc

#cp -r

cp no modifica los archivos originales, tan solo los duplica

opcion -r =copia recursivamente: si es un directorio copia el contenido de este

2.7.11. mv(move)

desplaza un archivo o lo renombra

Sintaxis:

#mv antiguo-nombre nuevo-nombre

#mv archivo [archivos] directorio

ejemplos:

#mv arch-a arch-b

#mv dire1 direc2

#mv arch1 arch2 arch3 arch4 direc

#mv arch1 .../bin/fx

2.7.12. rm (remove)

Borra el nombre de un archivo

Si ese nombre fuera el ultimo (numero de ligas=1). el archivo sera “fisicamente´´suprimido

Sintaxis: #rm archivo [archivos]

10

Page 20: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Ejemplo:

#rm arch1

Opciones:

-r recursivamente (si directorio contiene otro, borra contenido de este)

-f forza (no despliega errores, ni hace preguntas)

-i interactivo (pregunta)

2.7.13. echo

Muestra una linea de texto

Sintaxis

echo [-n][-e] cadena

Este comando sirve para mostrar a la salida estandar la cadena que se le pasa como argumento. Su utilidad

esta en los shell-scripts para dar informacion.

Opciones:

-n: No pone el caracter de control final de lınea, al final de la cadena.

-e: Permite la utilizacion de las siguientes caracteres en la cadena.

\a: Beep.

\b: Retroceso.

\c: Suprime salto de lınea.

\f: Avance de pagina.

\n: Salto de lınea.

\r: Retorno de carro.

\t: Tabular.

\v: Tabular vertical.

cadena: cadena a mostrar en la salida estandar.

2.7.14. grep

Sirve para encontrar dentro de un conjunto de archivos, todas lıneas que contienen una cadena de caracteres

especificadas por una expresion regular.

Sintaxis: #grep [opciones] expr-reg [archivos]

Opciones:

11

Page 21: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

-v despliega las lıneas que no contienen la expresion

-c imprime solo el numero de lıneas que contienen la expresion

-i no hace diferencia entre mayusculas y minusculas

-n despliega el numero de lıneas

Dentro de la misma familia, se encuentran los comandos siguientes:

• fgrep no admite las expresiones regulares

• egrap admiten expresiones regulares extendidas

2.7.15. sort

Permite ordenar las lineas de un arcchivo de texto. Por default, sort ordena en funcion de todos los

caracteres de la lınea, en orden creciente de los valores de caracteres ASCII

Sintaxis: #sort [opciones] [llave de ordenamiento] [archivos]

Opciones

-u suprime las lıneas conteniendo las llaves identicas

-n ordenamiento numerico

-b ignorar los blancos rn principio de la lınea

Ejemplos:

#sort archivo

#sort arch1 arch2>ordenado.txt

#sort -n numeros

2.7.16. Ejecucion en background

Concepto de job en cshell: Cuando se teclea un comando se le asigna un numero de job.

Para los comandos lentos en su ejecuıon resulta interesante poder disponer de la terminal, de tal forma

que se pueda ejecutar otros comandos.

Ponemos un “&”despues de la lınea de comandos, este se ejecutara en background, de una forma

sincronica.

Ejemplo

#sleep 10 &

[1] 712

#

1 es el numero de job

712 identificador del proceso (PID)

12

Page 22: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Una vez concluida el proceso, el sistema imprimira el siguiente mensaje luego de la proxima tecla ENTER

presionada:

[1]+ Done sleep 10

Para obtener un listado de los proceso que se estan ejecutando en segundo plano, solo hay que escribir el

comando

#jobs

[1]- Running sleep 20 &

[2]+ Running sleep 30 &

La tabla contiene tres columnas:

Primera el numero de proceso en background.

Segunda el estado (Running ejecutandose, Stopped parado).

Tercera la cadena correspondiente al proceso en cuestion.

Nota: La salida estandar de estos comandos, (ası como la salida estandar de errores) es la pantalla, salvo

si hay una redireccion, (lo cual es aconsejable).

Para regresarlo a foreground se usa el comando fg seguido del numero de job

#fg %1

si se quiere regresarlo a backgrond

#bg %1

2.7.17. Las entradas/salidas estandar

cuando se realiza una conexion a un sistema UNIX tres archivos, (descriptores), son abiertos:

Entrada estandar(stdin): el teclado por donde se teclean los comandos

Salida estandar (stdout): la pantalla donde son desplegados los resultados de los coamndos

Salida de errores (stderr): la pantalla donde son desplegados los errores eventuales

Un numero (el descriptor de archivo) es asignado a cada archivo:

0 entrada estandar

1 salida estandar

2 salida de errores

13

Page 23: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2.7.18. Redireccion de las entradas/salidas estandar

< redireccion de la entrada estandar

> redireccion de la salida estandar(creacion)

À redireccion de la salida estandar (anadir y agregar)

La redireccion de la salida de error depende del shell

bshell cshell

2> redireccion de la salida de error >& redireccion de la salida y de error

2>&1 redireccion de la salida estandar y de error No existe opcion para redireccionar solo la salida error

2.7.19. Restricciones de las redirecciones

Es posible modificar la redireccion de la salida estandar en shell con la variable noclobber

Existe el archivo?

SI NO

< lee el archivo error

> error creacion del archivo

À anadir al final del archivo error

14

Page 24: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 3

Autorizacion de acceso a archivos

3.1. Categorias de usuarios en UNIX

En UNIX el acceso a los archivos y directorios se diferencia en funcion de tres categorias de usuarios:

1. Propietario

El propietario del archivo

En principio se refiere al que creo el archivo

2. Grupo

En principio se refiere al grupo al cual pertenece el creador del archivo

3. Otros

El resto del mundo

A traves de ls -l podemos ver las autorizaciones de los archivos:

r w x r w x r w x

0 0 0 0 0 0 0 0 0

o o o

1 1 1 1 1 1 1 1 1

propietario grupo otros

Tabla 3.1: Permiso de usuarios

Para los archivos:

r autorizacion lectura

w autorizacion escritura

x autorizacion ejecucion

Para los directorios:

r autorizacion para leer el directorio (ls)

w autorizacion de escribir en el directorio (creacion, modificacion o supresion de archivos)

x autorizacion de se posicionar en el directorio (cd)

15

Page 25: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Por default cuando se crea un directorio se le asigna las autorizaciones siguiente

rwx rwx rwx (o sea 777 en octal)

En el caso de los archivos:

rw- rw- rw- (o sea 666 en octal)

Ejemplos:

octal binario propietario grupo otros

0600 000 110 000 000 rw- - - - r- -

0644 000 110 100 100 rw- r- - r- -

0666 000 110 110 110 rw- rw- rw-

Tabla 3.2: Ejemplo de permiso de usuarios

Cambio de autorizacion de acceso (chmod)

• El comando chmod permite modificar las autorizaciones de acceso de los archivos y de los directorios

ya creados.

• Sintaxis: chmod nuevo-modo [archivos] [directorios]

• Existen dos formas de especificar el nuevo nodo

F en octal:

chmod ooo archivo

Ejemplo: #chmod 660 tarea

F en modo simbolico:

#chmod [ugoa][+=][rwx]

u permiso del usuario

g permiso de grupo

o permiso de los otros

a todos los permisos

• Ejemplos de chmod

#la -lg e1

-rw-rw-rw- 1 toto daemon 0 Oct 12 18:20 e1

#chmod 755 e1

#-ls -lg e1

-rwxr-xr-x 1 toto daemon 0 Oct 12 18:20 e1

#ls -lg e1

-rw-r--r-- 1 toto daemon 0 Oct 12 18:20 e1

#chmod g+x e1

#chmod o-r e1

#ls -lg e1

-rw-r-x--- 1 toto daemon 0 Oct 12 18:20 e1

16

Page 26: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte II

Apuntadores y arreglos

17

Page 27: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 4

Que es un apuntador?

Suele resultar dificil dominar el concepto de punteros. Especialmente a los programadores que aprendieron

con lenguajes de alto nivel, donde no se permite trabajar directamente con posiciones de memoria, El mayor

problema en C surge al tratar de detectar esos “segmentation fault” maquiavelicos que aveces aparecen ,

aveces no.

En el libro de Kernighan y Ritchie “el elnguaje de la programacion en C”, se define un puntero Puntero!definicion

como “una variable que contiene la direccion de una variable”. Tras esta definicion clara y precisa, podemos

remarcar que un puntero es un tipo especial de variable que almacena el valor de una direccion de memoria.

La representacion anterior se utilizara a lo largo del texto para ilustrar los distintos ejemplos. De esta forma, si

Figura 4.1: Representacion de la Memoria

ejecutamos el siguiente ejemplo:

01 #include <stdio.h>

02 int main(void)

03

04 float pi=3.14;

05 printf("%.2f\n",pi);

06 return 0;

07

listado 1

Obtenemos en pantalla el esperado 3.14. Sin embargo, que operaciones han hecho el ordenador hasta mostrar

el resultado?. En la lınea 4, la instruccion reserva memoria para la variable pi. En este ejemplo, asumimos que

un decimal de tipo float ocupa 4 bytes. Dependiendo de la arquitectura del computador, la cantidad de bytes

18

Page 28: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

requerida para cada tipo de datos puede variar.

En la figura 4.2 muestra el estado de la memoria despues de la declaracion de la variable pi. la representacion

elegida facilita el seguimiento de los ejemplos, sin embargo, la representacion real en la memoria de ordenador

no tendrıa esa disposicion. Cuando se utiliza pi en la lınea 5, ocurre dos pasos diferenciables. En primer lugar,

Figura 4.2: Memoria reservada para una variable

el programa buscala direccion de memoria reservada para pi (en nuestro ejemplo. serıa la direccion 146). Hecho

esto, en segundo lugar, el programa recupera el contenido de esa direccion de memoria.

Asi, distinguimos por un lado la direccion de memoria asignada a una variable, y por otro lado el contenido

de la posicion de memoria reservada. Podemos acceder a la direccion de una variable utilizando el operador &.

Mediante este operador, obtenemos la direccion donde se almacena la variable (un numero!). En el ejemplo del

listado 2, se ha utilizado %u y una conversion forzada a entero sin signo para que la salida por pantalla sea

mas entendible. Se podıa haber utilizado el conversir estandar para punteros %p y haber quitado la conversion

forzada a entero sin signo.

01 #include <stdio.h>

02 int main(void)

03

04 float pi=3.14;

05 printf("Direccion de pi %u\n",(unsigned int)&pi);

06 return 0;

07

listado 2

Si ejecutamos el programa del listado 2 en la memoria del ejemplo anterior, deberıa mostrar la direccion 146.

Recordemos que una direccion de memoria es unicamente un numero. De hecho, podrıamos almacenarla en una

variable entera sin signo (ver listado 3).

01 #include <stdio.h>

02 int main(void)

03

04 float pi=3.14;

05 unsigned int dir=(unsigned int)&pi;

06 printf("Direccion de pi %u\n",dir);

07 return 0;

19

Page 29: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

08

listado 3

el codigo anterior demuestra que no hay nada de magia ni efectos especiales en el tema de las direcciones.

Simplemente son numeros que pueden ser almacenados en variables enteras1. Esto se puede ver graficamente

en la siguiente figura 4.3 (suponiendo que las variables de tipo entero sin signo requiere 3 bytes en memoria).

Figura 4.3: Uso del operador &

lo que se quiere representar simplemente es que mediante el operador & accedemos a la direccion de memoria

de variables. Esta direccion es un simple numero entero con el que podemos trabajar directamente.

Para diferenciar una variable entera que almacene valores de nuestro programa (por ejemplo, ındices, contadores,

valores...) de las variables que almacena direcciones, en C se creo un nuevo tipo de variable para alamcenar

direcciones. Este tipo es, precisamente, el puntero. Ası, podemos tener algunas declaraciones de punteros como

las siguientes:

char *nombre;

float *altura;

Como interpretar las lıneas anteriores? Podemos leer que nombre es un entero, pero podemos anadir que

es un entero especialmenter disenado para guardar la direccion de un caracter. Por su parte, altura es un

entero, ademas es un entero especialmente disenado para guardar la direccion de un float. Es lo que decimos

resumidamente con “nombre es un puntero a char” o “altura es un puntero a float”.

El operador * tsambien se utiliza para recuperar el contenido de una posicion de memoria indicada por una

direccion. No debemos confundir su uso en la declaracion de un puntero, como hemos visto antes, con los casos

en que se emplea para recuperar el contenido de una posicion de memoria. Este uso lo veremos a continuacion.

1Recordemos que el modificador unsigned simplemente indica que no se permiten numeros negativos. Las direcciones no pueden

ser negativas

20

Page 30: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 5

Para que usar punteros?

Hagamos un programa que cambie el valor de la variable pi del ejemplo anterior a cero. Para ello, realizaremos

el codigo que se muestra en el listado 4. En ese caso intentamos pasar el parametro vpi por referencia, para

que cambie el contenido de pi dentro de la funcion. Si ejecutaramos el programa, el resultado obtenido no es el

esperado, Porque?

01 void cambiavalor(float vpi)

02 vpi=0;

03 printf("Direccion de vpi: %u\n",(unsigned int)&vpi);

04 printf("Valor de vpi: %f\n",vpi);

05

06 int main(void)

07 float pi=3.14;

08 cambiavalor(pi);

09 printf("Direccion de pi: %u\n",(unsigned int)&pi);

10 printf("Valor de pi: %f\n",pi);

11 return 0;

14

Listado 4

la ejecucion del programa muestra que el parametro pi ha sido pasado por valor (en lugar de por referencia). Si

hacemos un seguimiento mas exhaustivo del programa, vemos que cuando llamamos a la funcion cambiavalor

en la lınea 8, el programa salta a la lınea 1 para ejecutar la funcion. Allı, se crea una nueva variable llamada

vpi, y se copia el valor de pi en ella. Esto se representa en el esquema de la figura 5.2.

queda clara la razon por la que vpi y pi tiene diferentes direcciones en memoria; se ha realizado una copia del

contenido de la memoria que tiene pi, se ha llevado a otra zona de memoria y se le ha puesto de nombre vpi.

De esta formna, cuando se asigna 0 a la variable vpi en la lınea 2, se esta asignado el contenido de memoria de

vpi ver figura 5.3.

Los argumentos de la funcion en C se pasan siempre por valor. No existe un a forma directa para que la

funcion que se invoca altere una variable de la funcion de la funcion que la llama. Es imprescindible el uso de

los punteros en el caso de querer modificar el valor de una variable pasada como parametro a una funcion.

21

Page 31: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 5.1: Resultados listado 4

Figura 5.2: Copia del valor de pi en vpi

Figura 5.3: Cambio de valor de vpi

Ası, podemos modificar el programa de Listado 4, para que el paso del argumento se realice por referencia.

Tendremos que utilizar los operadores * y & que hemos visto anteriormente. El nuevo programa se muestra en

Listado 5.

00 #include <stdio.h>

01 void cambiavalor(float *vpi)

02 *vpi=0;

03 printf("Direccion de vpi: %u\n",(unsigned int)vpi);

04 printf("Valor de vpi: %f\n",*vpi);

22

Page 32: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

05

06 int main(void)

07 float pi=3.14;

08 cambiavalor(&pi);

09 printf("Direccion de pi: %u\n",(unsigned int)&pi);

10 printf("Valor de pi: %f\n",pi);

11 return 0;

12

Listado 5

Figura 5.4: Resultados listado 5

En esta nueva version del programa, la funcion cambiavalor , recibe el parametro como un puntero (lınea 1).

Le estamos diciendo al compilador que pase como argumento a la funcion una direccion de memoria. Es decir,

queremos que le pase un entero; ese entero sera una direccion de memoria de un float.

En un seguimiento en detalle del programa vemos que, en la lınea 8, cuando llamamos a la funcion cambiavalor,

le tenemos que pasar una direccion de memoria. Para ello, tenemos que hacer uso del operador & , para pasarle

la direccion de la variable pi . Con esto, cuando llamamos a la funcion cambiavalor en 1, lo que se hace es una

copia de la direccion de memoria de pi en la variable (ver 5.5). Ası mediante el operador * podremos acceder

al contenido de la posicion de memoria de pi y cambiar su valor.

En la lınea 2 del programa se cambia el valor de la zona de memoria apuntada por vpi . Podemos leer esa

instruccion como “asignamos cero al contenido de la direccion de memoria apuntada por vpi”. Recordemos que

esa zona de memoria tiene que ser de tipo float. En el ejemplo de la figura 5.5, esta direccion de memoria es la

de 146. Podemos ver el esquema de l resultado de la operacion de la figura 5.6

Ahora, cuando acabe la funcion cambiavalor y retornemos a la funcion principal main, el valor de vi sera. 0.

Hemos realizado un pase de parametros por referencia.

23

Page 33: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 5.5: Paso por referencia de pi

Figura 5.6: Asignacion de 0 al contenido de la memoria apuntado por vpi

A continuacion examinaremos en profundidad algunos tipos de datos complejos (como vectores y estructuras)

y su utilizacion con punteros, ası como algunos operadores nuevos especıficos de punteros.

24

Page 34: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 6

Artimetica de Punteros

Como hemos mencionado anteriormente, podemos considerar el tipo de un puntero como el tipo de datos que

contiene la zona de memoria a la que este apunta. Es decir, si tenemos:

int *ptr;

*ptr=2;

La primera lınea indica que tenemos una variable llamada ptr , cuyo contenido es una direccion de memoria a

una zona donde hay un entero. E la segunda lınea asignamos al contenido de esa direccion de memoria el valor 2.

Una operacion interesante que podemos realizar con punteros es incremetarlos. Obviamente, la operacion de

incremento en un puntero no es igual que en una variable normal. Un incremento en un puntero

hara que apunte al siguiente elemento del mismo tipo de datos que se encuentran en memoria.

en el ejemplo de la figura 6.1, inicialmente el puntero ptr apunta al tercer entero almacenado en el bloque

de memoria representado (suponiendo que cada entero ocupa 3 posiciones de memoria). Ası, la asignacion

*ptr=168 hara que el contenido de esa posicion de memoria sea 168 hara que el contenido de esa posicion

de memoria sea 168. Si incrementamos el puntero (cuidado!, no hablamos del contenido del puntero, sino del

propio puntero), haremos que apunte al siguiente entero que hay en memoria. Este incremento del puntero no

implica sumar 1 a la direccion que contiene el puntero. En este ejemplo, serıa necesario sumar 3 (tres posiciones

de memoria que ocupa el entero para acceder al siguiente).

Naturalmente esta “suma de posiciones de memoria” no la tendra que hacer el programador. Sera el

compilardor el que se encargue de realizar las cuentas para ver cuantas posiciones de memoria debe avanzar.

En general, el compilador tendra sumar tantas posiciones de memoria como requiera el tipo de datos sobre el

que se haya definido el puntero. En el caso de que incrementemos alegremente el puntero y nos salgamos de la

zona de memoria que tengamos reservada recibiremos la visita de nuetro amigo ¡¡segmentation fault¿¿.

Vamos a ilustrar el uso de punteros con vectores mediante un ejemplo. Supongamos que definimos un array de

entero (que, a fin de cuentas, es un conjunto de enteros a los que se reserva posiciones de memoria consecutiva)

como se muestra en la lınea 2 del Listado 6.

01 #include<stdio.h>

02 int vector[]=1,56,47,-89,10;

03 int *ptr;

04 int main(void)

05

25

Page 35: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 6.1: Incremento de punteros

06 int i;

07 ptr=&vector[0];

08 for(i=0; i<5; i++)

09 printf("Vect[%d]= %d\n", i, vector[i]);

10 printf("Ptr + %d= %d\n",i,*(ptr+i));

11

12 return 0;

13

Listado 6

Figura 6.2: Resultados listado 6

Tenemos un vector de 5 enteros. Podemos acceder a ellos mediante el ındice que ocupa en el array (como por

ejemplo, vector[3] nos darıa el cuarto entero del array). De igual forma podemos acceder a el mediante un

puntero. En la lınea 7, asignamos al puntero la direccion del primer elemento del vetor. En C, el nombre del

arreglo apunta al primer elemento de array; por lo que podriamos cambiar la linea 7 por la instruccion

ptr=vector; con identicos resultados.

26

Page 36: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

De cualquier forma, debemos de tener muy claro que, una vez definido un array, el nombre del array identifica

a la posicion del primer elemento, pero es una constante. Es decir, podemos utilizarlo como hemos indicado

antes para asignarselo a un puntero (copiamos la direccion de memoria del primer elemento), pero no podemos

asignarle a un array ya creado una direccion distinta; vector=otro puntero provocarıa un error. Algunos

programadores llaman al nombre de un array como “puntero constante”, indicando que no puede cambiarse

su valor. Es simplemente un puntero al que no podemos cambiar su direccion.

27

Page 37: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 7

Cadena de caracteres

Recordemos que en C, las cadenas de caracteres son simplemente arrays de caracteres. No hay un tipo de datos

especial, como en Basic o Pascal. Una cadena en C terminara siempre con un caracter especial reservado para

indicar “fin de cadena”, que es un cero binario, representado por “ \0 ”.

Vamos a implementar nuestr4a funcion de copia de cadenas, similares a la de ANSI C: strcpy ().

01 #include <stdio.h>

02 char origen[40]="Apuntes de Punteros";

03 char destino[40];

04 char *mi_strcpy(char *dest, const char *orig)

05

06 char *p=dest;

07 while (*orig != ’\0’)

08 *p++=*orig++;

09 *p=’\0’;

10 return dest;

11

12 int main(void)

13

14 int i;

15 mi_strcpy(destino, origen);

16 printf("%s",destino);

17 return 0;

18

Listado 7

En el listado anterior, el espacio necesario para las dos cadenas se reserva en origen y destino. Cuando llamamos

a la funcion mi strcpy, en la lınea 15 lo unico que se pasa es la direccion de memoria de ambas cadenas. Este

plantemiento nos permite trabajar con arrays de gran tamano sin tener que mover gran cantidad de informacion

en memoria. como las cadenas de caracteres son exactamente iguales que los arrays de cualquier otro tipo de

datos, el enfoque utilizado es el mismo en todos los casos.

Recordemos que en la figura 5.5, lo que pasamos como argumento a la funcion es una copia de la direccion

28

Page 38: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 7.1: Resultados listado 7

de memoria. Ası, si cambiamos el puerto dentro de la funcion mi strcpy, a la vuelta de la funcion los punteros

originales seguiran apuntando a donde estaban. Esto se puede ver en el ejemplo anterior; cuando llamamos a la

funcion en la lınea 15, los punteros origen y destino apuntan al comienzo de las cadenas. Dentro de la funcion

mi strcpy, los punteros van avanzando en la (lınea 8), y al final ambos estan situados al final de las cadenas.

Al ser copias, cuando volvemos de la funcion, los punteros origen y destino siguen apuntando al inicio de la

cadena.

El modificar const usado en la lınea 4 hace que, dentro de la funcion, no podamos modificar el contenido de la

memoria apuntada por orig, tomando el contenido como valor constante.

En la lınea 6, hacemos que un puntero auxiliar apunte al inicio de la cadena destino (dest). Despues, mientras

el caracter apuntado por el puntero por orig sea distinto del fin de cadea, copiamos el caracter en la zona de

memoria apuntada por p y avanzamos. Esto lo realizamos con una unica instruccion (lınea 8 ). Es equivalente

*p++ a (*p)++?. No, segun la precedencia de los operadores, *p++ es equivalente a *(p++). Y,

que decimos en cada caso?. Pues con la primera instruccion, *p++, decimos que primero veamos el contenido

del puntero y despues incrementemos la posicion del puntero (utilizado en la funcion de arrays), mientras que

con (*p)++ decimos que incremente el contenido de la posicion de memoria donde apunta p. Si p apuntara a

un caracter “C”, harıa que ese caracacter pasara a ser “D”. Si p apunta a un array cuyo valor es 24, despues

de esa instruccion serıa 25.

Queda claro, por tanto, la necesidad de establecer correctamente el orden de aplicacion entre operadores.

Una buena practica que mantiene el codigo mas legible es utilizar un parentesis siempre que haya dudas.

Naturalmente, siempre dentro de unos lımites razonables. Es necesario el apuntador auxiliar p? Claramente

No. se ha utilizado unicamente para dejar bien claro que trabajamos con direcciones de memoria , y que *p y

*dest es exactamente lo mismo. Por eso, al retornar de la funcion devolveremos dest. El codigo de la funcion

mi strcpy del Listado 7 podıan simplificarse como muestra el Listado 8.

01 while (*orig != ’\0’)

29

Page 39: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

02 *(dest++) = *(orig++);

03 *dest = ’\0’;

04 return dest;

Listado 8

En esta ultima version se ha hecho uso de parentesis para indicar de forma clara, que queremos copiar el

contenido de la memoria apuntada por orig en dest y, despues, incrementar los punteros. Finalmente hay que

anadir la constante \0 en la ultima posicion de dest (para que cupla el convenio utilizado con cadenas de

caracteres en ANSCI C). Incluso podrıamos optimizar algo mas el codigo poniendo en la definicion del bucle

while(*orig), ya que sera cierto mientras el valor apuntado por orig se distinto de cero.

finalmente recordemos que podemos utilizar tambien la notacion de array para trabajar con cadenas de

caracteres. Ası, tednrıamos la expresion dest[i] y *(dest + 1) como equivalentes.

30

Page 40: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 8

Estructuras

En C una Estructura es una coleccion de variables que se referencian bajo el mismo nombre. Las variables que

conforman la estructura son llamados elementos estructurados.

La palabra clave struct dice al compilador que se esta definiendo una plantilla de estructura.

struct dire

char nombre[40];

char calle[40];

char ciudad[20];

char estado[3];

unsingned long int codigo;

;

Note que la definicion termina en un punto y coma. La razon para el punto y coma es que una definicion de

estructura es una sentencia.

En este momento del codigo, realmente no ha sido declarada ninguna variable. El codigo solo ha definido el

formato de los datos. Para declara una variable real con esta estructura, se escribirıa.

struct dire info_dire;

Se puede declara una o mas variables cuando se define una estructura. Veamos el siguiente listado como ejemplo

de lo anterior.

struct dire

char nombre[40];

char calle[40];

char ciudad[20];

char estado[3];

unsingned long int codigo;

info_dire, binfo, cinfo;

Si solo necesita una variable estructurada, no se necesita incluir el nombre de la estructura. Esto significa que

el codigo

struct

char nombre[40];

char calle[40];

31

Page 41: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

char ciudad[20];

char estado[3];

unsingned long int codigo;

info_dire;

El formato general de una definicion de estructura es

struct nombre tipo de estructura

type nombre_elemento1;

type nombre_elemento1;

type nombre_elemento1;

.

.

.

type nombre_elementoN;

variables_estructura;

8.1. Referenciando elementos estructurados

Se referencia elementos individuales de la estructura usando el operador “.”, que se llama a veces

operador de punto. Por ejemplo el siguiente codigo asignara el codigo postal 12345 al campo correspondiente

de la variable estructura info_dire que se declaro antes.

info_dire.codigo=12345;

El formato gerneral para acceder a los elementos es:

nombre_estructura.nombre_elemento

Por tanto, para imprimir el codigo postal en pantalla, se escribirıa

printf("%u",info_dire.codigo);

8.2. Declarando un puntero de estructuras

Se declara un puntero de estructura poniendo el * delante del nombre de la variable de estructura. Por ejemplo,

si se supone la estructura dire definida antes, los siguiente declara puntero_dire para puntero de datos de ese

tipo:

struct dire *puntero_dire;

32

Page 42: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

8.3. Usando punteros de estructura

Para encontrar la direccion de una variable de estructura, se pone el operador & antes del nombre de variable

de estructura. Por ejemplo el fragmento siguiente

struct bal

float balance;

char name[80]

persona;

struct bal *p;

Se vera que este codigo

p=&persona;

pone la direccion de persona en el puntero p. Para referenciar el elemento balance, se escribiria

(*p).balance

Los parentesis son necesarios alrededor del puntero porque el operador de punto tiene una prioridad mas alta

que el operador *.

Existe otro metodo de acceso a los elementos de estructuras usando punteros utiliza operador flecha (->), que

es esencialmente un atajo para el metodo anterior.

Para ver como se puede usar un puntero de estructura, examinar este sencillo programa que imprime horas,

minutos y segundos en la pantalla usando un temporizador por software.

Ejemplo 1:

/*visualiza un temporizador software */

#include <stdio.h>

#include <conio.h>

struct time_struct

int hours;

int minutes;

int seconds;

;

void update(struct time_struct *t);

void display(struct time_struct *t);

void delay(void);

33

Page 43: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

main()

struct time_time;

time.hours=0;

time.minutes=0;

hora.second=0;

for(; !kbhit(); )

update(&hora);

display(&hora);

/* version 1 explica las referencias de puntero */

void update(struct time_struct *t)

(*t).seconds++;

if((*t).seconds==60)

(*t).seconds=0;

(*t).minutes++;

if((*t).minutes==60)

(*t).minutes=0;

(*t).hours++;

if((*t).hours==24) (*t).hours=0;

delay();

void display(struct time_struct *t)

printf("%d:", (*t).hours);

printf("%d:", (*t).minutes);

printf("%d:", (*t).seconds);

void delay(void)

long int t;

for(t=1; t<128000; ++t);

Listado 30

34

Page 44: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Se usa el operado flecha en lugar del operador punto cuando se accede a un elemento de estructura, dado un

puntero a la variable estructura. Por ejemplo:

(*t).hours

es lo mismo que

t->hours

por lo tanto en el listado 30 se podrıa reescribir update() como:

/* version 2 con el operador flecha */

void update(struct time_struct *t)

t->seconds++;

if(t->seconds==60)

t->seconds=0;

t->minutes++;

if(t->minutes==60)

t->minutes=0;

t->hours++;

if(t->hours==24) t->hours=0;

delay();

Ejemplo 2:

Vamos a realizar un programa que muestre por pantalla un elemento de tipo estructura.

01 #include <stdio.h>

02 #include <string.h>

03 struct alumno

04 char nombre[20];

05 char apell[40];

06 int edad;

07 float nota;

08 ;

09 void ver_alumno(struct alumno *a)

10 printf("%s %s \n",a->nombre,a->apell);

11 printf("\n Calificacion: %f",a->nota);

12

35

Page 45: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

13 int main(void)

14

15 struct alumno alum;

16 strcpy(alum.nombre,"Perico");

17 atrcpy(alum.apell,"Palotes");

18 alum.edad=22;

19 alum.nota=8.75;

20 ver_alumno(&alum);

21 return 0;

22

Listado 9

Figura 8.1: Resultados del listado 9

Notas:

Un error tipico es olvidar el puntero y coma al terminar la definicion de una estructura (lınea 8) del

Listado 9. Despues de declarar una varible del tipo estructura alumno, dentro de main.

Podemos acceder a sus campos con el operador punto “.” siempre que estemos trabajando con la estructura

directamente.

Cuando estemos utilizando un puntero a estructura, utilizaremos el operador ->. Este operador es

equivalente a utilizar (*puntero_estructura).campo.

ANSI C permite pasar por valor las estructuras completas. Sin embargo, esto implica que se copia el

contenido de toda la estructura (completa) desde la funcion llamante a la funcion llamada. Esto, para

estructuras con un gran numero de elementos, puede ser un problema. Por cuestiones de eficiencia y

comodidad, pasaremos las estructuras a la funciones mediante un puntero.

36

Page 46: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Ası, en la lınea 11 del Listado 9 podıamos haber accedido al campo nota del puntero a estructura ‘‘a’’,

mediante la instruccion (*a).nota sin embargo, es mas comodo utilizar la notacion flecha “− >”para estos

casos.

37

Page 47: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 9

Puntero a Matrices

Anteriormente hemos visto la relacion entre punteros y array; vamos a ampliar nuestro cmapo de vision utilizando

arrays de varias dimensiones (matrices).

01 #include <stdio.h>

02 #define NFILAS 5

03 #define NCOLUMNAS 8

04 int main(void)

05

06 int fil, col;

07 int matriz[NFILAS][NCOLUMNAS];

08 /*asignamos valores a los elementos */

09 for(fil=0; fil<NFILAS; fil++)

10 for(col=0; col<NCOLUMNAS; col++)

11 matriz[fil][col]=fil*col;

12 /*Mostramos los valores de 2 formas */

13 for(fil=0; fil<NFILAS; fil++)

14 for(col=0; col<NCOLUMNAS; col++)

15 printf("%d - ",matriz[fil][col]);

16 printf("%d\n", *(*(matriz+fil)+col));

17

18 return 0;

19

Listado 10

El resultado es el siguiente:

En el ejemplo del listado 10, tenemos una declaracion de matriz en la linea 7. Que significa esa instruccion?

Por un lado tenemos un array de 5 elementos (filas), pero a su vez cada uno de estos elementos son arrays de 8

enteros (columnas).

Sabemos que los array se alamacenan contiguos en memoria. Ası, el estado de nuestra memoria pordrıa

representarse como:

38

Page 48: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 9.1: Representacion de matriz bidimensional

000000000123456702468101214036912151821...

el primer cero indica la direccion de &matriz[0][0] (que podemos nombrar tambien unicamente con el nombre

de matriz). De esta forma, si quicieramos mostrar el tercer elemento de la segunda fila (el numero 2 marcado

en rojo), podriamos hacerlo como matriz[1][1] o bien como *(*(matriz+1)+2). La primera forma es sencilla;

utilizamos la notacion de array a la que estamos acostumbrados. la notacion de punteros podemos leerla como

“Dame el contenido de una direccion. Esa direccion esta formada por el contenido de donde apunta matriz mas

uno. A ese resultado sumale dos”. Podemos ver cada fila de la matriz como un puntero cuyos elementos son

punteros a arrays de enteros (ver figura 9.1).

Ası, podemos acceder al elemento de la segunda fila, segunda columna pidiendo primero el contenido de

matriz+1. Esto nos dara el segundo puntero del array (recordemos que los arrays en C empiezan a numerarse

en 0). Con esto, tenemos una direccion. Si a esa direccion de memoria le sumamos dos, accedemos al tercer

elemento del segundo array (en el ejemplo de la figura 9.1, tendriamos el 11). En caso de querer acceder al primer

elemento del segundo array, bastaria con escribir *(*(matriz+1)) o, de forma equivalente **(matriz+1).

Queda claro, por tanto, que el primer componente de la matriz bidimensional es equivalente a un array

del siguiente tipo: int *filas[NFILAS]. En esta declaracion estamos diciendo que tenemos un vector cuyos

elementos son punteros a enteros, los mismo que tenıamos en la parte de filas de la declaracion anterior.

Que tiene que hacer el compilador para acceder a cada uno de los elementos de la matriz? Debe clacular la

posicion del elemento en memoria. Para ello, debera multiplicar la fial en la que se encuentra por el numero de

elementos por columna y sumarle la columna en la que esta actualmente. Es decir:

Posicion en memoria=(filas * NCOLS) + columna

Como puede verse en la expresion anterior, es necesario que el compilador sepa a priori, el numero de elementos

que tiene cada columna de nuestra matriz. En egernal, es necesario especificar, al menos, la segunda dimension

de la matriz en forma fija. Puede interesarnos implementar una funcion que nos permita trabajar con nuestro

array multidimensional. Un ejemplo muy sencillo (aunque inutil) de esto puede verse en el listado 11.

39

Page 49: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

void reset_matriz(int mat[][NCOLUMNAS])

int fil, col;

for(fil=0; fil<NFILAS; fil++)

for(col=0; col<NCOLUMNAS; col++)

mat[fil][col]=1;

Listado 11

En general, en arrays de mas de una dimension es necesario indicar, en la definicion los parametros a la funcion,

las dimensiones segunda, tercera, etc . . .

40

Page 50: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 10

Asignacion dinamica de memoria

En muchas ocasiones no podemos saber a priori el espacio que vamos a necesitar para nuestra estructura de

datos. Esto debera decidirse en tiempo de ejecucion y llevarse a cabo mediante funciones de gestion de memoria

como malloc, calloc, u otras similares.

Cuando pedimos memoria con alguna funcion de gestion (como malloc()), esta nos devuelve un puntero. En

compiladores ANSI, este puntero es de tipo void. Como void es un tipo generico que a apunta a “cualquier

cosa”, tendremos que indicar al compilador que haga una conversion de tipo (cast) al tipo de datos que nos

interese; es decir, a un puntero al tipo de datos que queramos.

Ademas, sera necesario indicarle a la funcion que llamemos para pedir memoria el numero de bytes que

necesitamos. En C, el numero de bytes que se necesitan para almacenar cada tipo de datos es dependiente

de la maquina sobre la que ejecutamos el programa. Por ejemplo, en dos maquinas distintas una puede requerir

2 bytes para un entero y otra utilizar 4 bytes para el mismo entero. como podemos decirle el numero de bytes

que vamos a necesitar? Utilizando la funcion sizeof(). Ası, el tamano que queremos reservar estara en funcion

del tamano del tipo de datos que utilicemos. Esto puede verse en el ejemplo del Listado 12.

01 #include <stdio.h>

02 #include <stdlib.h>

03 int main(void)

04

05 int *ptr, i;

06 ptr=(int *)malloc(10*sizeof(int));

07 if (ptr==NULL) printf("Error de Mem.");

08 for(i=0; i<10; i++)

09 ptr[i]=1;

10 return 0;

11

Listado 12

Utilizando la equivalencia entre la notacion de punteros y de array, en la lınea 9 asignamos a todos los elementos

de array creados anteriormente un valor constante 1.

El ejemplo anterior premite crear un arrreglo de una dimension dinamico en tiempo de ejecucion, pero ’?Como

creariamos un arraymultidimensional?. Hemos visto en el puntero anterior que los arrays bidimensionales se

41

Page 51: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

gestionan como arrays de punteros a arrays del tipo de datos que queremos utilizar. Como reservamos memoria

para estas matrices?. En un planteamiento general, necesitaremos generar la matriz dinamicamente, tanto el

numero de filas como de columnas. En los siguientes ejemplos utilizaremos como tipo de datos base el entero,

pero podrıa utilizarse cualquier otro.

01 #include <stdio.h>

02 #include <stdlib.h>

03 int main(void)

04

05 int nfilas=5, ncols=10;

06 int fila;

07 int **filaptr;

08 filaptr=malloc(nfilas * sizeof(int *));

09 if(filaptr==NULL) printf("Error");

10 for(fila=0; fila<nfilas; fila++)

11 filaptr[fila]=(int *)malloc(ncols *sizeof(int));

12 if(filaptr==NULL)printf("Error");

13

14 return0;

15

Listado 13

En el Listado 13, filaptr es un puntero a puntero a entero. En este caso, el programa crea una matriz de 5

filas por 10 columnas. Esos datos, naturalmente, podrıa gestionarse en tiempo de jecucon.

La primera llamada a malloc (lınea 8) reserva espacio para array de punteros a los arrays de enteros. Po esta

razon, a la funcion sizeof le pasamos el tipo “puntero a entero”. Con el primer array de punteros creado,

pasamos a reservar memoria para los arrays que son apuntados por cada elemento “fila” (en al Lınea 11).

En el ejemplo anterior se necesitan en total 6 llamadas a malloc; una para reservar el array de filas y una para

cada array de enteros asociado a cada fila. Ademas no nos aseguramos de tener toda la matriz en un bloque

contiguo de memoria. Se puede comprobar, utilizando un puntero auxiliar al primer elemento de la primera fila

(es decir, usando int *ptr_aux que apunte a filaptr[0] e incrementado *(testptr++) no accedemos a los

valores introducidos a la matriz). Tal vez obtenemos un bonito “segmentation fault”.

Con este planteamiento, deberemos acceder a los elementos de la matriz unicamente utilizando la notacion de

corchetes. No podemos utilizar un puntero para reccorrer la memoria.

01 #include <stdio.h>

02 #include <stdlib.h>

03 int main(void)

04

05 int nfilas=5, ncols=10;

42

Page 52: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

06 int *mem_matriz;

07 int **filaptr;

08 int *testptr;

09 int fila, col;

10 mem_matriz=malloc(nfilas * ncols * sizeof(int));

11 if(mem_matriz == NULL) printf("Error");

12 filaptr=malloc(nfilas * sizeof(int *));

13 if(filaptr == NULL) printf("Error");

14 for(fila=0; fila<nfilas; fila++)

15 filaptr[fila] = mem_matriz+(fila*ncols);

16 for(fila=0; fila<nfilas; fila++)

17 for(col=0; col<ncols; col++)

18 filaptr[fila][col]=1;

19

20 testptr=filaptr[0];

21 for(fila=0; fila<nfilas; fila++)

22 for(col=0; col<ncols; col++)

23 printf("[%d][%d]%d \n",fila, col, *(testptr));

24 return 0;

25

Listado 14

En el ejemplo del Listado 14 se reserva la memoria para la matriz en un bloque contiguo. En la lınea 10 se

reserva la memoria para todas las celdas de la matriz. En la lınea 12 hacemos el array de punteros correspondiente

a la fila de la amtriz. en la lınea 14-15 hacemos que cada puntero de las filas apunte a la celda donde empieza

cada fila. Esto puede calcularse con un simple producto (fila*ncols).

Las lıneas siguientes (16. . . 23) desmuestran que el bloque creado es contiguo, y que podemos utilizar un

punterom auxiliar para rrecorrer las posiciones de memoria.

Con este metodo se ha tenido que utilizar unicamente dos llamadas a la funcion malloc; una para crear el array

de filas y otro para reservar el espaciode todas las celdas. Ademas , serıa posible utilizar funciones que trabajan

con zonas de memoria para su inicializacion de una vez (como memset()).

43

Page 53: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 11

Puntero a funciones

Loa apuntadores a funciones son quiza uno los usos mas confusos de los apuntadores en C. Los apuntadores a

funciones no son tan comunes como otros usos que tienen los apuntadores. Sin embargo, un uso comun es cuando

se pasan apuntadores a funciones como parametros en la llamada a una funcion. Lo anterior es especialmente

util cuando se deben distintas funciones quizas para realizar tareas similares con los datos. Por ejemplo, se

pueden pasar los datos y la funcion que sera usada por alguna funcion de control. como se vera mas adelante

la biblioteca estandar de C da funciones para ordenamiento (qsort) y para realizar busqueda (bsearch), a las

cuales se les puede pasar funciones.

Para declara una apuntador a una funcion se debe hacer:

int (*pf)();

Lo cual declara un apuntador pf a una funcion que regresa un tipo de dato int. Todavia no se ha indicado a

que funcion apunta.

Suponiendo que se tiene una funcion int f(), entonces simplemente se debe escribir:

pf=&f;

Para que pf apunte a la funcion f()

Para que trabaje en forma completa el compilador es conveniente que se tengan los prototipos completos de las

funciones y los apuntadores a las funciones, por ejemplo:

int f(int);

int (*pf)(int)=&f;

Ahora f() regresa un entero y toma un entero como parametro.

Se pueden hacer cosas como:

ans=f(5);

ans=pf(5);

Los cuales son equivalentes.

Las funciones de biblioteca estandar qsort es muy util y esta disenada para ordenar un arreglo usando un valor

44

Page 54: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

como llave de cualquier tipo para ordenar en forma ascendente.

El prototipo de la funcion qsort de la biblioteca stdlib.h es:

void qsort(void *base, size_t nmiemb, size_t tam, int(*compar)(const void *, const void *));

Donde:

base apunta al comienzo del vector que sera ordenado.

nmiemb indica el tamano del arreglo

tam es el tamano en bytes de cada elemento del arreglo

compar es un apuntador a una funcion

la funcion qsort llama a la funcion compar la cual es definida por el usuario para comparar los datos cuando se

ordenen. Observar que qsort conservar su independencia respecto al tipo de dato al dejarle la responsabilidad al

usuario. La funcion compar debe regresar un determinado valor entero de acuerdo al resultado de comparacion

que debe ser:

A continuacion se muestra un ejemplo que ordena un arreglo de caracteres, observar que en la funcion comp,

< si el primer valor es menor que el segundo

0 el primer valor es igual al segundo

> si el primer valor es mayor que el segundo

se hace un cast para forzar el tipo void * al tipo char *.

#include <stdlib.h>

#include <stdio.h>

int comp(const void *i, const void *j);

main()

int i;

char cad[]="Facultad de ciencias fisico-matematicas";

printf("\n\n Arreglo original: \n");

for(i=0; i<strlen(cad); i++)

printf("%c",cad[i]);

qsort(cad, strlen(cad), sizeof(char), comp);

printf("\n\n Arreglo ordenado: \n");

for(i=0; i<strlen(cad); i++)

printf("%c",cad[i]);

printf("\n");

45

Page 55: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

int comp(const void *i, const void *j)

char *a, *b;

a = (char *) i; /*para forzar void * al tipo de char *, se hace cast */

b = (char *) j; /* empleando (char *) */

return *a - *b;

Listado 20

Figura 11.1: Resultado listado 20

bsearch() realiza una busqueda binaria en datos ordenados. Tiene el siguiente prototipo:

void *bsearch(const *clave, const void *comienzo, tamano_t num, tamano_t anchura,

int (*cmp)(const void *, const void *));

clave es un puntero a la clave que se esta buscando.

comienzo es un puntero al principio del array ordenado

num tamano del arreglo

anchura El numero de bytes usado por cada elemento, todos los elementos del mismo tamano

cmp() es un apuntador a una funcion

Si tiene que trabajar solo con datos no ordenados se debe usar la funcion lsearch().

Tiene este prototipo:

void *lsearch(const void *clave, const void *comienzo, tamano_t num,

46

Page 56: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

tamano_t anchura, int (*cmp)(const void *, const void *));

01 #include <stdio.h>

02 #include <stdlib.h>

03 #include <search.h>

04 int cmp(char *, char *);

05 main()

06

07 char s[10]="abcdefghij";

08 int num;

09 num=10;

10

11 if(lsearch("d", s, &num, 1, cmp)) printf("lsearch lo he encontrado \n");

12 if(bsearch("d", s, num, 1, cmp)) printf("bsearch lo he encontrado \n");

13

14 return 0;

15

16 int cmp(char *a, char *b)

17

18 return *a-*b;

19

Listado 21

47

Page 57: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte III

Archivos

48

Page 58: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 12

Archivos

Los archivos son la forma mas comun de los flujos.

Lo primero que se debe hacer es abrir el archivo. La funcion fopen() hace lo siguiente:

FILE *fopen(const char *nomb, const char *modo);

fopen regresa un apuntador a un FILE. En la cadena nomb se pone el nombre y la trayectoria del archivo que

se desea accesar. La cadena modo controla el tipo de acceso. Si un archivo no puede ser accesado por alguna

razon un apuntador NULL es devuelto.

Los modos son:

”r”lectura;

”w”escritura; y

”a”

Para abrir un archivo se debe tener un flujo (apuntador tipo archivo) que apunte a la estructura FILE. Por lo

tanto, para abrir un archivo denominado miarch.dat para lectura haremos algo como lo siguiente;

FILE *flujo; /* Se declara un flujo */

flujo = fopen("miarch.dat","r");

es una buena practica revisar si un archivo se pudo abrir correctamente

if ( (flujo = fopen("miarch.dat","r")) == NULL )

printf("No se pudo abrir %s\n","miarch.dat");

exit(1);

.....

12.1. Lectura y escritura de archivos

Las funciones fprintf y fscanf son comunmente empleadas para accesar archivos.

int fprintf(FILE *flujo, const char *formato, args ... );

int fscanf(FILE *flujo, const char *formato, args ... );

49

Page 59: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Las funciones son similares a printf y scanf excepto que los datos son leıdos desde el flujo, el cual debera ser

abierto con fopen().

El apuntador al flujo es automaticamente incrementado con todas las funciones de lectura y escritura. Por lo

tanto, no se debe preocupar en hacer lo anterior.

char *cadena[80];

FILE *flujo;

if ( (flujo = fopen( ... )) != NULL)

fscanf(flujo,"%s",cadena);

Otras funciones para archivos son:

int getc(FILE *flujo) int fgetc(FILE *flujo)

int putc(char ch, FILE *s) int fputc(char ch, FILE *s)

Estas son parecidas a getchar y putchar.

getc esta definida como una macro del preprocesador en stdio.h. fgetc es una funcion de la biblioteca de C.

Con ambas se consigue el mismo resultado.

Para el volcado de los datos de los ujos a disco, o bien, para disasociar un ujo a un archivo, haciendo previamente

un volcado, usar:

int fflush(FILE *flujo);

int fclose(FILE *flujo);

Tambien se puede tener acceso a los ujos predeterminados con fprintf, etc. Por ejemplo:

fprintf(stderr,"<<No se puede calcular!!\n");

fscanf(stdin,"%s",string);

12.2. sprintf y sscanf

Son parecidas a fprintf y fscanf excepto que escriben/leen una cadena.

int sprintf(char *cadena, char *formato, args ... )

int sscanf(char *cadena, cahr *formato, args ... )

Por ejemplo:

float tanque_lleno = 47.0; /* litros */

float kilometros = 400; char km_por_litro[80];

sprintf( km_por_litro, "Kilometros por litro = %2.3f", kilometros/tanque_lleno);

50

Page 60: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

12.3. Peticion del estado del flujo

Existen unas cuantas funciones utiles para conocer el estado de algun flujo y que tienen los prototipos siguientes:

int feof(FILE *flujo);

int ferror(FILE *flujo);

void clearerr(FILE *flujo);

int fileno(FILE *flujo);

feof() devuelve verdadero si el ujo indicado esta en el fin del archivo. Por lo tanto para leer un ujo, fp,

lınea a lınea se podrıa hacer algo como:

while ( !feof(fp) )

fscanf(fp,"%s",linea);

ferror() inspecciona el indicador de error para el flujo indicado, regresando verdadero si un error ha

ocurrido.

clearerr() limpia los indicadores de fin-de-fichero y error para el flujo indicado.

fileno() examina el argumento flujo y devuelve su descriptor de fichero, como un entero.

12.4. E/S de bajo nivel o sin almacenamiento intermedio

Esta forma de E/S es sin buffer cada requerimiento de lectura/escritura genera un acceso al disco (o dispositivo)

directamente para traer/poner un determinado numero de bytes.

No hay facilidades de formateo ya que se estan manipulando bytes de informacion.

Lo anterior significa que se estan usando archivos binarios (y no de texto).

En vez de manejar apuntadores de archivos, se emplea un manejador de archivo de bajo nivel o descriptor de

archivo, el cual da un entero unico para identificar cada archivo. Para abrir un archivo usar:

int open(char* nomb, int flag);

que regresa un descriptor de archivo o -1 si falla.

flag controla el acceso al archivo y tiene los siguientes macros definidas en fcntl.h:

O_APPEND el archivo se abrira en modo de solo anadir

O_CREAT si el archivo no existe, sera creado.

O_EXCL abrir en forma exclusiva.

O_RDONLY solo lectura

O_RDWR lectura y escritura

O_WRONLY solo escritura

51

Page 61: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

para ver otras opciones usar man. La funcion:

int creat(char* nomb, int perms);

puede tambien ser usada para crear un archivo.

Otras funciones son:

int close(int fd);

int read(int fd, char *buffer, unsigned longitud);

int write(int fd,char *buffer, unsigned longitud);

que pueden ser usadas para cerrar un archivo y leer/escribir un determinado numero de bytes de la

memoria/hacia un archivo en la localidad de memoria indicada por buffer.

La funcion sizeof() es comunmente usada para indicar la longitud.

Las funciones read y write regresan el numero de bytes leıdos/escritos o -1 si fallan.

Se tiene a continuacion dos aplicaciones que usan algunas de las funciones indicadas:

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

main(int argc, char **argv)

float buffer[]=23.34,2.34,1112.33;

int df;

int bytes_esc;

int num_flot;

/* Primeramente se crea el archivo */

if ( (df = open(argv[1], O_CREAT, S_IRUSR | S_IWUSR) ) == -1)

/* Error, archivo no abierto */

perror("Archivo datos, apertura");

exit(1);

else printf("Descriptor de archivo %d\n",df);

/* Despues se abre para solamente escribir */

if ( (df = open(argv[1], O_WRONLY) ) == -1)

/* Error, archivo no abierto */

perror("Archivo datos,apertura");

exit(1);

else printf("Descriptor de archivo %d\n",df);

/* En primer lugar se escribe el numero de flotantes que seran escritos */

num_flot = 3;

if ( (bytes_esc = write(df, &num_flot, sizeof(int)) ) == -1)

/* Error en la escritura */

52

Page 62: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

perror("Archivo datos, escritura");

exit(1);

else printf("Escritos %d bytes\n",bytes_esc);

/* Se escribe el arreglo de flotantes */

if ( (bytes_esc = write(df, buffer, num_flot*sizeof(float)) )== -1)

/* Error en la escritura */

perror("Archivo datos, escritura");

exit(1);

else printf("Escritos %d bytes\n",bytes_esc);

close(df);

Ejemplo de lectura del ejemplo anterior:

/* Este programa lee una lista de flotantes de un archivo binario. */

/* El primer byte del archivo es un entero indicando cuantos */

/* flotantes hay en el archivo. Los flotantes estan despues del */

/* entero, el nombre del archivo se da en la linea de comandos. */

#include <stdio.h>

#include<fcntl.h>

main(int argc, char **argv)

float buffer[1000];

int fd;

int bytes_leidos;

int num_flot;

if ( (fd = open(argv[1], O_RDONLY)) == -1)

/* Error, archivo no abierto */

perror("Archivo datos");

exit(1);

if ( (bytes_leidos = read(fd, &num_flot, sizeof(int))) == -1)

/* Error en la lectura */

exit(1);

if ( num_flot > 999 )

/* arch muy grande */ exit(1);

if ( (bytes_leidos = read(fd, buffer, num_flot*sizeof(float))) == -1)

/* Error en la lectura */

exit(1);

53

Page 63: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte IV

NCURSES

54

Page 64: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte V

llamadas al sistema y gestion de

procesos

55

Page 65: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 13

Funcion system

La funcion system, cuyo prototipo se ofrece mas abajo, ejecuta el comando especificado por string, que puede

incluir opciones y argumentos, pasandolos a /bin/sh -c, que en su momento pasa la lınea decomando entera

(/bin/sh -c string) a la llamada del sistema execve sobre la que leeremos en poco tiempo. Devuelve 127 si

no se encuentra /bin/sh, -1 si se produce otro error o el codigo de retorno de string. Sin embargo, si string

es NULL, system devuelve un valor distinto de 0, o, en otro caso, 0.

#include <stdlib.h>

int system(const char *string);

Ejemplo:

/*system.c */

#include <stdio.h>

#include <stdlib.h>

int main(void)

int retval;

retval=system("ls -l");

if(retval==127)

fprintf(stderr, "/bin/sh no disponible \n");

exit(127);

else if(retval==-1)

perror("system");

exit(EXIT_FAILURE);

else if(retval != 0)

fprintf(stderr,"comando devuelto %d\n",retval);

perror("ls");

56

Page 66: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

else puts("comando ejecutado con exito");

exit(EXIT_SUCCESS);

57

Page 67: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 14

La familia exec

La funcion exec es realmente una familia de seis funciones, cada una con convenciones y usos de llamada

ligereamente diferentes. A pesar de las multiples funciones, convencionalmente se les llama funciones exec. Se

declara en <unistd.h>. Los prototipos son:

int execl (char *ruta, char *arg0, char * arg1,..., char *argn, (char *)0);

int execv (char *ruta, char *argv[]);

int execle (char *ruta, char *arg0, char * arg1,...,char *argn, (char *)0, char *envp[]);

int execve (char *ruta, char *argv[], char *envp[]);

int execlp (char *fichero, char *arg0, char * arg1,..., char *argn, (char*)0);

int execvp (char *fichero, char *argv[]);

Donde:

ruta Es una cadena con el path (absoluto o relativo) de un archivo ejecutable.

fichero Es el nombre de un fichero ejecutable.

arg0, arg1,...,argn Son cadenas de caracteres que constituyen la lista de parametros que se le pasa al

nuevo programa. Por convenio, al menos arg0 esta presente siempre y coincide con ruta o

con el ultimo componente de ruta. Hay que destacar que tras argn se pasa un puntero

NULL Para indicar el final de los argumentos.

argv Es un array de cadenas de caracteres que constituye la lista de argumentos que va a recibir

el nuevo programa. Por convenio, argv debe tener al menos un elemento, que coincide

con ruta o con el ultimo componente de ruta. El

argv Se indica colocando un puntero NULL detras del ultimo parametro.

Ejemplo 1 (execve):

/*execve.c */

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)

char *args[]="/bin/ls", NULL;

58

Page 68: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

if(execve("/bin/ls",args,NULL)==-1)

perror("execve");

exit(EXIT_FAILURE);

puts("no deberiamos estar aqui");

exit(EXIT_SUCCESS);

Ejemplo 2 (execlp):

Figura 14.1: execlp

59

Page 69: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 15

Atributos de proceso

¿Que es exactamente un proceso? Un proceso es una instancia de un programa en ejecucion y tambien la unidad

basica de planificacion de Linux.

15.1. Estados de un proceso

Figura 15.1: Estados de un proceso

15.2. Creacion de un proceso(fork)

La llamada fork crea un nuevo proceso. El nuevo proceso, o proceso hijo sera una copia del proceso que llama,

o proceso padre. La sintaxis es de fork es:

#include <unistd.h>

pid_t fork( );

60

Page 70: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

y la forma de invocarla es pid=fork(). La llamada a fork hace que el proceso actualse duplique. A la salida de

fork, los dos procesos tiene una copia identica del contexto del nivel de usuario excepto el valor de pid, que

para el proceso padre toma el valor del PID del proceso hijo y para el proceso hijo toma erl valor 0, El proceso

0, creado por el nucleo cuando arranca el sistema, es el unico que no se crea con una llamada a fork. Si la

llamada a fork falla, devolvera el valor -1 y en errno estara el codigo de error producido.

Una secuencia de codigo tıpica para manejar la llamada a fork es la siguiente:

int pid;

...

if((pid=fork())==-1)

perror("Error en la llamada a fork");

else if(pid==0)

//Codigo que ejecutara el proceso hijo

else

//Codigo que ejecutara el proceso padre

Si tengo un programa que contiene:

puts("Antes");

creado = fork();

puts("Despues");

Como primer ejemplo de uso de fork veamos un programa que crea un proceso hijo y a continuacion tanto el

padre como el hijo escribiran continuamente en pantalla.

Soy el proceso PADRE

Soy el proceso HIJO

Soy el proceso PADRE

61

Page 71: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Soy el proceso HIJO

...

Programa

#include <sys/types.h>

main()

int i=0;

switch(fork())

case -1:

perror("Error al crear procesos");

exit(-1);

break;

case 0: /*Codigo para el hijo. */

while(i<10)

sleep(1);

printf("\t\t Soy el proceso hijo: %d\n",i++);

break;

default: /*Codigo para el padre */

while(i<10)

printf("Soy el proceso padre: %d\n",i++);

sleep(2);

exit(0);

Cuando llamamos a fork, el nucleo realiza las siguientes operaciones:

1. Busca una entrada libre en la tabla de procesos y la reserva para el proceso hijo.

2. Asigna un identificador de proceso PID para el proceso hijo. Este numero es unico e invariable durante

toda la vida del proceso y es la clave para poder controlar desde otro proceso.

3. Realiza una copia del contexto del nivel de usuario del proceso padre para el proceso hijo. Las secciones

que deben ser compartidas, como el codigo o las zonas de memoria compartida, no se copia, sino que se

incrementan los contadores que indican cuantos procesos comparten esa zonas.

4. Las tablas de control de fichero locales al proceso -como pueden ser la tabla de descriptores de archivos-

tambien se copian del proceso padre al proceso hijo, ya que forman parte del contexto del nivel usuario.

En las tablas globales del nucleo -tabla de ficheros y tabla de inodes- se incrementan los contadores que

indican cuantos procesos tienen abiertos esos ficheros.

5. Rertorna el proceso padre el PID del proceso hijo, y al proceso hijo le devuelve el valor 0.

62

Page 72: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

15.3. Terminacion de procesos (exit y wait)

15.3.1. exit

Una situacion muy tipica en programacion concurrente es que el procerso padre espere a la terminacion del

proceso hijo antes de terminar su ejecucion. Un ejem plo de esta situacion es la forma de operar de los interpretes

de ordenes. Cuando escribimos una orden, el interprete arranca un proceso para ejecurtarla y no devuelve el

control hasta que no se ha ejecutado completamente. Naturalmente, esto no se apllica cuando la orden se jecuta

en segundo plano.

Para sincronizar los procesos padre e hijo se emplean las llamadas exit y wait. La declaracion de exit es la

siguiente:

#include <stdio.h>

void exit(int status);

Esta llamada termina la ejecuacion de un proceso y le devuelve el valor de status al sistema.

La llamada exit tien ademas las siguientes consecuencias:

Las funciones registradas por atexit son invocadas en orden inverso a como fueron resgistradas. atexit

permite indicarle al sistemas las acciones que se deben ejecutar al producirse la terminacion de un proceso.

El contexto del proceso es descargado de memoria, lo que implica que la tabla de descriptores de fichero

es cerrada y sus ficheros asociados cerrados, si no quedan mas procesos que los que tengan abiertos.

Si el proceso padre esta ejecutando una llamada a wait, se le notifica la terminacion de su proceso hijo

y se le envia 8 bits menos significativos de status. Con esta informacion, el proceso padre puede saber en

que condiciones ha terminado el proceso hijo.

Si el proceso padre no esta ejecutando una llamada a wait, el proceso hijo se transforma en un proceso

zombi. Un proceso zombi solo ocupa una entrada en la tabla de proceso del sistema y su contexto es

descargarlo de memoria.

exit es uno de los pocos ejemplos de llamadas que no devuelven ningun valor. Es logico, ya que el proceso que

la llama deja de existir despues de haberla ejecutado.

15.3.2. wait

La declaracion de wait es la siguiente:

#include <sys/types.h>

#include <sys/wait>

pid_t wait(int *stat_loc);

Esta llamada suspende la ejecucion del proceso que la invoca hasta alguno de sus procesos hijo termina. La

forma de invocar a wait es:

63

Page 73: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

pid_t pid;

int estado;

...

pid=wait(&estado);

//Tambien vale

pid =wait(NULL);

Donde:

pid es el identificador de alguno de los procesos hijo zombi.

estado es la variable donde se almacena el valor que el proceso hijo le envia al proceso padre mediante la

llamada exit y que da la condicion de finalizacion del proceso hijo. Si queremos ignoarar este valor, podemos

pasarle a wait un puntero NULL.

Puede ocurrir que el proceso hijo termine de forma anormal. Para estos caso hay definidas, en el fichero

<sys/wait.h>, unas macros que analizan el valor de estado para determinar la causa de terminacion del

proceso. Estos macros son:

WIFEXITED, devuelve VERDAD -cualquier valor distinto de 0- cuando el proceso termina con una llamada a

exit o a _exit.

WEXITSTATUS, si WIFEXITED devuelve VERDAD, esta macro devuelve el valor de los 8 bits menos significativos

que exit le pasa al proceso padre.

WIFSIGNALED, devuelve VERDAD cuando el proceso termina debido a la accion por defecto de alguna senal.

WTERMSIG, si WIFSIGNALED devuelve VERDAD, esta macro devuelve el numerode la senal que ha causado

la terminacion del proceso.

COREDUMP, devuelve VERDAD si ha generado un ficherocon un volcado de la memoria -core image- del

proceso.

WIFSTOPPED, devuelve VERDAD si el proceso esta parado.

WSTOPSIG, si WIFSTOPPED devuelve VERDAD, esta macro devuelve el numero de la senal que ha causado la

parada del proceso.

Como se ha indicado antes, exit y wait se suelen usar para conseguir que el proceso padre espere a la terminacion

de su proceso hijo. Una secuencia de codigo para realizar esta operacion puede ser:

pid_t pid;

int estado;

...

if((pid = fork()) == -1)

//Error en la creacion del proceso hijo

else if(pid == 0)

//Codigo del proceso hijo

64

Page 74: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

exit(10);

else //codigo del proceso padre

pid = wait(&estado);

//cuando el proceso hijo llame a exit,

//le pasara al padre el valor 10,

//que este puede recibir a traves de estado (estado=10)

15.4. Identificadores de proceso

Todo proceso tiene asociado dos numeros desde el momento de creacion: el didentificador del proceso y el

identificador del proceso padre.

El identificador de proceso PID es un nuimero entero positivo que actua a modo de nombre de proceso. El

identificador del proceso padre PIDD, es el PID del proceso que ha creado el actual. El PID de un proceso no

cambia durante el tiempo de vida de este, sin embargo, su PPID si puede variar. Esta situacion se da cuando

el proceso padre muere, pasando el PPID del proceso hijo a tomar el valor 1 (PPID del proceso init).

Para leer los valores de PID y PPID utilizaremos las llamadas getpid y getppid respectivamente:

#include <types.h>

pid_t getpid();

pid_t getppid();

65

Page 75: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

15.5. Ejemplo terminacion normal

/************* toto.c ****************/

main()

int m,n;

if(fork()==0)

printf("Proceso hijo id: %d \n",getpid());

exit(3);/***********/

else

m=wait(&n); /***********/

printf("Fin del proceso %d valor regreso: %d n",m,n);

#gcc toto.c -o toto

#./toto

Proceso hijo id:9227

Fin del proceso 9227 valor regreso:768

#

15.6. Ejemplo de terminacion anormal

/************ toto2.c ****************/

main()

int m,n;

if(fork()==0)

printf("Proceso hijo id: %d \n",getpid());

for(;;);/***********/

else

m=wait(&n); /***********/

printf("Fin del proceso %d valor regreso: %d n",m,n);

#gcc toto2.c -o toto2

66

Page 76: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#./toto2 &

Proceso hijo id:9286

#kill -9 9286

Fin del proceso 9286 valor regreso:9

#./toto2 &

Proceso hijo id:9290

##kill -5 9290

Fin del proceso 9286 valor regreso:133

(se crea un core)

15.7. Ejemplo usando llamadas al sistema

Suponiendo que existe una archivo llamado suma o en caso contrario se crear usando #vi suma, y esta organizado

de la siguiente manera:

1

56

3

10

40

#include <stdio.h>

#include <wait.h>

#include <sys/types.h>

#include <unistd.h>

#include <errno.h>

#include <stdlib.h>

int process_fork(int nproc)

int i;

for(i=1; i<=nproc-1;i++) if(fork()==0) return(i);

return(0);

static char *cmd[]="who","ls","date","ps","uname";

int j,i,pid,status,proc;

int buf; FILE *fp;

main()

j=rand()%5; /* utilizado por el proceso uno*/

pid=process_fork(6);

switch(pid)

67

Page 77: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

case 0: printf("estoy en proceso");

wait(&status);

fprintf(stdout,"\n\n ********* padre con ID=%ld \n\n",getpid());

exit(1);

case 1: fprintf(stdout,"\n soy el proceso 1 con ID=%ld \n\n",getpid());

execlp(cmd[j],cmd[j],0);

exit(0);

case 2: fprintf(stdout,"\n soy el proceso 2 con ID=%ld \n\n",getpid());

system("sort -n suma");

exit(0);

case 3: fprintf(stdout,"\n soy el proceso 3 con ID=%ld \n\n",getpid());

execlp("sort","sort","-n","suma",0);

exit(0);

case 4: fprintf(stdout,"\n soy el proceso 4 con ID=%ld \n\n",getpid());

if((fp=fopen("suma","r"))==NULL)

printf("error al abri el archivo");

exit(0);

else printf("buscando archivo \n");

while(!feof(fp))

fscanf(fp,"%d",&buf);

printf("%d \n",buf);

fclose(fp);

exit(0);

case 5: fprintf(stdout,"\n soy el proceso 5 con ID=%ld \n\n",getpid());

if((fp=fopen("suma","a"))==NULL)

printf("error al abrir el archivo/escritura");

exit(0);

else printf("escribiendo archivo \n");

fprintf(fp,"%d\n",2000);

fclose(fp);

exit(0);

default: printf("no hay tal comando \n");

exit(0);

wait(&status);

68

Page 78: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

El resultado es el siguiente:

Figura 15.2: Procesos y llamadas al sistema

69

Page 79: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte VI

Senales

70

Page 80: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 16

Concepto de senal

Las senales son interrupciones software que pueden ser enviadas a un proceso para informarle de algun evento

asıncrono o situacion especial. El termino senal se emplea tambien para referirse al evento.

Los proceso pueden enviarse senales unos a otros a traves de la llamada kill.

16.1. Como utilizar la funcion Kill

En el fichero de cabecera <signal.h> estan definidas las senales que pueden manejar el sistema. Las senales

del UNIX System V son:

SIGHUP Desconexion. Es enviada cuando una terminal se desconectade todo proceso

de que es terminal de control. Tambien se envia a tocos los procesos de un

grupo cuando el lıder del grupo termina su ejecucion. La acccion por defecto

de esta senal es terminar la ejecucion del proceso que la recibe.

SIGINT Interrupcion. Se envia a todo proceso asociado con una terminal de control

cuando se pulsa la tecla de interrupcion Ctrl+C. Su accion por defecto es

terminar la ejecucion del proceso que la recibe.

SIGQUIT Salir. Similar a SIGINT, pero es generada al pulsar la tecla de salida

Control+\. Su acccion por defecto es generar un fichero core y terminar el

proceso.

SIGKILL Intruccion ilegal. Es enviada cuando el procesador detecta una instruccion

que no forma parte de su repertorio. En los programas escritos en C suele

producirse este tipo de error cuando manejamos punteros a funciones que

no han sido correctamente inicializados. Su acccion por defecto es generar

un fichero core y terminar el proceso.

SIGTRAP Trace trap. Cuando un proceso se esta ejecutando paso a paso, esta senal

es enviada despues de ejecutar cada instruccion. Es empleada por los programas

depuradores. Su accion por defecto es generar un fichero core y

terminar el proceso.

SIGBUS Error de bus. Se produce cuando se da un error de acceso a memoria.

Las dos situaciones tıpicas que la provocan suele ser intentar acceder a

una direccion que fısicamente no existe o intentar acceder a una direccion

impar, violando asi las reglas de alineacion que impone el procesador.

Su accion por defecto es generar un archivo core y terminar el proceso.

71

Page 81: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

SIGKILL Terminacion abrupta. Esta senal provoca irremadiablemente la terminacion

del proceso. No puede ser ignorada y siempre que se recibe se ejecuta.

Su accion por defecto, que consite en generar un archivo core y termina

el proceso

SIGSEGV Violacion de segmento. Es enviada a un proceso cuando intenta acceder

a datos que que se encuentran fuera de su segmento de datos.

Su accion por defecto es generar un fichero core y terminar el proceso.

SIGTERM Finalizacion controlada. Es la senal utilizada para indicarle a un proceso

que debe terminar su ejecucion. Esta senal no es tajante como SIGKILL

y puede ser ignorada. Lo correcto es que la rutina de tratamiento de esta senal

se encargue de tomar las acciones necesarias para dejar al proceso en un estado

coherente y a continuacion finalizar su ejecucion con una llamada a exit.

Esta senal es enviada a todos los proceso cuando se emiten las ordenes shutdown

reeboot. Su acccion por defecto es terminar el proceso.

SIGCLD Terminacion del proceso hijo. Es enviada al proceso padre cuando alguno

de sus procesos hijo termina. Esta senal es ignorada por defecto.

Un proceso se puede terminar con otro uilizando la funcion kill cuyo prototipo es:

#include<sys/types.h>

#include <sys/types.h>

int kill(pid_t pid, int sig);

pid especifica el proceso que queremos eliminar y sig es la senal que queremos enviar. Como esta seccion

especifica la eliminacion de un proceso, la unica senal de la que nos tenemos que preocupar por ahora es SIGKILL.

Con Kill -l podemos ver las posibles senales que se les puede envia a los procesos.

En la siguiente tabla tenemos un resumen de ellas:

Por lo tanto, para eliminar un proceso, primero buscaremos su PID con un comando ps y despues ejecutarıa el

comando kill de la siguiente forma:

$kill -9 PID_del proceso

Si queremos que un proceso se envie una senal a si mismo, podemos usar la llamada raise:

#include <signal.h>

int raise (int sig);

donde sig es el numero de la senal que queremos enviar; raise se puede codificar a partir de kill de la siguiente

forma:

int raise(int sig)

return kill (getpid(), sig);

72

Page 82: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Senal Nombre Capturable Sgnificado Restaura rutina

por defecto

1 SIGHUP Sı Cierre de lınea ?

2 SIGINT Sı Interrupcion ?

3 SIGQUIT Sı Salir ?

5 SIGTRAP Sı Senal de traza de proceso

7 SIGBUS No Error en el bus del sistema ?

9 SIGKILL No Muerte de un proceso ?

11 SIGSEGV No Violacion de segmento de memoria ?

15 SIGTERM Sı Terminacion de un proceso ?

17 SIGCHLD Sı Senal enviada al padre por el hijo cuando este termina ?

18 SIGCLD Sı Senal enviada al padre por ?

el hijo cuando este termina

Tabla 16.2: Senales usadas por Kill

16.2. Tratamiento de senal(signal)

Para especificar que tratamiento debe realizar un proceso al recibir una senal, se emplea la llamada signal:

#include <signal.h>

void (*signal (int sig, void (*action)()))();

La declaracion de signal puede resultar extrana, pero es frecuente en los programas C. Como vemos, signal

es del tipo funcion que devuelve un puntero a una funcion void y recibe dos parametros.

sig es el numero de la senal cuya forma de tratamiento queremos especificar.

action es la accion que se tomara al recibir la senal y puede tomar tres clases de valores:

• SIG_DFL, indica que la accion a realizar cuando se recibe la senal es la accion por defecto asociada

a la senal -manejador por defecto-. Por lo general, esta accion consiste en terminar el proceso y en

algunos casos tambien incluye generar un fichero core.

• SIG_IGN, indica que la senal se debe ignora.

• direccion, es la direccion de la rutina de tratamiento de la senal -manejador suministrado por el

usuario-; la declaracion de esta funcion debe ajustarse al siguiente modelo:

#include <signal.h>

void handler(int sig, int code, struct sigcontext *scp);

73

Page 83: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

16.3. Ejemplos

Ejemplo 1:

En este ejemplo el proceso padre envia una senal al proceso hijo a traves de su pid para su terminacion

#include<signal.h>

main()

int pid;

if((pid = fork()) == 0)

/* codigo del proceso hijo */

while(1)

printf("HIJO PID=%d \n",getpid());

sleep(1);

sleep(10);

printf("PADRE. Terminacion del proceso %d\n",pid);

kill(pid,SIGTERM);

exit(0);

Ejemplo 2:

/* SenalProceso.c */

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

#define TIMEOUT 30

/*** Codigo manejador de se~nales*/

void manejador(int senal)

fprintf(stderr, "Llego: %d\n", senal);

int main(int argc, char *argv[])

/*** Establece los manejadores de se~nales */

signal(SIGINT, manejador);

74

Page 84: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

signal(SIGQUIT, manejador);

signal(SIGTERM, manejador);

/*** Entra en un ciclo infinito esperando ser

* terminado

*/

for (;;)

fprintf(stdout, "Esperando se~nal\n");

sleep(TIMEOUT);

Ejemplo 3:

/*

* Ejemplo de como una proceso puede enviar una se~nal a otro proceso mediante

* kill()

*/

#include <sys/types.h>

#include <signal.h>

#include <unistd.h>

/* Prototipo de la funcion para tratamiento de la se~nal */ void trataSenhal (int);

/* Programa principal.

* Crea un proceso hijo y le envıa una se~nal SIGUSR1 cada segundo.

*/

main()

/* Identificador del proceso hijo */

pid_t idProceso;

/* Se crea el proceso hijo y se comprueba el error */

idProceso = fork();

if (idProceso == -1)

perror ("No se puede lanzar proceso");

exit (-1);

/* Camino que sigue el proceso hijo.

* Pone trataSenhal() para tratar la se~nal SIGUSR1 y se mete en un bucle

* de espera

*/

if (idProceso == 0)

75

Page 85: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

signal (SIGUSR1, trataSenhal);

while (1)

pause ();

/* Camino que sigue el proceso padre.

* Cada segundo envıa una se~nal SIGUSR1 a su proceso hijo.

*/

if (idProceso > 0)

while (1)

sleep (1);

kill (idProceso, SIGUSR1);

/* Funcion de tratamiento de SIGUSR1.

* Escribe en pantalla un aviso de que ha llegado la se~nal.

*/

void trataSenhal (int numeroSenhal)

printf ("Recibida se~nal del padre\n");

Ejemplo 4:

#include <stdio.h>

#include <signal.h>

#include <time.h>

#define MAX 256

int pid_emisor, pid_receptor; void enviar(sig)

char str[MAX];

FILE *fp;

printf("Proceso emisor mensaje : ");

if(gets(str)!=NULL)

if((fp=fopen("buzon","w"))==NULL)

76

Page 86: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

perror("enviar");

kill(pid_receptor,SIGTERM);

fputs(str,fp);

fclose(fp);

signal(SIGUSR1,enviar);

kill(pid_receptor,SIGUSR1);

else

kill(pid_receptor,SIGTERM);

exit(0);

void recibir(sig)

char str[MAX];

FILE *fp;

if((fp=fopen("buzon","r"))==NULL)

perror("enviar");

kill(pid_emisor,SIGTERM);

fgets(str,MAX,fp);

fclose(fp);

printf("PROCESO RECEPTOR MENSAJE : %s\n",str);

signal(SIGUSR1,recibir);

kill(pid_emisor,SIGUSR1);

main(int argc, char *argv[])

//struct tms bt1,bt2;

int estado;

pid_emisor=getpid();

//t1=times(&bt1);

if((pid_receptor=fork())==-1)

perror(argv[0]);

exit(-1);

else if(pid_receptor==0)

/*codigo del proceso hijo */

77

Page 87: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

signal(SIGUSR1,recibir);

while(!0) pause();

else

/*codigo del proceso padre*/

//wait(&estado);

//t2=times(&bt2);

sleep(2);

enviar();

while(!0) pause();

exit(0);

78

Page 88: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte VII

Comunicacion Interprocesos (IPC)

79

Page 89: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 17

Condiciones de competencia

Los procesos requieren con frecuencia la comunicacion entre ellos(IPC). Esto es porque en un sistema los procesos

tienden a compartir recursos de espacio comun de almacenamiento(condiciones de competencia). En algunos

S.O., los proceso que trabajan juntos comparten con frecuencia un espacio en comun para almacenamiento,

principalmente cuando leen y escriben en un espacio compartido que puede estar en la memoria principal o un

archivo.

Por ejemplo.

Supongamos que se desea imprimir un archivo, entonces se ejecuta la instruccion y el kernel manda a un procesos

A a imprimir el archivo, colocando el nombre de este en un directorio spooler, mientras otro proceso verifica

frecuentemente si hay archivos por imprimir, si existe entonces lo imprime y lo elimina. Tambien el spooler

tiene un numero enorme de entradas de peticiones, teniendo dos operaciones basicas en las peticiones:

OUT : Apunta al siguiente archivo a imprimir.

IN : Apunta al la siguiente entrada libre dentro del directorio.

Cabe la posibilidad de que otro proceso B solicite imprimir su archivo a la vez (i.e.) El proceso A y el proceso

B deciden colocar un archivo en la fila de impresiones como se muestra en la figura siguiente.

Figura 17.1: Spooler de impresiones

El proceso A lee IN y almacena su valor en una variable local llamada ”next-free-slot”. Cuando ocurre

una interrupcion de reloj y la CPU decide que el proceso A ya ha estado en ejucucion el tiempo suficiente,

entonces altera con el proceso B. El proceso B tambien lee IN, y obtiene su mismo valor por lo que almacena el

80

Page 90: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

nombre de su archivo en el registro de impresiones(IN ). El porceso A vuelve a ajecutarse a partir del momento

en que abandono y revisa el next-free-slot y sobreescribe el nombre de su archivo con el nombre que tenia

anteriormente escrito en la lista de impresion. y por ultimo incrementa next-free-slot++. Y es por eso que el

deamon de impresion no detecta algo raro en este procedimiento. La consecuencia es que el proceso B nunca

imprime su archivo, porque el proceso A sobreescribio en la peticion del proceso B como se muestra en la figura

17.1.

Definiendo las condiciones de competencia: Cuando dos o mas procesos leen o escriben en ciertos datos

campartidos y el resultado final depende de quien ejecute ¿que? y en ¿que momento?.

Si un proceso utiliza una variable V o un archivo A compartido, los demas procesos no pueden utilizarlo,

segun la definicion 1 evitara las condiciones de competencia y se llama ”Exclusion Mutua”.

Definicion 1 Exclusion Mutua

Cuando dos o mas procesos no pueden entrar a su seccion crıtica al mismo tiempo

Ahora hay que definir lo que es la seccion crıtica .

Definicion 2 La seccion crıtica o region critica

Determina una forma de prohibir que mas de un proceso lea o escriba en los datos compartidos a la vez.

La seccion crıtica (SC) se puede ver como un estado del proceso en el momento que esta usando el recurso

compartido.

Una vez que se sabe que es la exclusion mutua y la seccion crıtica , ahora hay que mencionar que los

procesos no siempre estan compitiendo por la SC , tambien trabajan, en otras palabras, hacen calculos o

procesan informacion que no conducen a las condiciones de competencia, sin embargo otros procesos comparte

recursos o realizan labores crıticas que pueden llevar a conflictos.

Figura 17.2: Visualizacion de dos procesos estan al mismo tiempo en su SC

Se pude observar que los dos procesos entran a su SC al mismo tiempo, mas no se debe confundir con el hecho

de que los procesos utilicen(escribir) el recurso compartido al mismo tiempo, esto no se puede por razones fısicas

en la memoria. Ademas se debe agregar que no se comunican los procesos para poder realizar una sincronizacion

entre ellos.

Para garantizar la exclusion mutua se proponen cuatro condiciones para obtener mejores resultados:

1. Dos procesos no deben encontrarse al mismo tiempo dentro de sus SC.

81

Page 91: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2. No se deben hacer hipotesis sobre la velocidad y numero de CPU.

3. Ningun proceso que este en ejecucion fuera de su seccion crıtica puede bloquear a otros procesos

4. Ningun proceso debe esperar eternamente para entrar a su seccion crıtica

Los metodos que no garantizan correctamente la exclusion mutua son:

Desactivacion de iterrupciones: La forma mas simple de evitar que dos procesos entren a su SC es

desactivando todas sus iterrupciones justo antes de entrar a su SC y activarlas de nuevo una ves qyue salga

de su SC, al desactivar las interrupciones no puede ocurrit una interrupcion de reloj. Este metodo es poco

confiable porque el programador tiene en sus manos el control de activar y desactivar las interrupciones,

ası tambien el problema se agrabia cuando el proceso recive una senal kill, estando en su S.C. Esto

probocarıa que nadie pueda usar el recurso compartido y ademas no cumpliria las condiciones 3 y 4.

Donde el proceso P1 que fue eliminado por una senal no dejaria entrar a los procesos Pi a su SC(regla 4).

Ademas el proceso P1 que no esta en su SC, esta bloqueando a los demas procesos esto se puede ver como

el no cumplimiento de la regla 3. Acontinuacion ilustraremos el caso en la figura 17.3. Donde el proceso

que toma el recurso compartido antra a su SC y desactiva las interrupciones y al salir las vueleve a activar

Figura 17.3: Proceso activando y desactivando interrupciones

Variables de Cerradura: En este metodo se utiliza una variable compartida llamada cerradura. Si se desea

que un proceso entre a su SC primero se hace una prueba de cerradura, i.e se pregunta por el valor de la

variable cerradura, si es cero, entonces el proceso cambia el valor de la variable a uno y entra a su SC.

en caso contrario, si el valor de la cerradura es uno , entoces el proceso espera hasta obtener el valor cero

de nuevo. El problema de este metodo es que se tiene probleas de sincronizacion puesto que tambien la

variable de cerradura es un recurso campartido y no se garantiza la exclusion mutua en esta zona y por

ende falla.

Alternancia estricta. En esta metodo el acceso a la Sc de los procesos es alterno y va pasando uno por uno

utilizando un turno. Al proceso que no le toca se mantiene en espera hasta detectar cuando conmuta la

variable ”turno”y entrar a su SC.

Este chequeo continuo de una variable se denomina espera activa. la incovenecia de este metodo es que

los procesos deben alternarse de forma estricta en el uso de sus SC´s. No es un buen enfoque cuando un

proceso es mucho mas rapido que el otro. Se viola la condicion 3 mencionada: un proceso esta bloqueado

por otro que no esta en su SC. Es decir, si un porceso falla, entonces bloqueara a otro, como se ilustra en

el siguiente codigo.

82

Page 92: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Proceso 1 Proceso 2

(1) while(true) while(true)

(2) while(turn!=0) while(turn!=1)

(3) SC(); SC();

(4) turn=1; turn=0;

(5) Not_SC(); Not_SC();

Algoritmo de Alternancia Estricta

Algoritmo de Peterson: El algoritmo de Peterson consiste en utilizar una variable global llamada ”senal”que

indica el interes de los procesos por entra a su SC y una variabble llamada ”turno”que resuelve los confrictos

de simultaneidad. este algoritmo da una solucion sencilla para la exclusion mutua. En este algoritmo, un

proceso debe de entrar a su SC como un proceso que llama a una funcion ”enter region”con su propio

numero de proceso 0 o 1 como parametro, hasta que pueda entrar(fuerza). Despues de usar la variable

compartida, el proceso llama a otra funcion, llamada ”leave region”para indicar que ha terminado y

permita la entrada de otro proceso. Dichas funciones se pueden contemplar a continuacion.

private boolean ban = true;

private final int n = 2;

int turn, interested[n];

(1) public void enter_region(int process)

(2) Int other;

(3) other =1-process;

(4) interested[process]=ban;

(5) turn=process;

(6) while(turn==process&& intrested[other]==ban);

(7) private void leave_region(int process)

(8) interested[process]=!ban;

Algoritmo de Peterson

En la lınea 6 del codigo 2 ningun proceso esta en su region critica. El proceso P0 llama a la funcion

enter region. Determina el elemento de su arreglo y asigna turn =0. Puesto que el proceso P1 no

esta interesado, regresa inmediatamente, Si P1 llama a enter region, espera hasta que interested[0]=!ban,

este evento sucede solo cuando el P0 llama a leave region. En caso que ambos procesos llaman a enter region

en forma simultanea, ambos tienen su turno con turn.

83

Page 93: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Cuando se llega a la linea (6) WHILE, P0 se ejecuta 0 veces y P1 hace un ciclo y no entra a su region

critica

TSL (Test and set lock). Lee el contenido de una palabra de memoria en un registro para despues almacenar

su valor distinto de cero en una direccion de memoria. Las instrucciones son a bajo nivel y el nemonico

tsl que esta en la lınea 2 del codigo 3 rutina ”Enter region”bloquea un registro de banderas y por ultimo

en la lınea 7, limpia el registro de vanderas para que otro proceso pueda entrar a su SC.

(1) Enter_region:

(2) Tsl register,flag

(3) Cmp register,#0

(4) Jnz enter_region

(5) Ret

(6) Leave_region:

(7) Mov flag,#0

(8) ret

Algoritmo TSL

84

Page 94: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 18

Conceptos basicos IPC

IPC=Interprocess Communication

IPC es un termino generico que describe los metodos que utilizan los procesos para comunicarse los unos

con los otros. Sin IPC, los proceso pueden intercambiar datos u otra informacion solo a traves del sistema de

archivos o, en el caso de los procesos que tiene un ancestro en comun (como la relacion padre-hijo despues de un

fork), a traves de descriptores de archivos heredados. En particular, conoceremos los conductos, FIFO, memoria

compartida, semaforos y colas de mensajes.

Cola de Mensajes Semaforos Memoria Compartida

Archivo cabecera #include <sys/msg.h> <sys/sem.h> <sys/shm.h>

Llamada sistema crear o abrir msgget semget shmget

Llamada para operaciones de control msgctl semctl shmctl

Llamada operaciones IPC msgsnd semop shmat

msgrcv shmdt

Tabla 18.1: Archivos cabecera y llamadas al sistema

18.1. El comando ipcs

ipcs da un reporte del status de las facilidades de comunicacion entre procesos

Sintaxis:

#ipc [-abcmopqsr] [-C corefile] [-N namelist]

Sin opciones la informacion corta para las colas de mensajes, la memoria compartida y los semaforos que se

encuentran activos en el sistema.

Algunas opciones:

85

Page 95: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

-m informacion acerca de segmentos activos de memoria compartida

-s informacion acerca de semaforos activos

-a utiliza todas las opciones de salida

-t imprime informacion de tiempo de la ultima operacion de control que

cambio los permisos de acceso para todas las facilidades

-p muestra informacion de los numeros de procesos

18.1.1. Salida del comando ipcs

Salida del tipo:

T ID KEY MODE OWNER GROUP

Donde:

T Tipo de facilidad:

q cola de mensajes

m memoria compartida

s semaforos

ID identificador para la entrada del facilitador

KEY llave usada como argumento para la creacion acceso de la facilidad

MODE modos de acceso a la facilidad y banderas

Dos primeros caracteres:

R proceso espera un msgrcv

S proceso espera un msgsnd

D segmento de memoria asociado ha sido suprimido

C segmento memoria asociado sera limpiado (cleared)

- la bandera correspondiente no esta activa

Los siguiente nueve caracteres son permisos agrupados en conjunto de tres (propietario, grupo y el resto).

El ultimo caracter generalmente no se usa.

r permiso de lectura

w permiso de escritura

a permiso de alteracion

- permiso no autorizado

OWNER nombre de firma (login) del propietario de la entrada de la facilidad

GROUP nombre del grupo del grupo del propietario de la entrada de la facilidad

86

Page 96: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 18.1: Ejemplo del comando ipcs

18.2. El comando ipcrm

Borra uno o mas mensajes, semaforos o indicadores de memoria compartida

Sintaxis:

ipcrm [-m shmid][-q msqid][-s semid][-M shmkey][-Q msqkey][-S semkey]

Opciones:

-m shmid Borra el segmento de memoria compartido con identificador shmid

-q msgid Borra la cola de mensaje con identificador msgid

-s semid Borra el semaforo con identificador semid

-M shmkey Borra la memoria compartida, creada con la llave shmkey

-Q msqkey Borra la cola de mesajes, creada con la llave msqkey

-S semkey Borra el semaforo, creado con la llave semkey

18.3. Pipes UNIX Semi-duplex

La forma mas simple de IPC en Linux son los pipes o tuberıas, han estado presentes desde los primeros

orıgenes del sitema operativo UNIX y proporcionan un metodo de comunicaciones en un sentido (unidirecional,

semiduplex entre procesos).

Una tuberıa (pipe) es simplemente un metodo de conexion que une la salida estandar de un proceso a la entrada

estandar de otro. Para esto se utilizan “descriptores de archivos”reservados, los cuales en forma general son:

0: entrada estandar (stdin).

87

Page 97: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 18.2: Ejemplo del comando ipcrm

1: salida estandar (stdout).

2: salida de error(stderr).

Este mecanismo es ampliamente usado, incluso en la lınea de coamndos UNIX (en la Shell)

ls | sort | lp

Lo anterior es un ejemplo de “pipeline”, donde se toma la salida de un comando ls como entrada de un

comando sort, quien a su vez entrega su salida en la entrada lp. Los datos corren por la tuberia semi-duplex,

viajando(virtualmente) de izquierda a drecha por la tuberia.

Cuando un proceso crea una tuberia, el kernel instala dos descriptores de archivos para que los use la tuberia.

Un descriptor se usa para permitir un camino de entrada a la tuberia(write), mientras que la otra se usa para

obtener los datos de la tuberia(read). A estas alturas, la tuberia tiene un pequeno uso practico, ya que la

creacion del proceso solo usa tuberias para comunicarse consigo mismo. Se podrıa considerar esta presentacion

de un proceso y el kernel despues de que se haya creado una tuberıa.

En la figura19.1, es facıl ver como se conectan los descriptores. Si el proceso envia datos por la tuberıa

(fildes[0]), tiene la habilidad de obtener (leer) es informacion de fildes[1]. Sin embargo, hay un objetivo

mas amplio sobre el es esquema anterior. Mientras una tuberia conecta inicialmente un proceso a si mismo, los

datos que viajan por la tuberıa se mueven por el kernel.

Veamos como un proceso hijo hereda cualquier descriptor de archivo abierto del padre, ahora tenemos la base

para la comunicacion multiprocesos (entre padre e hijo).

88

Page 98: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 19

Pipe sin nombre

Para crear una tuberia simple con C, se usa la llamada al sistema pipe(). Toma un argumento solo, que es un

arreglo de dos enteros, y si tiene exito, la tabla contendra dos nuevos descriptores de archivos para ser usados

por la tuberia.

Figura 19.1: Comunicacion unidirecional utilizando una tuberia

Un pipe no tiene nombre y, por tanto, solo puede ser utilizado entre los procesos que lo hereden a traves de

la llamada fork(). A continuacion se describen los servicios que permiten crear y acceder a los datos de un pipe.

19.1. Creacion de un ((pipe))

El servicio que permite crear un pipe es el siguiente:

int pipe(int fildes[2]);

Esta llamada devuelve dos descriptores de archivos (vease figura 19.1) que se utilizan como identificadores.

89

Page 99: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

El prime elemento del arreglo fildes (elemento 0) esta fijado para lectura, mientras el segundo entero (elemento

1) esta fijado y abierto para escritura. Visualmente hablando, la salida de fildes[1] se vuelve la entrada

para fildes[0].

Todos los datos que se mueven por la tuberia lo hacen por el kernel. Se debe recordar que un nombre de arreglo

en C es un puntero a su primer elemento miembro. Es decir, fildes es equivalente a &fildes[0]. Una vez

establecida la tuberia, se puede crear(mediante fork) un nuevo proceso hijo.

Si el padre quiere recibir datos del hijo (ver figura 19.2), debe cerrar fildes[1], y el hijo debe cerrar fildes[0].

Si el padre quiere enviarle datos al hijo, debe cerrar fildes[0], y el hijo debe cerrar fildes[1]. como los

descriptores se comparten entre el padre y el hijo, siempre se debe cerrar el extremo de la tuberia que no

interesa, nunca se devolvera EOF si los extremos innecesarios de la tuberia no son explicitamente cerrados. Como

se menciono previamente, una vez que se ha establecido la tuberia, los descriptores de archivos se tratan como

descriptores a archivos normales.

En resumen:

fildes[0], descriptor de archivo que se emplea para leer del pipe.

fildes[1], descriptor de archivo que se utiliza para escribir en el pipe.

La llamada pipe devuelve 0 si fue bien y −1 en caso de error.

19.2. Escritura en un ((pipe))

El servicio para escribir datos en un pipe es el siguiente:

int write(int fd, char *buffer, int n);

Figura 19.2: Tuberia entre dos procesos

90

Page 100: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

19.3. Cierre de un ((pipe))

El cierre de cada uno de los descriptores que devuelve la llamada pipe se consigue mediante el servicio close,

que tambien se emplea para cerrar cualquier archivo. Su prototipo es:

int close(int fd);

El argumento de close indica el descriptor de archivo que se desea cerrar. La llamada devuelve 0 si se ejecuto con

exito. En caso de error, devuelve −1

19.4. Ejemplos

Ejemplo 1:

Este primer ejemplo (bastante inutil), crea, escribe y lee desde un pipe:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>

int main()

int fd[2];

char buffer[30];

if(pipe(fd) == -1) /*pipe() devuelve 0 en caso

de exito o -1 en caso de error */

perror("pipe");

exit(1);

printf("Escribiendo en el descriptor de archivo #%d\n",fd[1]);

write(fd[1],"probando 1 2 3",5);

printf("Leyendo desde el descriptor de archivo #%d\n",fd[0]);

read(fd[0],buffer,5);

printf("Leido \ "%s\"\n",buffer);

En este ejemplo no tiene mucho sentido ya que se esta comunicando consigo mismo.

91

Page 101: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Ejemplo 2:

Veamos que pasa si se llama fork().

El hijo recibe una copıa de todos los descriptores de archivos del padre, incluyendo una copia de los descriptores

de archivos del pipe. Esto permite que el hijo mande datos al extremo de escritura del pipe, y el padre los reciba

del extremo de lectura:

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

int main()

int fd[2];

char buffer[30];

pipe(fd);

if(!fork())

printf("HIJO: Escribiendo \n");

write(fd[1],"hola",5);

printf("HIJO: adiossssssss \n");

exit(0);

else

printf("PADRE: leyendo desde el pipe \n");\

read(fd[0],buffer,5);

printf("PADRE: He leido \"%s\"\n",buffer);

wait(NULL);

En este caso, el padre intento leer desde el pipe antes que el hijo escribiera. Cuando esto ocurre, se dice que el

padre se bloquea, o duerme, hasta que llegan datos que leer. Al parecer el padre intento leer, se durmio, el hijo

escribio y termino, y el padre desperto y leyo el dato.

Veamos un ejemplo mas completo y con mas verificacion de errores.

#include <stdio.h>

#include <unistd.h>

#include <sys/type.h>

int main(void)

92

Page 102: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

int fd[2], nbytes:

pid_t childpid;

char string[] = "Hola a todos! \n";

char readbuffer[80];

pid(fd);

if((childpid = fork()) == -1)

perror("fork");

exit(1);

if(childpid == 0)

/*cierra el descriptor de entrada en el hijo*/

close(fd[0]);

/*enviar el saludo vıa descriptor de salida */

write(fd[1]), string, strlen(string));

exit(0);

else

/*cierre del descriptor de salida en el padre */

close(fd[1]);

/*leer algo de la tuberia... el saludo */

nbytes = read(fd[0], readbuffer, sizeof(readbuffer));

printf("He recibido el string: %s", readbuffer);

return(0);

93

Page 103: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 20

Creacion de tuberias con nombre

(FIFO) First In First Out

Un FIFO tambien se conoce como una tuberia con nombre. El nombre es el de una archivo que multiples proceso

pueden abrir, leer y escribir. Una tuberia con nombre funciona como una tuberia normal, pero tiene algunas

diferencias notables:

Las tuberias con nombre existen en el sistema de archivos como un archivo de dispositivo especial.

Los procesos de diferentes padres pueden compartir datos mediante una tuberia con nombre.

Una vez finalizada toda las operaciones de E/S, la tuberia con nombre permanece en el sistema de archivos

para un uso posterior.

20.1. Creacion de una FIFO

Hay varias formas de crear una tuberia con nombre. Las dos primeras se pueden hacer directamente desde el

shell:

mknod MIFIFO p

mkfifo a=rw MIFIFO

Los dos comandos anterior realizan operaciones identicas, con una excepcion. El comando mkfifo proporciona

una posibilidad de alterar los permisos del archivo FIFO directamente tras la creacion. Con mknod sera necesaria

una llamada al comando chmod.

Los archivos FIFO se pueden identificar rapidamente en un archivo fisico por el identicador “p”que aparece en

la lista del directorio.

$ ls -l MIFIFO

prw-r--r-- 1 root root 0 Dec 14 22:15 MIFIFO|

Tambien hay que observar que la barra vertical (|), simbolo de pipe, esta situada inmediatamente detras del

nombre de archivo.

Para crear un FIFO en C, se puede hacer uso de la llamada del sistema mknod() con sus respectivos argumentos:

94

Page 104: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

mknod("/tmp/MIFIFO", S_IFIFO|0644, 0);

En este caso el archivo “/tmp/MIFIFO”se crea como un archivo FIFO. El segundo argumento es el modo de

creacion, que sirve para indicarle a mknod() que crea un FIFO (con S_IFIFO) y pone los permisos de acceso a

ese archivo al igual como se puede hacer con el comando chmod. Finalmente, el tercer argumento de mknod() se

ignora (al crear un FIFO) salvo que se este creando un archivo de dispositivo. En ese caso, se deberia especificar

los numeros mayores y menor del archivo de dispositivo.

Los permisos se ven afectados por la configuracion de umask de la siguiente forma:

umask_definitiva = permisos_solicitados & ~umask_inicial

Un truco comun es usar la llamada del sitema umask() para borrar temporalmente el valor de umask:

umask(0);

mknod("/tmp/MIFIFO", S_IFIFO|0666, 0).

20.2. Operaciones con FIFOs

Las operaciones E/S sobre un FIFO son esencialmente las mismas que para las tuberias normales, con una gran

excepcion. Se debe usar una llamada al sistema open() o una funcion de librerias para abrir fisicamente una

canal para la tuberia. Con las tuberias semi-duplex, esto es innecesario, ya que la tuberia reside en el kernel y

no en un sistema de archivos fisico. En nuestro ejemplo trataremos la tuberia como un stream, abriendolo con

fopen(), y cerrandose con fclose().

20.3. Ejemplos

Consideremos un proceso servidor simple:

/*******************************

MODULO: fifoserver.c

*******************************/

#include <stdio.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <unistd.h>

#include <linux/stat.h>

#define FIFO_FILE "MIFIFO"

int main(void)

95

Page 105: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

FILE *fp;

char readbuf[80];

/*crea el FIFO si no existe*/

umask(0);

mknod(FIFO_FILE, S_IFIFO|0666, 0);

while(1)

fp = fopen(FIFO_FILE, "r");

fgets(readbuf, 80, fp);

printf("Cadena recibida: %s\n", readbuf);

fclose(fp);

return(0);

Como un FIFO bloquea por defecto, se debe ejecutar el servidor en segundo plano tras compilarlo:

$ fifoserver &

Se discutira la accion de bloqueo de un FIFO mas adelante. Primero consideremos el siguiente proceso cliente:

/*******************************

MODULO: fifoclient.c

*******************************/

#include <stdio.h>

#include <stdlib.h>

#define FIFO_FILE "MIFIFO"

int main(int argc, char *argv[])

FILE *fp;

if(argc != 2)

printf("USO: fifoclient [cadena]\n");

exit(1);

if((fp=fopen(FIFO_FILE,"w")) == NULL)

perror("fopen");

exit(1);

96

Page 106: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

fputs(argv[1],fp);

fclose(fp);

return(0);

20.4. Acciones bloqueantes en un FIFO

Normalmente, el bloqueo ocurre en un FIFO. En otras palabras, si se abre el FIFO para lectura, el proceso

estara “bloqueado”hasta que cualquier otro proceso lo abra para escritura. Esta aacion funciona al reves tambien.

si este comportamiento no nos interesa, se puede usar la bandera O_NONBLOCK en la llamada Open() para

desactivar la accion del bloqueo por defecto.

En el caso del servidor simple, se ha puesto en segundo plano, y permitido hacer su bloqueo alli.

97

Page 107: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 21

Colas de mensajes

Una cola de mensaje funciona como un FIFO, pero con algunas diferencias. Generalmente, los mensajes son

sacados de la cola en el orden en que se pusieron. Sin embargo, hay manera de sacar cierto mensaje de la cola

antes de que alcance llegar al inicio de la cola.

Un proceso puede crear una nueva cola de mensajes, o se puede conectar a una ya existente. De esta forma, dos

procesos pueden compartir informacion mendiante la misma cola de mensajes.

Una vez que se crea una cola de mensajes, esta no desaparece hasta que se destruya. Todos los procesos que

alguna vez se usaron pueden finalizar, pero la cola todavıa existira. Una buena costumbre serıa usar el comando

ipcs para verificar que si existe alguna cola de mensajes que ya no este en uso y destruirla con el comando

ipcrm.

Primero que nada queremos conectarnos a una cola, a crearla si no existe.

21.1. Crear y abrir una cola

Para hacer esto se utiliza la llamada al sistema msgget():

Su prototipo es:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgget(key_t key,int msgflg);

Donde:

msgget() Devuelve el ID de la cola de mensajes en caso de exito, o -1 en caso de error.

key Es un identificador unico que describe la cola a la cual uno se quiere conectar

(o crear). Cualquier otro proceso que quiera conectarse a esta cola debera usar

este mismo key.

msgflg Le dice a msgget() que hacer con la cola. Para crear una cola este campo

debe ser igual a IPC_CREAT junto a los permisos para la cola. (Los permisos

de la cola son los mismos que los permisos estandares de archivos- las colas reciben

los user-id y group-id del programa que los creo).

98

Page 108: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Como crear el identificador key?

1. Ya que el tipo key_t es long, se puede usar cualquier numero que se quiera (siempre y cuando otro

programa no utilize el mismo numero), entonces es recomendable la siguiente alternativa.

2. Usando la funcion ftok() la cual genera un identificador a partir de dos argumentos:

key_t ftok(const char *path, int id);

Donde:

path Debe ser un archivo que el proceso pueda leer.

id Es normalmente un char escogido arbitrariamente, como por ejemplo ‘A’.

Ejemplo:

#include <sys/msg.h>

key=ftok("/home/hola",’b’);

msqid=msgget(key,0666 | IPC_CREAT);

Ejemplo:

/* mkq.c - crea una cola de mensajes */

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[])

int qid; /*identificador de la cola */

key_t key; /*clave de la cola */

key=123;

/*crea la cola */

if((qid=msgget(key, IPC_CREAT |0666))<0)

perror("msgget:create");

exit(EXIT_FAILURE);

99

Page 109: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

printf("creado ID de cola = %d \n ",qid);

exit(EXIT_SUCCESS);

La salida de este programa deberıa paracecer a la siguiente.

#./mkq

creado ID de la cola = 128

#

21.2. Enviar a una cola

Una vez uno se conecta a la cola de mensajes usando msgget(), se puede mandar y recibir mensajes.

Para mandar mensajes:

Cada mensaje consiste en dos partes, las cuales estan definidas en la estructura struct msgbuf, tal como aparece

en sys/msg.h:

struct msgbuf

long mtype;

char mtext[1];

Donde:

mtype Es usado para recibir mensajes, y puede ser cualquier numero positivo.

mtext Es el dato que se anadira a la cola.

Se puede usar cualquier estructura para mandar mensajes a la cola, siempre y cuando el primer elemento es un

long. Por ejemplo, se podrıa usar la siguiente estructura para lamacenar todo tipo de informacion:

struct pirata_msgbuf

long mtype; /*debe ser positivo */

char nombre[30];

char tipo_de_barco;

int crueldad;

;

Una vez definida la estructura, solo resta enviar la informacion a la cola usando msgsnd(): Su prototipo es:

#include <sys/types.h>

100

Page 110: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#include <sys/ipc.h>

#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

Donde:

msqid Es el identificador de la cola de mensajes devuelta por msgget()

msgp Es un puntero a los datos que se quiere poner en la cola

msgsz Es el tamano de los datos que se quieren enviar

msgflg Permite poner parametros adicionales, que se pueden ignorar por el momento en 0.

Este es el codigo que muestra como nuestros datos son anadidos a la cola de mensajes;

/* qsnd.c - envia un mensaje a una cola previamente abierta*/

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#define BUFSZ 512

/* estructura del mensaje*/

struct msg

long msg_type;

char msg_text[BUFSZ];

pmsg; /*puntero a la estructura de mensaje*/

int main(int argc, char *argv[])

int qid; /*identificador de la cola */

int len; /*el tama~no de los datos enviados*/

/*Espera el ID de cola pasado en la linea de comandos*/

if(argc!=2)

puts("USAGE: qsnd <ID de la cola>");

exit(EXIT_FAILURE);

qid=atoi(argv[1]);

/*Obtiene el mensaje a a~nadir a la cola*/

101

Page 111: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

puts("Intoducir el mensajea enviar:");

if((fgets((&pmsg)->msg_text, BUFSZ, stdin)) == NULL)

puts("no hay mensaje para enviar");

exit(EXIT_SUCCESS);

/*asocia el mensaje con este proceso*/

pmsg.msg_type=getpid();

/*a~nade el mensaje a la cola*/

len=strlen(pmsg.msg_text);

if((msgsnd(qid, &pmsg, len, 0))<0)

perror("msgsnd");

exit(EXIT_FAILURE);

puts("mensaje enviado");

exit(EXIT_SUCCESS);

Una ejecucion de ejemplos de este programa tiene como resultado la siguiente salida.

Observese que el programa utiliza el ID de cola devuelto por mkq.

#./qsnd 128

Introduzca el mensaje a enviar

Anadiendo el mensaje al ID de cola 128

Mensaje enviado

#

21.3. Recibir desde la cola

La contraparte de msgsnd() es msgrcv(). Esta serıa la forma de recibir usando la llamada msgrcv().

Su prototipo es:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flags);

Si msgrcv tien exito elimina el mensaje devuelto de la cola. Los argumentos son los mismo que acepta msgsnd

excepto que rellena la estructura con el tipo de mensaje y hasta nbytes de datos. El argumento adicional, type

determina que mensaje se devuelve, como podemos ver en la siguiente lista.

Muchas veces se requiere simplemente recibir el proximo mensaje de la cola, sin importar type. En ese caso, se

dejarıa el parametro type en 0.

102

Page 112: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

type Efecto en msgrcv()

0 Recibe el proximo mensaje de la cola(el de arriba).

> se devuelve el primer mensaje cuyo msg_type sea igual a type.

< se devuelve el primer mensaje cuyo msg_type sea el menor valor o igual que el valor absoluto de type.

El siguiente listado qrd.c lee un mensaje de una cola poblada y creada previamente. La cola de la que leer se

pasa como argumento de la lınea de comandos.

/* qrd.c lee todos los mensajes de una cola de mensajes */

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <stdio.h>

#include <stdlib.h>

#define BUFSZ 512

/*estructura del mensaje*/

struct msg

long msg_type;

char msg_text[BUFSZ];

pmsg; /* una estructura de mensajes*/

int main(int argc, char *argv[])

int qid; /* el identificador de la cola */

int len; /*el tamano del mensaje*/

/* espera el ID de cola pasado en la linea de comandos */

if(argc!=2)

puts("USAGE: qrd <ID de cola>");

exit(EXIT_FAILURE);

qid=atoi(argv[1]);

/*recupera y muestra un mensaje de la cola */

len=msgrcv(qid, &pmsg, BUFSZ, 0,0);

if(len>0)

(&pmsg)->msg_text[len]= ’\0’;

printf("leyendo el ID de la cola: %05d\n",qid);

printf("tipo de mensaje: %05ld\n",(&pmsg)->msg_type);

printf("tamano del mensaje: %d bytes \n",len);

printf("texto del mensaje: %s \n", (&pmsg)->msg_text);

103

Page 113: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

else

perror("msgrcv");

exit(EXIT_FAILURE);

exit(EXIT_SUCCESS);

A continuacion tenemos la salida de una ejecucion de este programa. Como antes, utiliza el ID de la cola creado

por la ejecuacion de mkq anteriormente. Ademas lee el mensaje enviado por el ejemplo anterior qsnd.

#./qrd 128

tipo de mensaje: 06360

tamano del mensaje: 34 bits

texto del mensaje: Anadiendo un mensaje al ID de cola 128.

#

21.4. Como eliminar cola de mensajes

Llega un momento cuando se quiere eliminar una cola de mensajes. Como ya se menciono, las colas de mensaje

quedan a menos que se eliminen explicitamente; es importante hacer esto para no gastar recursos del sistema.

1. Usar el comando de UNIX ipcs para obtener una lista de colas de mensajes definidas, luego usar el

comando ipcrm para eliminar la cola.

2. Escribir el codigo necesario.

Generalmente, es mas recomendada la segunda opcion, ya que se puede desear que el programa limpie la cola

en cierto momento. Para hacer esto se utiliza otra funcion: msgctl().

Su prototipo es:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

Donde:

msqid Es el ID de cola existente.

cmd Puede ser uno de los siguientes:

104

Page 114: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

IPC_RMID. Elimina la cola msqid

IPC_STAT. Rellena buf con la estructura msqid_ds de la cola y nos permite ver su contenido sin eliminar

ningun mensaje.

IPC_SET. Nos permite cambiar el UID, GID, modo de acceso y maximo numero bits permitidos de la cola.

El siguiente listado qctl.c utiliza la llamada msgctl para eliminar una cola cuyo ID se pasa en lınea de

comandos.

/* qctl.c - Elimina una cola de mensajes */

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

int main(int argc, char *argv[])

int qid;

if(argc!=2)

puts("USAGE: qctl <qid>");

exit(EXIT_FAILURE);

qid=atoi(argv[1]);

if(msgctl(qid, IPC_RMID, NULL)) <0

perror("msgctl")

exit(EXIT_FAILURE);

printf("cola %d eliminada \n", qid);

exit(EXIT_SUCCESS);

#ipcs -q

..............

#./qctl 128

cola 128 eliminada

#ipcs -q

.................

#

105

Page 115: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

21.5. Ejemplo

Este programa realiza la suma de dos vectores:

Por ejemplo:

A=[1,2,3,4]

B=[1,1,1,1]

A+B=[2,3,4,5]

/* msg-A+B.c */

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<time.h>

#define tam_buf 10

struct mensaje

long tipo_msj;

int vec[tam_buf];

;

main(int argc,int *argv[])

struct mensaje A,B;

int k,l,i,j;

int id_c,tam,pid,estado,x,arr[10];

srand(time(NULL));

struct mensaje buf_msj;

key_t key;

key=666;

if((id_c=msgget(key,IPC_CREAT|0666))<0)

perror("msgget:create");

exit(-1);

if((id_c==msgget(key,0))<0)

perror("msgget:open");

exit(-1);

106

Page 116: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

pid=fork();

switch(pid)

case -1: perror("fork");

exit(1);

case 0: msgrcv(id_c,&B,10*sizeof(struct mensaje),0,0);

msgrcv(id_c,&A,10*sizeof(struct mensaje),0,0);

printf("\t HIJO ");

printf("\nMENSAJE RECIBIDO: ");

printf("suma de A + B \n");

for(i=0;i<10;i++)printf("%d ", A.vec[i]+B.vec[i]);

printf("\n\n");

exit(1);

default: printf("\n EL PADRE\n");

/* vector A */

for(i=0;i<10;i++) A.vec[i]=rand()%20;

printf("A %d \n",A.vec[k]);

msgsnd(id_c,&A,sizeof(struct mensaje),0);

/* vector B */

for(j=0;j<10;j++) A.vec[j]=rand()%50;

printf("B %d \n",A.vec[k]);

msgsnd(id_c,&A,sizeof(struct mensaje),0);

puts("\nMENSAJES ENVIADOS");

wait(&estado);

msgctl(id_c,IPC_RMID,NULL);

exit(1);

/*fin del swicht*/

/*fin del programa*/

107

Page 117: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

El resultado se muestra en la siguiente figura:

Figura 21.1: suma de dos vectores

108

Page 118: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 22

Semaforos

Los semaforos se pueden utilizar para controlar el acceso a los archivos, memoria compartida, y en general

cualquier otro recurso compartido.

Figura 22.1: Los semaforos

109

Page 119: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

22.1. Caracterısticas de los semaforos

1. Desbloqueando procesos

El semaforo informa a los procesos que se encuentran bloqueados

Todos los procesos se desbloquean, y pasan a estado listo, es el administrador de procesos quien elije quien

pasa a ejecucion. (Dependiendo de la implementacion: FIFO, LIFO, etc).

2. Atomicidad

Se garantiza que al iniciar una operacion con semaforo, ningun otro proceso puede tener acceso al semaforo

hasta que la operacion termine o se bloquee. En este tiempo ningun otro proceso puede simultaneamente

modificar el mismo valor de semaforo.

Figura 22.2: Tipos de semaforos

22.2. Semaforos generales o contadores

Utiles cuando un recurso sera asignado, tomandolo de un conjunto de recurso identicos.

Semaforo es inicializado con el numero de recursos existentes: P(S) decrementa a S en 1; indicando que

un recursos ha sido suprimido del conjunto.

110

Page 120: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Si S=0 entonces no hay mas recursos y el proceso se bloquea

V(S) incrementa a S en 1; que un recurso ha sido regresado al conjunto.

Si un proceso esperaba por un recurso, este despierta.

22.3. Semaforos Binarios I

Solo puede tomar dos valores: 0 o 1

Generalmente se inicializan con un valor de 1.

Son usados por dos o mas procesos para garantizar que solo uno puede entrar en seccion critica.

Antes de entrar en seccion crıtica un proceso ejecuta un P(S) y un V(S) antes de salir de ella.

La variable de tipo semaforo se llama entrar y es inicializada en 1. Cada proceso tiene la estructura

siguiente:

while(1)

P(entrar)

<seccion critica>

V(entrar)

do

se detalla ampliamente en la seccion 22.5.

22.4. Semaforos sincronizadores

Solucion de varios problemas de sincronizacion.

Sean dos procesos concurrente P1 y P2 que se encuentran corriendo:

P1 con enunciado S1

P2 con enunciado S2

Se desea que S2 sea ejecutado despues de que S1 haya terminado

Solucion:

Semaforo sincrono (inicializado en 0)

P1: ———- P2:———-

S1 P(sincro)

V(sincro) S2

—————- ——————

111

Page 121: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

22.5. Semaforos Binarios II

Obtener semaforos con IPC de System V, se obtienen conjuntos de semaforos. Obviamente, se puede obtener

un conjunto de semaforos que solo contiene un semaforo, pero la idea es que se puede tener una gran cantidad

de semaforos al crear un solo conjunto de semaforos.

Figura 22.3: Estructura del semaforo

112

Page 122: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 22.4: Estructura del semaforo

113

Page 123: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 22.5: Relacion estructuras

22.5.1. Peticion de semaforo (semget())

Como se hace para crear un conjunto de semaforos? Se hace con la llamada semget(), que devuelve el id

(identificador) del semaforo (de aquı en adelante sera referido como semid):114

Page 124: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

Donde:

key Es un identificador unico que es usado por procesos diferentes para identificar este conjunto de

semaforos (El key sera generado usando ftok(), descrito en los apuntes sobre las colas

de mensajes).

nsems Es la cantidad de semaforos en este conjunto de semaforos.

semflg Le indica a semget() entre otras cosas; que permisos tendra el nuevo conjunto de semaforos,

si se esta creando un nuevo conjunto o si solo se quiere conectar a un conjunto ya existente. Para

crear un nuevo conjunto, se puede combinar los permisos con el argumento IPC_CREAT.

Aquı hay un ejemplo donde se crea el key con ftok() y luego se crea un conjunto de 10 semaforos, con los

permisos 666 (rw-rw-rw-):

#include <sys/ipc.h>

#include <sys/sem.h>

key_t key;

int semid; key = ftok(".", ’E’);

semid = semget(key, 10, 0666 | IPC_CREAT);

Una vez ejecutado este ejemplo, se puede verificar la existencia del conjunto de semaforos con el comando ipcs.

(No se olviden eliminar el semaforo con el comando ipcrm!)

115

Page 125: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

22.5.2. Operaciones P y V (semop())

Figura 22.6: Definicion de semop()

Todas las operaciones sobre semaforos utilizan la llamada al sistema semop(). Esta llamada al sistema es de

proposito general, y su funcionalidad es dictada por una estructura que se le pasa, struct sembuf:

116

Page 126: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

struct sembuf

ushort sem_num;

short sem_op;

short sem_flg;

;

Donde:

sem_num Es el numero del semaforo en el conjunto que se quiere manipular.

sem_op Es lo que se quiere hacer con ese semaforo. Esto tiene varios significados,

dependiendo si sem_op es positivo, negativo, o cero, tal como se muestra en la tabla 22.5.2:

sem_flg Permite al programa especificar modificadores que modifican mas aun los efectos de una llamada

a semop().

y puede ser uno de los siguiente modificadores:

IPC_NOWAIT, la llamada a sem_op devuelve el control en caso de que no se pueda

satisfacer la operacion especificada en sem_op. La forma de trabajar por defecto es IPC_WAIT

SEM_UNDO este causa que semop() grabe, de cierta forma, el cambio realizado en el semaforo.

Cuando el programa termina su ejecucion, el kernel automaticamente deshace todos los cambios

que fueron marcados con el modificador SEM_UNDO.

sem op accion

> (V) El valor de sem_op es sumado al valor del semaforo y el recurso controlado

por el semaforo es liberado

< (P) El proceso que llama esta indicando que quiere esperar hasta que el recurso controlado

este disponible, en cuyo momento el valor del semaforo disminuira y el recurso sera bloqueado

por el proceso.

0 El proceso que llama se bloqueara hasta que el semaforo sea 0; si ya es 0, la llamada

vuelve inmediatamente

Tabla 22.1: operaciones de sem op

Ası que, basicamente, lo que se hace es cargar un struct sembuf con los valores que se quieran, y luego llamar

a semop(), de esta manera:

int semop(int semid ,struct sembuf *sops, unsigned int nsops);

Donde:

semid Es el numero obtenido de la llamada a semget().

sops Es un puntero a una estructura struct sembuf que ya se haya rellenado

con los comandos de los semaforos. Incluso se puede crear un arreglo de struct sembufs,

para realizar muchas operaciones al mismo tiempo.

nsops Es el total de elementos que tiene el array de operaciones

117

Page 127: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 22.7: operaciones semop().

118

Page 128: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

22.5.3. lock de un semaforo P(S)

/*poniendo un candado al semaforo */

void P(sem,n)

int sem,n;

struct sembuf sop;

/* construccion arreglo operaciones de un elemento */

sop.sem_num=n;

sop.sem_op=-1; /*bloquea solo una unidad */

sop.sem_flg=0;

/*bloquea hasta que el recurso es liberado */

semop(sem,&sop,1);

22.5.4. Unlock de un semaforo V(S)

/*Quitando un candado al semaforo */

void V(sem,n)

int sem,n;

struct sembuf sop;

/* construccion arreglo operaciones de un elemento */

sop.sem_num=n;

sop.sem_op=-1; /* +1 -> desbloquea*/

sop.sem_flg=0;

/*bloquea hasta que el recurso es liberado */

semop(sem,&sop,1);

22.5.5. Control de las estructuras de semaforo (semctl())

Hay dos maneras de deshacerse de un conjunto semaforos: una es usando el comando ipcrm de UNIX. La otra

manera es a traves de la llamada semctl() con los argumentos adecuados.

119

Page 129: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Esta es la union union semun, junto con la llamada a semctl() para destruir el semaforo:

union semun

int val; /* usado solo para SETVAL */

struct semid_ds *buf; /* para IPC_STAT y IPC_SET */

ushort *array; /* usado para GETALL y SETALL */

;

Sintaxis:

int semctl(int semid, int semnum, int cmd, union semun arg);

Donde:

semid El ID del semaforo que se quiere destruir.

semnum Indica cual es el semaforo, de los que hay bajo semid, al que

queremos acceder.

cmd Debe estar en IPC_RMID, lo que le indica a semctl()

que debe sacar este conjunto de semaforos.

semnum arg No tienen ningun significado en el contexto de

IPC_RMID y se puede dejar en cualquier cosa.

Los siguientes valores son validos para cmd:

VALOR DESCRIPCION

GETVAL Se utiliza para leer el valor de un semaforo.

el valor se devuelve a traves del nombre de la funcion

SETVAL Permite inicializar un semaforo a un valor determinado que se

especifica en arg.

GETPID Se usa para leer el PID del ultimo proceso que actuo sobre el

el semaforo. Este valor se devuelve a traves del nombre de la funcion.

GETNCTN Permite leer el numero de procesos que hay esperando a que se incremente el valor

del semaforo. Este numero se devuelve a traves del nombre de la funcion.

GETSCNT Permite leer el numero de procesos que hay esperando a que el semaforo tome el

el valor 0. Este numero se devuelve a traves del nombre de la funcion

GETALL Permite leer el valor de todos los semaforos asociados a identificados

semid. Estos valores se almancenan en arg.

SETALL sirve para inicializar el valor de todos los smaforos asociados al identificados

semid. Los valores de inicializacion debe estar en arg.

IPC_STAT e IPC_SET Permiten leer y modificar la informacion administrada asociada

al identificador semid. Para obtener mas informacion puede consultar el manual

de LINUX.

IPC_RMID Le indica al nucleo que debe borrar el conjunto de semaforos agrupados bajo el

identificador semid. La operacion de borrado no tendra efecto mientras haya

algun proceso que este usando los semaforos.

120

Page 130: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Esto es un ejemplo de como eliminar un semaforo:

union semun dummy;

int semid;

.

.

semid = semget(...);

.

.

semctl(semid, 0, IPC_RMID, dummy);

Cuando recien se crearon los semaforos, estos quedan inicializados en cero. Esto es grave, ya que significa que

todos estan marcados como ocupados; y se necesita otra llamada (ya sea a semop() o a semctl() para marcarlos

como libres).

Que significa todo esto? Esto significa que la creacion de semaforos no es atomica. Si dos procesos estan

tratando de crear, inicializar y usan un semaforo al mismo tiempo, se puede producir una condicion de carrera.

Este problema se puede resolver teniendo un solo proceso que crea e inicializa el semaforo. El proceso principal

solo lo accesa, pero nunca lo crea o lo destruye.

22.6. Ejemplos

Ejemplo 1:

#include <stdio.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

#define SEM_HIJO 0

#define SEM_PADRE 1

main(int argc, char *argv[])

int i=10, semid, pid;

struct sembuf operacion;

key_t llave;

/*Peticion de un identificador con dos semaforos */

llave=ftok(argv[0],’K’);

if ((semid=semget(llave,2,IPC_CREAT|0600))==-1)

perror("semget");

exit(-1);

121

Page 131: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

/*Inicializacion de los semaforos*/

/*Cerramos el semaforo del proceso hijo*/

semctl(semid,SEM_HIJO,SETVAL,0);

/*Abrimos el semaforo del proceso hijo*/

semctl(semid,SEM_PADRE,SETVAL,1);

/*Creacion del proceso hijo*/

if((pid=fork())==-1)

perror("fork");

exit(-1);

else if (pid==0) /*codigo del proceso hijo*/

while(i)

/*cerrramos el semaforo del proceso hijo*/

operacion.sem_num=SEM_HIJO;

operacion.sem_op=-1;

operacion.sem_flg=0;

semop(semid,&operacion,1);

/*abrimos el semaforo del proceso padre*/

printf("PROCESO HIJO: %d \n",i--);

operacion.sem_num=SEM_PADRE;

operacion.sem_op=1;

semop(semid,&operacion,1);

/*borrado del semaforo*/

semctl(semid,0,IPC_RMID,0);

else /*codigo del proceso padre*/

operacion.sem_flg=0;

while(i)

/*cerramos el semaforo del proceso padre*/

operacion.sem_num=SEM_PADRE;

operacion.sem_op=-1;

semop(semid,&operacion,1);

printf("PROCESO PADRE: %d \n", i--);

/*abrimos el semaforo del proceso hijo*/

operacion.sem_num=SEM_HIJO;

operacion.sem_op=1;

semop(semid,&operacion,1);

/*borrado del semaforo*/

122

Page 132: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

semctl(semid,0,IPC_RMID,0);

el resultado es el siguiente:

Figura 22.8: Resultado del ejemplo 1 (semaforos)

Ejemplo 2:

Esta compuesto por tres programas:

1. seminit.c crea e inicializa el semaforo.

2. semdemo.c controla el acceso a un supuesto archivo.

3. semrm.c es usado para destruir el semaforo (esto podrıa realizarse con el comando ipcrm).

La idea es ejecutar seminit.c para crear el semaforo. Se puede usar el comando ipcs desde la lınea de

comandos para verificar que el semaforo existe. Luego ejecutar semdemo.c en un par de ventanas y ver como

ellos interactuan. Finalmente, se debe usar semrm.c para eliminar el semaforo.

Este es el codigo de seminit.c (debe ejecutarse de los primeros!):

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

int main(void)

key_t key;

int semid;

123

Page 133: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

union semun arg;

if ((key = ftok(".", ’J’)) == -1)

perror("ftok");

exit(1);

/* crear un conjunto de semaforos con 1 semaforo: */

if ((semid = semget(key, 1, 0666 | IPC_CREAT)) == -1)

perror("semget"); exit(1);

/* inicializar el semaforo #0 en 1: */

arg.val = 1;

if(semctl(semid, 0, SETVAL, arg) == -1)

perror("semctl");

exit(1);

return 0;

Este es el codigo de semdemo.c:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

int main(void)

key_t key;

int semid;

struct sembufsb = 0, -1, 0; /* para reservar recursos */

if ((key = ftok(".", ’J’)) == -1)

perror("ftok");

exit(1);

/* conectarse al conjunto de semaforos creado por seminit.c: */

if ((semid = semget(key, 1, 0)) == -1)

perror("semget");

124

Page 134: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

exit(1);

printf("Presione ENTER para bloquear: ");

getchar();

printf("Tratando de bloquear...\n");

if (semop(semid, &sb, 1) == -1)

perror("semop");

exit(1);

printf("Bloqueado.\n");

printf("Presione ENTER para desbloquear: ");

getchar();

sb.sem_op = 1;

/* liberar recurso */

if (semop(semid, &sb, 1) == -1)

perror("semop");

exit(1);

printf("Desbloqueado\n"); return 0;

Este es el codigo de semrm.c:

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include<sys/sem.h>

int main(void)

key_t key;

int semid;

union semun arg;

if ((key = ftok(".", ’J’)) == -1)

perror("ftok");

exit(1);

/* conectarse al semaforo creado por seminit.c: */

if ((semid = semget(key, 1, 0)) == -1)

125

Page 135: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

perror("semget");

exit(1);

/* eliminarlo */

if (semctl(semid, 0, IPC_RMID, arg) == -1)

perror("semctl");

exit(1);

return 0;

Los semaforos son muy utiles en una situacion de concurrencia. Sirven para controlar el acceso a un archivo,

pero tambien a otro recurso como por ejemplo la memoria compartida.

Siempre cuando se tienen multiples procesos ejecutandose dentro de una seccion crıtica de codigo, se necesitan

los semaforos.

126

Page 136: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 23

Memoria compartida

La forma mas rapida de comunicar dos procesos es hacer que compartan una zona de memoria. Para enviar

datos de un proceso a otro, solo hay que escribir en memoria y automaticamente estos datos estaran disponibles

para que los lea otro proceso.

Figura 23.1: memoria compartida

La memoria convencional que puede direccionar un proceso a traves de su espacio de direcciones virtuales es

local a ese proceso y cualquier intento de direccionar esa memoria desde otro proceso provocara una violacion

de segmento.

127

Page 137: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Pasos para accesar una memoria compartica

Figura 23.2: pasos para accesar a una memoria compartida

128

Page 138: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

23.1. llamada shmget()

Con shmget obtenemos un identificador con el que podemos realizar futuras llamadas al sistema para controlar

una zona de memoria compartida. su declaracion es:

id=shmget(key,size,flag)

Donde:

int key

llave numerica de identificacion del segmento

int size

tamano del segmento en bytes

int flag

bandera para los derechos del segmento. 0 si el segmento ya esta creado

Numerico Simbolico Descripcion

0400 SHM_R Read by owner

0200 SHM_W Write by owner

0040 SHM_RÀ3 Read by group

0020 SHM_WÀ3 Write by group

0004 SHM_WÀ6 Read by world

0002 SHM_WÀ6 Write by world

IPC_CREAT Special IPC flag

IPC_EXCL Special IPC flag

int id

manejador del segmento

23.2. llamada shmat()

Antes de usar una zona de memoria compartida tenemos que asignarle un espacio de direcciones virtuales de

nuestro proceso. Esta accion se conoce como unirse o atarse al segmento de memoria compartida.

ptr=shmat(id,addr,flag)

Donde:

int id

manejador del proceso, (obtenido a partir de shmget()).

int addr

especificacion de una direccion de mapeo, generalmente cero, (el sistema se ocupa)

int flag

bandera

129

Page 139: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

ptr type *ptr apuntador del tipo de informacion almacenada en el segmento de memoria

Ejemplo

struct info

char *nombre;

int edad;

;

struct info *ptr;

ptr=(struct info *)shmat(id,0,0)

23.3. llamada shmdt()

shmdt(addr)

Desata un segmento cuando el proceso termino de utilizarlo

No borra el segmento de memoria

El segmento deja de estar accesible para el proceso.

Donde:

int *addr

#include algo.h

int id;

struct info *ctrl;

int main()

.

.

.

id=shmget(KEY,SEGSIZE,IPC_CREAT|0666);

ctrl=(struct info *) shmat(id,0,0);

<codigo uso memoria compartida>

shmdt(ctrl);

return 0;

23.4. llamada shmctl()

Con shmctl podremos realizar operaciones de control sobre una zona de memoria previamente creada por un a

llamada a shmget:

130

Page 140: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Sintaxis

int shmctl(shmid,cmd,buf)

Donde:

int shmid

identificador, (manejador), del segmento de memoria compartida.

int cmd

que puede ser alguno de los siguiente:

IPC_STAT

asigna cada uno de los valores de los campos de la estructura de datos asociada con shmid, en la

estructura apuntada por buf

IPC_SET

asigna el valor de los campos:

shm_perm.uid shm_perm.gid shm_perm.mode

en la estructura apuntada por buf

IPC_RMID

borra el identificador del segmento de memoria del sistema especificado por shmid, y destruye el

segmento de memoria y la estructura de4 datos asociados a el.

SHM_LOCK

bloquea el segmento de memoria especificado por el identificador shmid.

SHM_UNLOCK

desbloquea el segmento de memoria especificado por el identificador shmid.

struct shmid ds *buf

estructura en la que se almacena informacion del estatus del segmento de memoria, (no es usada por todas

las opciones).

131

Page 141: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Acceso a memoria compartida

Figura 23.3: parametros de entrada

132

Page 142: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

23.5. Ejemplo

#include <stdlib.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

#include <stdio.h>

#include <errno.h>

#define array_size 1000

extern char *shmat();

void spin_lock_init();

void spin_lock();

void spin_unlock();

int main()

key_t shm_key;

int shmid, semid, pid;

char *shm;

int *A, *addr, *sum;

int partial_sum;

int i;

printf("Before spin lock init \n");

spin_lock_init(&semid);

shm_key=0x567;

shmid=shmget(shm_key,(array_size*sizeof(int)+1),(IPC_CREAT|0600));

if(shmid==-1)

perror("shmget");

exit(1);

shm=shmat(shmid,NULL,0);

if(shm==(char*)-1)

perror("shmat");

exit(1);

133

Page 143: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

addr=(int*)shm;

sum=addr;

addr++;

A=addr;

*sum=0;

for(i=0;i<array_size; i++) *(A+i)=i+1;

printf("Before fork \n");

pid=fork();

if(pid==0)

partial_sum=0;

for(i=0;i<array_size; i=i+2)

partial_sum+=*(A+i);

printf("%d ",i);

else

addr++;

A=addr;

*sum=0;

for(i=0;i<array_size; i++) *(A+i)=i+1;

printf("Before fork \n");

pid=fork();

if(pid==0)

partial_sum=0;

for(i=0;i<array_size; i=i+2) partial_sum+=*(A+i);

else

partial_sum=0;

for(i=1;i<array_size; i=i+2)

partial_sum+=*(A+i);

partial_sum=0;

for(i=1;i<array_size; i=i+2)

partial_sum+=*(A+i);

printf("after fork \n");

134

Page 144: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

spin_lock(&semid);

*sum+=partial_sum;

spin_unlock(&semid);

printf("\n process pid=%d, partial sum=%d \n",pid,partial_sum);

if(pid==0) exit(0);

else wait(0);

printf("\n sum of 1 to %i is %d\n",array_size, *sum);

if(semctl(semid,0,IPC_RMID,1)==-1)

perror("semctl");

exit(1);

if(shmctl(shmid,IPC_RMID,0)==-1)

perror("shmctl");

exit(1);

exit(0);

void spin_lock(int *lok)

struct sembuf sembuffer, *sops;

sops=&sembuffer;

sops->sem_num=0;

sops->sem_op=-1;

sops->sem_flg=0;

if(semop(*lok,sops,1)<0)

perror("semop");

exit(1);

135

Page 145: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

/*end of spin_lock */

void spin_lock_init(int *lok)

int init_sem_value=1;

*lok=semget(IPC_PRIVATE,1,(0600|IPC_CREAT));

if(*lok==-1)

perror("semget");

exit(1);

if(semctl(*lok,0,SETVAL,init_sem_value)<0)

perror("semctl");

exit(1);

/*end of spin_lock_init */

void spin_unlock(int *lok)

struct sembuf sembuffer, *sops;

sops=&sembuffer;

sops->sem_num=0;

sops->sem_op=1;

sops->sem_flg=0;

if(semop(*lok,sops,1)<0)

perror("semop");

exit(1);

/*end of spin_unlock */

136

Page 146: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 23.4: resultado ejemplo memoria compartida

137

Page 147: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 24

Sockets

24.1. Obteniendo informacion: los resolvers y otras llamadas

En la seccion anterior se establecio que para poder utilizar sockets es necesario conocer el

numero IP de una maquina. En varias aplicaciones este tipo de informacion varıa de acuerdo a

la red o a la maquina sobre la cual se este ejecutando la aplicacıon.

Para poder obtener informacion acerca de las diferentes maquinas que componen una red de

computadoras, se cuenta con funciones conocidas con el nombre de resolvers. Los resolvers

tienen como caracterıstica principal el hecho de que no regresan informacion puntual, sino un

apuntador a una estructura que contiene toda la informacion asociada con la entidad relacionada

con el resolver. En la figura 24.1 se muestra el uso de los resolvers asociados para la obtencion

de informacion asociada a un host.

Los resolvers forman parte de NIS (Network Information Service) que se encarga de distribuir

a todas las computadoras de una red local, la informacion contenida en los archivos de con

guracion. La funcion principal de los resolvers es proporcionar, a nivel programacion, un medio

para obtener esta informacion. En lo que concierne a este capıtulo se hara referencia a dos

archivos /etc/hosts y /etc/services. El primero almacena las direcciones, nombres y alias de las

computadoras que forman la red local. El segundo archivo almacena los nombres de los servicios,

sus protocolos, y el numero de puerto a traves del cual se va a proporcionar el servicio.

24.1.1. Obteniendo datos del host

Los nombres de las computadoras que forman una red local estan reunidos en un archivo

llamado /etc/hosts. En ese archivo se encuentran las direcciones de las maquinas, sus nombres

y sus alias. Un ejemplo del contenido de dicho archivo se presenta abajo.

#

#Internet host table

#

127.0.0.1 localhost

148.241.61.15 brasil loghost

138

Page 148: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 24.1: Ejemplo de un resolver

Para poder obtener informacion acerca de los hosts de una red se cuenta con diferentes funciones.

Esta funciones difieren entre ellas por el parametro de entrada a partir del cual se va a buscar

la informacion. La sintaxis de las llamadas es la siguiente:

1. struct hostent *gethostbyaddr(const void *addr, size_t len,

int type);

2. struct hostent *gethostbyname(const char *name);

3. struct hostent *gethostent(void);

La llamada gethostbyaddr() obtiene informacion de un host a partir de una direccion, del tamano

de dicha direccion y del tipo de la direccion) Tambien es posible conseguir informacion a partir

139

Page 149: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

del nombre de la maquina con la llamada gethostbyname(). Si se quiere recorrer toda la base

de datos, registro por registro, se debe de utilizar gethostent() Dicha llamada lee el siguiente

registro en la base de datos.

Como puede constarse todas las llamadas regresan un apuntador a un estructura. Dicha

estructura contiene la informacion acerca de las computadoras de la red local y se ecuentra

organizada de la siguiente forma:

struct hostent

char *h_name; /*nombre oficial de un host */

char **h_aliases; /*lista de alias */

int h_addrtype; /*tipo de direccion del host */

int h_length; /*longitud de la direccion */

char **h_addr_list; /*lista de direcciones del servidor de nombres */

#define h_addr h_addr_list[0] /*direccion para compatibilidad */

;

Es importante no olvidar que los resolvers regresan un apuntador a esta estructura, y que si

se quiere un campo determinado es necesario accederlo directamente. Tambien vale la pena

aclarar que para agregar nuevos campos al archivo /etc/hosts es necesario contar con ciertos

privilegios.

24.1.2. Obteniendo datos de los servicios

Un servicio esta definido por un nombre, un numero de puerto y un protocolo. Existen servicios

(ftp, telnet, portmapper) usados frecuentemente que tienen establecidos por default un numero

de puerto (0-1024) y un protocolo. Dichos servicios se encuentran de nidos en el archivo

/etc/services. En la parte de abajo se muestra un ejemplo del contenido de dicho archivo.

#ident "@(#)services 1.6

#

#Network services, Internet style

tcpmux 1/tcp

echo 7/tcp

discard 9/tcp sink null

discard 9/udp sink null

systat 11/tcp users

daytime 13/tcp

daytime 13/udp

140

Page 150: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

netstat 15/tcp

chargen 19/tcp ttytst source

chargen 19/udp ttytst source

ftp-data 20/tcp

ftp 21/tcp

telnet 23/tcp

smtp 25/tcp mail

time 37/tcp timserver

time 37/udp timserver

domain 53/udp

domain 53/tcp

bootps 67/udp #BOOTP/DHCP server

bootpc 68/udp #BOOTP/DHCP client

hostnames 101/tcp hostname # usually to sri-nic

sunrpc 111/udp rpcbind

sunrpc 111/tcp rpcbind

Las siguientes funciones permiten conocer informacion acerca de un servicio (o de todos si es

necesario) que proporciona una red. La sintaxis de cada una de las funciones es la siguiente:

1. struct servent *getservbyname(const char *name, const char *proto);

2. struct servent *getservbyport(int port, const char *proto);

3. struct servent *getservent(void);

La primera de ellas nos permite conseguir informacion a partir del nombre del nombre del

servicio y del protocolo. La funcion gerservbyport() recibe como parametros un numero de

puerto y un protocolo. Por ultimo la funcion getservent() lee la siguiente entrada de la base de

datos, lo cual nos puede servir para obtener informacion acerca de todos los servicios existentes.

Todas las llamadas regresan un apuntador a la estructura struct servent, la cual cuenta con los

siguientes campos:

struct servent

char *s_name; /* nombre oficial del servicio */

char **s aliases; /* lista de alias */

int s_port; /* numero de puerto */

char *s_proto; /* protocolo a usar */

141

Page 151: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Al igual que el archivo /etc/hosts se requiere de permisos especiales para poder dar de alta un

servicio en el archivo de configuracion /etc/services.

24.1.3. Obteniendo el nombre del host local

Una de las caracterısticas principales de un sistema cliente-servidor es la de portabilidad. Para

lograr esto es necesario que los programas conozcan el nombre de la maquina sobre la cual se

estan ejecutando. Existen cuatro formas de obtener el nombre de la maquina sobre la cual se

esta ejecutando un proceso:

El comando hostname El comando regresa el nombre de la maquina sobre la que se esta

ejecutando;el problema es que es a nivel comando y no de llamada de sistema. Una posible

solucion es utilizar la llamada system() para redireccionar la salida del comando a un

archivo y despues leer el contenido de dicho archivo. El codigo siguiente es un ejemplo de

obtencion de nombre a partir del comando hostname:

#include <stdio.h>

main()

FILE *fd;

char nombre[50];

system("hostname > /tmp/name");

fd=fopen("/tmp/name","r");

fscanf(fd,"%s",nombre);

unlink("/tmp/name");

El archivo /etc/hostname.e01 Este archivo contiene el nombre de la maquina, en realidad

lo que hace el comando anterior es leer el contenido de este archivo. A nivel programa es

posible abrir el archivo y leer su contenido para conocer el nombre de la maquina.

El comando uname Con la opcion -n del comando uname es posible obtener el nombre

de la maquina. El problema es el mismo que en el primer de los casos y la solucion es la

misma redireccionar la salida a un archivo y despues leer su contenido.

La llamada de sistema gethostname(char *name, int namelen) Es la forma mas simple de

obtener el nombre. La llamada regresa el nombre estandar de la maquina sobre la cual

142

Page 152: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

se esta ejecutando. Recibe como parametros el tamano de la variable donde va a regresar

el nombre namelen y la variable donde se va a almacenar el nombre de la computadora,

(name). La llamada regresa 0 si todo salio bien y -1 si hubo algun error. Un ejemplo de

uso se presenta a continuacion:

#include <errno.h>

main()

char nombre[256];

int n=256;

if (gethostname(nombre, n))

printf("Error no. %d \n", errno);

else

printf("El nombre es: %s \n", nombre);

La eleccion entre una y otra dependera de la aplicacion y del sistema en que se este trabajando.

24.1.4. Conviertiendo diferentes formatos de direccion

En una comunicacion es importante que los datos que viajan a traves de la red sean

independientes de la arquitectura sobre la cual se esta ejecutando. Un mensaje esta formado por

una secuencia de bytes la cual representa cierto tipo de informacion, y la forma de interpretar

esos bytes puede diferir entre dos arquitecturas. Para no tener que realizar traducciones entre

todas las arquitecturas existentes, se eligio realizar tan solo dos traducciones del host a red y

de red a host.

Los nombres de estas funciones tiene forma XtoYT(), donde X puede ser ’h’(host, computador)

o ’n’(network, red), indicando el orden original; Y puede ser tambien ’h’o ’n’, indicando el

orden resultado de la traduccion, y T puede ser ’s ’(short, entero corto) o ’l ’(long, entero largo),

indicando el tamano del dato sobre el que se trabaja.

Para llevar a cabo lo anterior se cuenta con las siguientes llamadas:

1. in_addr_t htonl(in_addr_t hostlong); /*host to net - long */

2. in_port_t htons(in_port_t hostshort); /*host to net - short */

3. in_addr_t ntohl(in_addr_t netlong); /*net to host - long */

143

Page 153: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

4. in_port_t ntohs(in_port_t netshort); /*net to host - short*/

Estas funciones convierten cantidades de 16 y 32 bits entre formatos red y host. Las dos primeras

transforman un formato host a un formato red htonl() para enteros largos y htons() para enteros

cortos. Las dos Ultimas convierten de red a host ntohl() se utiliza en el caso de enteros largos

y ntohs() para enteros cortos. Todas reciben como parametro el valor a convertir y regresan

el valor en el formato solicitado. Para poder utilizar estas llamadas se requiere del archivo de

biblioteca #include <arpa/inet.h>

24.2. Comunicacion con sockets

Como se pueden comunicar dos procesos que no tienen parientes comunes?

Para conseguir esto aparece el concepto de conector o socket, dos procesos distintos crean cada

uno su conector.

1. Cada conector esta ligado a una direccion.

2. Un proceso puede enviar informacion, a traves de un socket propio, al socket de

otro proceso, siempre y cuando conozca la direccion asociada (la del otro socket). La

comunicacion se realiza entre una pareja de sockets.

Figura 24.2: conexion con sockets

24.3. Dominios y direcciones

Definimos un dominio de comunicacion como una familia de protocolos que se pueden emplear

para conseguir el intercambio de datos entre sockets.

144

Page 154: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

La estructura general de una familia de direcciones es:

struct sockaddr

u_short sa_family; /*familia direccion*/

char sa_data[4]

;

Un sistema UNIX particular puede ofertar varios dominios, aunque los mas habituales son estos

dos:

1. Dominio UNIX (PF UNIX). Para comunicarser entre procesos dentro de la misma

maquina1. La estructura donde almacena toda la informacion necesaria para el manejo

de este es:

struct sockaddr_un

short sun_family; /* AF_UNIX */

char sun_path[80]; /* Nombre del path */

;

Donde:

sun_family se le debe asignar el campo el valor AF_UNIX

path recibe el path y el nombre del archivo que va a servir de punte de comunicacion

entre el cliente y el servidor.

2. Dominio Internet(PF INET). Para comunicarse entre procesos en dos computadoras

conectadas mediante los protocolos de Internet (TCP-UDP/IP). La estructura que

almacena los datos necesarios para lograr lo anterior es la siguiente:

struct sockaddr_in

short sin_family; /* familia direccion*/

u_short sin_port; /* numero de puerto */

struct in_addr sin_addr; /* direccion */

char sin_zero[8];

;

1PF significa Protocol Family

145

Page 155: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Donde:

sin_family se usa para almacenar el tipo de socket y debe asignarle el valor AF_INET

sin_port almacena el puerto a traves del cual va establecer comunicacion

sin_addr contiene la direccion de la maquina

sin_zero es un campo de relleno con el fin de que las diferentes familias tenga

el mismo tamano

la estructura struct in_addr es de la forma:

struct in_addr

u_long s_addr; /* familia direccion*/

;

Un sockets del dominio PF UNIX no puede dialogar con sockets del dominio PF INET.

Cada familia de protocolos define una serie de convenciones a la hora de especificar los formatos

de las direcciones. Tenemos, por lo tanto, estas dos familias de direcciones:

1. Formato UNIX(AF UNIX). Una direccion de socket es como nombre de fichero(pathname)2.

2. Formato Internet (AF INET). Una direccion de sockets precisa de estos tres campos:

Una direccion de red de la maquina (Direccion IP).

Un protocolo (TCP o UDP) y

Un puerto correspondiente a ese protocolo.

Es importante resaltar que un socket puede ser referenciado desde el exterior solo si su

direccion es conocida. Sin embargo, se puede utilizar un socket local aunque no se conozca

la direccion que tiene.

Senalaremos que las constantes PF INET y AF INET tienen el mismo valor, y lo mismo

ocurre con PF UNIX y AF UNIX.

24.4. Estilos de comunicacion

Orientados a conexion:- Mecanismo que sirven para conseguir un canal para el intercambio

de octenos. Un proceso pone, a su ritmo, bytes en el canal, que recibe de forma fiable y ordenada

2AF significa una Address Family

146

Page 156: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

en el otro extremo, donde hay un proceso que los recoge a su conveniencia.

Ejemplo: pipes y los socketpairs

Comunicacion sin conexion:- tambien denominada comunicacion con datagramas. El

emisor envia mensajes envia un mensaje autonomo que el receptor debe recibir enteros. Por el

camino algun mensajes pueden perderse, retrasarse o llegar desordenados.

La comunicacion entre dos sockets puede realizarse con cualquiera de estas dos formas (aunque

los dos sockets comunicantes deben de hacerse creados con el mismo estilo, ademas de el mismo

dominio). Para ello, al crear un socket se indica el estilo de comunicacion correspondiente:

SOCK STREAM la comunicacion pasa por tres fases:

1. Apertura de conexion

2. Intercambio de datos

3. Cierre de conexion

El intercambio de datos es fiable y orientado a octenos (como los pipes): no hay frontera

de mensajes.

SOCK DGRAM. No hay conexiones. Cada mensaje (o datagrama) es autocontenido. Los

datagramas no se mezclan unos con otros. No hay garantia de entrega: se pueden perder,

estropear o desordenar.

24.5. Protocolos

Un protocolo es un conjunto de reglas, formato de datos y convenciones que es necesario

respetar para conseguir la comunicacion entre dos entidades, ejemplos: IP, TCP, UDP, FTP,

ETC.,

Cuando se crea un socket es necesario indicar cual es el protocolo de trasporte a emplear para

el intercambio de datos que se realizara a traves de el.

24.6. Las llamadas de sistema para la creacion y uso de sockets

Un proceso puede crear un socket utilizando la funcion socket().

24.6.1. Creacion del socket: socket( )

Sintaxis:

147

Page 157: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

Donde:

domain

∗ PF UNIX

∗ PF INET

type (socket)

∗ SOCK STREAM

∗ SOCK DGRAM

∗ SOCK RAW (root)

protocol sirve para especificar el protocolo de comunicacion a emplear, en general este

argumento es 0, indicando que se usa el protocolo por omision.

24.6.2. Escuchando en el puerto listen()

Utilizado por el servidor en protocolos orientados conexion. Una vez que el socket fue asociado

a una direccion es posible ponerse escuchar a traves de el. Ya que el socket esta asociado a una

direccion basta con accedar al socket para atomaticamente acceder a la direccion. La llamada

de sistema para escuchar tiene la sintaxis siguiente:

int listen(int sockfd, int backbg);

Donde:

sockfd Es el socket a traves del cual se va a escuchar

backbg Es el numero maximo de procesos en espera, es decir la longitud de la cola de espera. Generalmente este

parametro se deja con un valor de cinco.

La llamada regresa un 0 si todo sale bien y -1 si hubo algun error.

24.6.3. Aceptando una llamada accept()

En protocolos orientados conexion el servidor debe esperar una peticion de conexion para

despues crear un canal de comunicacion a traves del cual se emiten y reciben mensajes. La

llamada de sistema que crea este canal de comunicacion es accept(). La sintaxis de la llamada

es:

148

Page 158: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

int accept(int sockfd, struct sockaddr *cltaddr, int *addrlen);

Donde:

sockfd Representa al descriptor del socket.

cltaddr Es la direccion del proceso con el que se establecio el canal de comunicacion (el cliente)

addrlen es la longitud de la direccion.

La llamada regresa un descriptor a traves del cual se van a recibir y enviar los mensajes del

cliente. Este descriptor es una copia de las propiedades del descriptor sockfd pasado como

parametro. Si se presenta algun error regresa un valor de -1.

Es una llamada bloqueante desde el punto de vista que el proceso se bloquea hasta que llegue

una demanda de conexion.

24.6.4. Conectandose al puerto connect()

Las dos ultimas llamadas son usadas por el servidor para escuchar demandas de conexion y

crear canalaes de comuniacion. Por el otro lado el cliente cuenta con la llamada connect()

para solicitar una conexion. Esta llamada presenta la sintaxis siguiente:

int connect(int sockfd, struct sockaddr *servaddr, int *addrlen)

Donde:

sockfd Es el descriptor de socket creado a partir de la llamada socket().

servaddr Es la direccion del servidor

addrlen Es el tamano de la direccion.

La llamada regresa 0 si todo salio bien y -1 si no se pudo establecer ninguna conexion.

24.6.5. Recepcion de mensajes: read(), recv() y recvfrom()

Existen tres llamadas que se pueden utilizar para la recepcion de mensajes, y presentan la

siguiente sintaxis:

int read(int fildes, void *buf, size_t nbyte);

int recv(int sockfd, const void *buffer, size_t length, int flags)

ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags,

struct sockaddr *address, size_t *address_len);

149

Page 159: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

La primera es la utilizada para la lectura de archivos, se lee de fildes, nbytes bytes de

informacion y la lectura se alamcena en buf.

La llamada recv() es muy parecida a la anterior, lee un mensaje de longitud length del socket

sockfd, dejando el contenido en la variable buffer, el ultimo parametro es utilizado para

protocolos especiales, pero para nuestras aplicaciones se le asigna un valor de 0.

La ultima llamada, recvfrom() espera por un mensaje de sockfd de tamano length dejando

la lectura en buffer, en la variable address se tiene la informacion del emisor y en address_len

la longitud de esa direccion. El parametro flags es para protocolos especiales, aunque para

las aplicaciones de este capıtulo se le asignara el valor de cero. Si tambien los parametros

address_len y address tienen un valor de cero la llamada se comporta como una llamada

read() .

Todas las llamadas regresan el numero de bytes recibidos si todo salio bien o -1 si hubo algun

error. La eleccion entre una y otra depende del tipo de protocolo que se este usando: conexion

o no conexion. Las dos primeras son utilizadas en protocolos tipo conexion mientras que la

ultima se utiliza en protocolos orientado no conexion.

24.6.6. Enviando mensajes: write(), send() y sendto()

Se cuenta con tres llamadas para el envıo de mensajes, a continuacion se presentan las sintaxis

de estas llamadas

int write(int fildes, void *buf, size_t nbyte);

int send(int sockfd, const void *buffer, size_t length, int flags),

ssize_t sendto(int sockfd, const void *message, size_t length, int flags,

const struct sockaddr *dest_addr, size_t dest_len);

La primera es la utilizada para la escritura de archivos, se lee de fildes, nbytes bytes de

informacion y la lectura se alamcena en buf.

La llamada send() es muy parecida a la anterior, envia un mensaje de longitud length a traves

del socket sockfd, Se asume que el mensaje esta almacenado en la variable buffer el ultimo

parametro es utilizado para protocolos especiales, pero para nuestras aplicaciones se le asigna

un valor de 0.

La ultima llamada, sendto(), envıa el mensaje buffer de tamano length a traves de sockfd.

En la variable address se guarda la informacion del emisor y en address_len la longitud de

150

Page 160: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

esa direccion. El parametro flags es para protocolos especiales, aunque para las aplicaciones

de este capıtulo se le asignara el valor de cero 0. Si tambien se asigna un valor de cero a los

parametros address_len y address la llamada se comporta como una llamada write().

Todas las llamadas regresan el numero de bytes enviados si todo salio bien o -1 si hubo algun

error. Las dos primeras se utilizan para en el caso de protocolos orientados conexion mientras

que la ultima se utiliza en protocolos orientados no conexion.

24.6.7. Cerrando la comunicacion close()

El socket es un descriptor por lo que para cerrarlo se debe de utilizar la llamada de sistema

close() La sintaxis de dicha llamada es:

int close(int fildes)

Donde:

fildes es el descriptor del socket.

Si no se cierra el socket el puerto utilizado para la comunicacion no podra ser utilizado de nuevo

por ningun otro proceso. Incluso si por alguna razon se tiene que interrumpir el programa es

conveniente cerrar el socket antes de abortara ya que de lo contrario no se podra reiniciar el

programa, sobretodo el servidor.

24.7. La comunicacion cliente-servidor con sockets

Todo sistema cliente-servidor consta de dos programas un programa servidor que va a escuchar

las peticiones atenderlas y darles una respuesta. El otro programa (el cliente) se encarga

de enviar la peticion, esperar una respuesta y procesar dicha respuesta. Esto trae como

consecuencia que todo sistema construido a partir de sockets conste de dos partes la del cliente

y la del servidor, el diseno de uno sin el otro no tiene sentido. La figura 24.3 presenta los pasos

a seguir por el cliente y por el servidor para que puedan comunicarse.

24.8. Aspectos a considerar en el diseno de un servidor

El servidor es el encargado de esperar las peticiones, de atenderlas y de enviar una respuesta a

quien las envio. En el caso de que exista mas de un cliente realizando peticiones a un servidor,

este debe de ser capaz de diferenciar el origen de las diferentes peticiones.

En el caso de trabajar con protocolos orientados conexion la solucion es proporcionar a cada

cliente un canal privado de comunicacion a traves del cual se reciban las peticiones y se envıen

151

Page 161: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 24.3: Pasos para crear una conexion con sockets

las respuestas. Por el otro lado, con protocolos orientados no conexion, es necesario identificar al

emisor para poder otorgarle, o negarle, un servicio. Esto ultimo se realiza a traves de llamadas

de sistema como recvfrom() o sendto(). Un problema adicional se presenta cuando alguien

se hace pasar por dicho proceso.

Otro problema que se presenta cuando se tiene mas de un cliente es atender a todos. Existen

tres estrategias a seguir para la atencion de varios clientes: servidor serial, servidor padre y

servidor hijo. A continuacion se vera en mas detalle las caracterısticas de cada una de ellas.

24.8.1. Servidor serial

El servidor serial solo atiende un proceso a la vez. Una vez que termina de atender la peticion

solicitada verifica si tiene o no otra peticion por atender. Las peticiones no atendidas son

formadas en un cola de atencion cuya longitud es defnida por el servidor. En caso de que la

152

Page 162: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

cola se llene las peticiones, y lleguen mas estas seran rechazadas. El algoritmo de un servidor

serial es el siguiente:

while (true)

begin

escuchar puerto por peticiones

crear canal privado de comunicacion bidireccional

leer peticion cliente

atender peticion

responder al cliente

cerrar el canal de comunicacion

end

24.8.2. Servidor padre

El servidor padre tiene por objeto evitar que la cola se llene y que sean rechazadas las peticiones.

Este servidor crea un hijo, a traves de la llamada fork() que atiende cada una de las peticiones.

Es importante remarcar que el padre crea un canal de comunicacion con el cliente y lo hereda

a su hijo. Una vez heredado el canal, el padre regresa a escuchar el puerto por si llegan nuevas

peticiones.

while (true)

begin

escuchar puerto por peticiones

crear canal privado de comunicacion bidireccional

if (fork()== 0) then

begin

leer peticion cliente

atender peticion

responder al cliente

cerrar el canal de comunicacion

end

end

Este tipo de estrategia ejemplifica la utilidad que tiene el hecho de que los procesos vean el

socket como un descriptor de archivos.

153

Page 163: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

24.8.3. Aspectos a considerar en el diseno de un cliente

A diferencia de un servidor, no existen diferentes estrategıas para lo que es el diseno de un

cliente. Debido a la naturaleza del modelo cliente/servidor, el cliente solo se tiene que preocupar

por enviar su peticion y aguardar por la respuesta.

Existen dos aspectos principales a cuidar en el diseno de un cliente: el identificar de quien es

el mensaje que llega y que hacer en caso de que el servidor o la lınea de comunicacion no

funcionen.

Para identifıcar al emisor se deben utilizar las mismas llamadas que se mencionaron en la

seccion anterior, recvfrom() y sendto(). Las consecuencias de estar esperando una respuesta de

un servidor caıdo es que el cliente se quede un tiempo indefınido en espera. Esto se soluciona

con la implementacion de un time-out. La construccion de un time-out se base en la llamada

de sistema signal() el enunciado goto del lenguaje C y la llamada de sistema alarm().

El principio es el siguiente: antes de enviar la peticion se debe de activar la recepcion de la

senal SIGALARM, la accion a realizar cuando llegue dicha senal podrıa ser el desplegar que se

esta esperando una respuesta. El siguiente paso es enviar la peticion y activar un despertador

alarm() por el tiempo que se quiera esperar. Despues de pasado ese tiempo si no ha llegado

respuesta alguna, el proceso recibira SIGALARM ejecutara el procedimiento asociado y podra

abortat o reenviar el mensaje. El pseudocodigo de lo anterior se presenta a continuacion:

signal(SIGALARM, nada);

e: envia(peticion)

alarm(t)

esperando(respuesta)

si (llega(SIGALARM)) entonces

ejecuta(nada);

goto e;

24.9. Comunicacion con SOCKETPAIRS

Es un mecanismo de comunicacion entre procesos muy similar a los pipes, con la particularidad

que son bidirecionales. Comparte sin embargo, la caracterıstica de se utilizables solo por procesos

con un ancestro en comun.

Las secciones realizadas por cada proceso se resumen en la siguiente tabla 24.1:

/* Ejemplo: Ejsp.c */

#include <sys/types.h>

#include <sys/socket.h>

154

Page 164: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

PADRE HIJO

Crear un socketpair

Crear el hijo

Leer un mensaje Escribir un mensaje

Escribir un mensaje Leer un mensaje

Terminar Terminar

Tabla 24.1: Secciones de procesos

#include <stdio.h>

#define DATA1 "El rey ordeno a su visir..."

#define DATA2 "TENIA EL NOMBRE DE BELISA..."

main()

int n,sockets[2], child;

char buf[1024];

if(socketpair(PF_UNIX,SOCK_STREAM,0,sockets)<0)

perror("abriendo el par de sockets\n");

exit(1);

if ((child=fork())==-1) perror("creando el proceso hijo\n");

else if(child) //este es el padre

close(sockets[0]);

if(read(sockets[1],buf,1024)<0)

perror("padre leyendo el mensaje \n");

printf("ID padre = %d --> %s\n",getpid(),buf);

if(write(sockets[1],DATA2,strlen(DATA2)+1)<0)

perror("padre escribiendo el mensaje");

close(sockets[1]);

exit(1);

else /*este es el hijo */

close(sockets[1]);

if(write(sockets[0],DATA1,strlen(DATA1)+1)<0)

perror("hijo escribiendo mensaje");

wait(&n);

155

Page 165: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

if(read(sockets[0],buf,1024)<0) perror("hijo leyedo mensaje\n");

printf("ID hijo = %d --> %s\n",getpid(),buf);

close(sockets[0]);

24.10. Comunicacion orientada conexion

En este modo de comunicacion es necesario que el cliente le solicite al servidor una conexion.

Una vez establecida dicha conexion el cliente y el servidor pueden empezar a intercambiar

mensajes. Generalmente es el cliente el que emite el primer mensaje pero esto hecho no se

puede generalizar. En la figura 24.4 se presenta un esquema general de los pasos requeridos

para poder comunicar dos procesos en modo conexion.

24.10.1. El servidor

El servidor debe de crear un socket, y asociarlo a un puerto. Una vez asociado el socket al

puerto el servidor debe de esperar por peticiones de conexion al puerto asociado. Cuando la

peticion llegue se debe de crear un canal de comunicacion que sirva de puente de comunicacion.

Una vez establecido el canal, se puede empezar a enviar y a recibir mensajes. Al final el servidor

debe de cerrar el socket para que el puerto pueda ser utilizado por otros procesos. Mientras el

servidor este usando el puerto ningun otro proceso podra hacer uso de el.

La atencion de la peticion depende del servicio solicitado. En la mayor parte de los casos, se

trata de una simple llamada a una funcion que implementa el servicio.

Es logico que si el cliente esta intentanto conectarse con el servidor en un determinado puerto

y el servidor esta escuchando otro puerto, la conexion no podra llevarse a cabo.

1. Creacion del socket

2. Asociando el socket al puerto

3. Escuchando en el puerto

4. Aceptando una peticion

5. Recibiendo la peticion

6. Atendiendo la peticion

7. Enviando la respuesta

8. Cerrando la conexionn

156

Page 166: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Figura 24.4: Pasos a seguir para establecer una comunicacion en modo conexion

24.10.2. El cliente

Un proceso debe de crear un socket, asociarlo a un numero de puerto, el cual varıa de acuerdo a

la aplicacion, solicitar una conexion al servidor. Una vez establecida la conexion el cliente puede

empezar a emitir/recibir mensajes. Por ultimo debe de cerrar el socket, lo que provoca el cierre

del canal de comunicacion. A continuacion se describen los llamadas de sistema necesarias para

llevar a cabo lo anterior, puede compararse esta lista con lo descritos en laa figuras: 24.4 y 24.3.

1. Creacion del socket

2. Asociacion del socket a un puerto

3. Conectarse al servidor

4. Envıo de la peticion

5. Recepcion de la respuesta

6. Cerrando la comuniacion

157

Page 167: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

24.11. Comunicacion orientada no conexion

A diferencia de la comunicacion orientada conexion la comunicacion orientada no conexion no

necesita establecer un canal de comunicacion entre las dos entidades comunicantes. Tan solo

es necesario que el cliente envie su peticion, que el servidor la reciba, la atienda y en caso

necesario, envie la respuesta.

Vale la pena aclarar que en esta comunicacion los mensajes contienen la direccion del

destinatario, y pueden perderse, lo cual lo hace poco confıable:

En la figura 24.5 se presenta un esquema general de los pasos necesarios para poder establecer

una comunicacion orientada no conexion entre el cliente y servidor:

Figura 24.5: Pasos a seguir para establecer una comunicacion en modo no conexion

24.11.1. El servidor

Los pasos a seguir por un servidor para atender las peticiones de los clientes difieren de aquellos

utilizados en modo conexion. No es necesario que el servidor espere solicitudes de conexion y

cree un canal de comunicacion para poder empezar a recibir y enviar mensajes. El servidor

debe de crear un socket asociarlo a un puerto y ponerse a esperar que las peticiones lleguen

para despues antenderlas y enviar la respuesta si esta es necesaria.

158

Page 168: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Por el lado del servidor este debe de realizar los siguientes pasos:

1. Creacion de un socket

2. Asociar el socket a un puerto

3. Esperar por un mensaje

4. Atencion de la peticion

5. Envıo de la respuesta

6. Cerrar la comunicacion

Como se puede apreciar el codigo del servidor es mas reducido en el caso del modo no conexion

que el del modo conexion.

24.11.2. El cliente

Los pasos que un cliente debe de realizar para poder establecer una comunicacion con el servidor

en un modo no conexion no difieren mucho de los realizados en modo conexiion. La principal

diferencia radica en que no es necesario enviar una peticion de conexion. En efecto despues

de crear el socket y asociarlo a un puerto, el cliente envia directamente al cliente su mensaje.

Dependiendo de la aplicacion puede esperar por una respuesta o ponerse a hacer otra cosa. Las

llamadas de sistema necesarias para que el cliente se comunique con el servidor son las siguientes:

1. Crear un socket

2. Asociar el socket a un numero de puerto

3. Enviar la respuesta

4. Esperar la respuesta

5. Cerrar la comunicacion

Siguiendo los pasos anteriores el codigo del cliente no puede asegurar que el mensaje llegue al

servidor, ni detectar que el servidor esta caıdo o que la vıa de comunicacion se rompio. Con el

finn de evitar esperas infinitas por parte del cliente se sugiere implementar un time-out para

volver enviar el mensaje o darse por vencido e intentarlo despues si ası se desea.

159

Page 169: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

24.12. Ejemplo sobre datagrama en el dominio UNIX

Nota: Primero ejecutamos el receptor, despues el emisor como se muestra en la figura 24.6

Extremo receptor

/* recep-inter.c */

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/un.h>

#include <stdio.h>

#define NAME "socketdir"

main()

int sock, length, lon2;

struct sockaddr_un name;

char buf[1024];

sock=socket(PF_UNIX, SOCK_DGRAM,0);

if(sock<0)

perror("abriendo socket de datagramas");

exit(1);

name.sun_family=AF_UNIX;

strcpy(name.sun_path,NAME);

if(bind(sock,(struct sockaddr *)&name,sizeof(name))<0)

perror("asociado con nombre al socket");

exit(1);

printf("Direccion del socket -> %s \n",NAME);

if(recvfrom(sock,buf,1024,0,NULL,&lon2)<0)

perror("recibiendo un datagrama");

printf("--> %s\n",buf);

close(sock);

unlink(NAME);

160

Page 170: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

exit(0);

Extremo emisor

/* emisor-inter.c */

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/un.h>

#include <stdio.h>

#define DATA "holaaaaa como esta alla"

main(int argc, char *argv [])

int sock;

struct sockaddr_un name;

sock=socket(PF_UNIX, SOCK_DGRAM,0);

if(sock<0)

perror("Abriendo socket de datagrama");

exit(1);

name.sun_family=AF_UNIX;

strcpy(name.sun_path,argv[1]);

if(sendto(sock,DATA,strlen(DATA)+1,0,

(struct sockaddr *)&name,sizeof name)<0) perror("enviando un datagrama");

close(sock);

exit(0);

161

Page 171: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

El resultado es el siguiente:

Figura 24.6: resultado emisor-receptor sockets

162

Page 172: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

24.13. Ejemplo sobre datagrama en el dominio INTERNET

Una vez que hemos visto como realizar una palicacion con sockets de datagramas en el dominio

UNIX, vamos aver una aplicacion equivalente en el dominio de Internet. Veremos que la logica

del programa no depende del dominio de comunicacion. Los cambios fundamentales radican

en la gestion de las direcciones asociadas a los sockets. en el espacio de direcciones AF UNIX

las direcciones se alamacena en estructuras del tipo ( struct sockaddr_un). En el espacio

AF INET las estructuras son del tipo (struct sockaddr_in). Las funciones que necesitan

direcciones (bind(), send(), recvfrom() y otras) aceptan cualquiera de estos dos formatos siempre

que se realice un cast a la estructura generica (struct sockaddr).

24.13.1. Receptor

Observese como se crea un socket con el estilo SOCK DGRAM en el dominio PF INET.

/* recinetd.c */

/* Internet. Extremo receptor */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

main()

int sock, length, lon2;

struct sockaddr_in name;

char buf[1024];

sock=sock(PF_INET, SOCK_DGRAM,O);

if(sock<0)

perror("abriendo socket de detagrama");

exit(1);

name.sin_family=AF_INET;

name.sin.addr.s_addr=htonl(INADDR_ANY);

name.sin_port=htons(0);

if(bind(sock, (struct sockaddr *)name, sizeof name)<0)

perror("asociando nombre al socket");

exit(1);

163

Page 173: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

length=sizeof name;

if(getsockname(sock,(struct sockaddr *)&name,&length)<0)

perror("averiguando el nombre del socket");

exit(1);

printf("puerto del socket --> %d \n", ntohs(name.sin_port));

if(recvfrom(sock,buf,1024,0,(struct sockaddr *)NULL, &lon2)<0)

perror("recibiendo un datagrama");

printf("--> %s \n",buf);

close(sock);

exit(0);

De este programa es importante fijarse en la parte en la que se manipula la estructura name,

de tipo sockaddr in, que contiene la direccion del receptor. Hay tres setencias en las que se

rellenan los tres campos de interes de la estructura:

name.sin family=AF INET;

name.sin addr.s addr=htonl(INADDR ANY);

name.sin port=htons(0);

La primera sentencia rellena el campo correspondiente al formato de la direccion. La

segunda rellena la direccion IP a la que se va asociar el socket. La tercera rellena el puerto

correspondiente. En los dos ultimos campos se introducen, en vez de valores concretos, unos

valores especiales con el siguiente significado:

Poner INADDR ANY en la direccion IP significa que, cuando se utilice bind()para asociar

una direccion a un socket, la direccion IP que se va a emplear es la correspondiente a la

maquina en la que se este ejecutando el programa. Es una forma de indicar ”la direccion

IP de esta maquina”.

Poner un cero(0) en el puerto significa que, cuando se utilice bind(), el puerto que se va

asociar al sockets es uno cualquiera libre asignado por el sistema operativo.

Por lo tanto, la estructura name no define por completo la direccion que se va a asociar al

socket. Los valores finales no lo sabremos hasta despues de la ejecucion de bind(). Por ese

motivo seutiliza la funcion getsockname(): para saber que direccion nos ha correspondido.De

164

Page 174: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

aqui lo unico que nos interesa es el puerto, que se imprime por pantalla (una vez convertido al

orden de la maquina con ntohs()). si se imprime es para que, al poner en marcha el emisor, le

podamos indicar exactamente el puerto en el que el receptor esta esperando datos.

El prototipo de getsockname() es:

#include <sys/types.h>

#include <sys/socket.h>

int getsockname(int s, struct sockaddr *name, int *namelen);

Donde:

sock la direccion que queremos conocer

name aqui es donde queda la direccion el tipo puede ser sockadd in o sockaddr un,

siempre que se utilice el cast adecuado.

namelen indica el espacio reservado para contener la estructura con la direccion, y se actualiza

con el tamano real ocupado por dicha estructura.

24.13.2. Emisor

Como pasaba con el receptor, el programa emisor en el dominio Internet es muy similar a su

equivalente en el dominio de UNIX, estando la complejidad en la gestion de direcciones.

/* eminetd.c */

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/in.h>

#include <setdb.h>

#include <stdio.h>

#define DATA "Este es el mensaje..."

main(int argc, char *argv [])

int sock;

struct sockaddr_in name;

struct hostent *hp, *gethostbyname();

sock=socket(PF_INET, SOCK_DGRAM,0);

165

Page 175: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

if(sock<0)

perror("Abriendo socket de datagrama");

exit(1);

hp=gethostbyname(argv[1]);

if(hp==0)

fprintf(stderr, %s: host desconocido",argv[1]);

exit(2);

memcpy((char *)&name.sin_addr, (char *)hp->h_addr,hp->h_length);

name.sun_family=AF_INET;

name.sin_port=htons(atoi(argv[2]));

if(sendto(sock,DATA,strlen(DATA)+1,0,(struct sockaddr *)&name,sizeof name)<0)

perror("enviado un data grama");

close(sock);

exit(0);

El emisor consigue la direccion del receptor a partir de dos argumentos facilitados en la lınea

de comandos:

1. El nombre de la maquina receptora (argv[1])

2. El puerto en el que espera el receptor(argv[2])

E nombre de la maquina se convierte en direccion IP utilizando la funcion gethostbyname().

Una vez hecho esto, se procede a rellenar la estructura name con los datos exactos del receptor.

Aquı no se puede utilizar comodines: todos los campos tienen valores perfectasmente definidos.

El emisor no se molesta en asignar direccion a su socket. Esto no quiere decir que no la tenga.

El sistema operativo comprueba,cuando se envıa informaciona traves de un socket, si tiene o

no direccion asociada con bind().Si no es ası, se asigna automaticamente: la direccion IP es de

la maquina local, y el puerto es uno cualquiera libre.

24.13.3. Ejecucion del ejemplo

1. Crear los ejecutables correspondietes de emisor(eminetd) y receptor(recinetd)

166

Page 176: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2. Lanzar tarea como de fondo (recinetd &). Debera escribir por pantalla el string “puerto

del socket –>” y un numero, sea n.

3. Lanzar despues el emisor, facilitandose el nombre del computador donde esta el receptor

y el numero de puerto asignado a su socket (eminetd maquina receptora n). El receptor

deberıa escribir “–> Este es el mensaje ...”. Tras ello ambos programas terminan.

24.14. Conexion en el dominio INTERNET

En esta seccion vamos a ver, por medio de un ejemplo, como realizar comunicacion orientada a

conexion en el dominio Internet. no vemos ningun ejemplo en el dominio UNIX porque todo lo

que expongamos en el contexto del dominio Internet tambien sera aplicable al dominio UNIX,

a exepcion de lo que se refiere a los formatos de las direcciones.

El ejemplo consta de dos programas, a los que llamaremos cliente y servidor. El cliente es el

que inicia la conexion(TCP) de forma activa, mientras que el servidor espera en su puerto a

que le llamen, abriendo las conexiones de forma pasiva. Una vez establecida una comunicacion

entre el cliente y el servidor, la conexion es bidireccional: cualquiera puede leer y escribir por

ella. En este caso, el cliente se limitaa enviar una mensaje, y el servidor a escribirlo. Pasamos

a describir con algo mas de detalle cada uno de los programas.

24.14.1. Cliente

El programa cliente sigue el siguiente esquema:

Crea un socket en el dominio PF_INET, con el estilo SOCK_STREAM.

Calcula la direccion del servidor, usando para ello la lınea de comandos (argv[1] contiene

el nombre de la maquina servidora) y un puerto conocido a priori.

Se conecta, de forma activa, con el servidor

Envıa un mensaje

Cierra el socket y, por lo tanto, la conexion.

/* clinetc.c */

#include <sys/type.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <stdio.h>

#define SERV_ADDR (IPPORT RESERVED + 1)

167

Page 177: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#define DATA "##--##--##----***----##--##--##"

main(int argc, char *argv[])

int sock;

struct sockaddr_in server;

struct hostent *hp, *gethostbyname();

if(argc <2)

printf("Uso: %s nombre_host \n",argv[0]);

exit(1);

/* creamos un socket orientado a conexion */

sock=socket(PF_INET, SOCK_STREAM, 0 );

if(sock<0)

perror("No se ha podido conseguir un socket");

exit(1);

/* Nos conectamos con el socket de escucha */

/* del servidor. Lo conseguimos claculando */

/* primero su direccion, a partir del nombre */

/* del computador y del numero de puerto */

server.sin_family=AF_INET;

hp=gethostbyname(argv[1]);

if(hp==0)

fprintf(stderr, "%s: No conozco ese computador \n",argv[1]);

exit(2);

memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);

server.sin_port=htons(SERV_ADDR);

if(connect(sock,(struct sockaddr *)&server,sizeof server)<0)

perror("Conexion no aceptada!!!");

exit(1);

if(write(sock,DATA,strlen(DATA)+1)<0)

168

Page 178: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

perror("No he podido escribir el mensaje");

close(sock);

exit(0);

Como primer punto a destacar tenemos el uso de la constante SERV_ADDR con el numero de

puerto del servidor. No es necesario, como en el caso anterior, confiar en que nos los dan

como parametro. El inconveniente radica en que, si el servidor cambia de puerto, entonces

hay que modificar y recompilar el cliente. Una alternativa mejor es registrar una asociacion

¡servicio puerto/protocolo¿en el fichero /etc/services y utilizar la funcion getservbyname(),

para obtener, dada una cadena dem caracteres con el nombre del servicio, el numero de puerto

asociado.

Por otra parte, SERV_ADDR esta definido en funcion de otra constante: IPPORT RESERVED.

En un sitema UNIX, todos los puertos menores que esta constante, (cuyo valor puede ser 1024)

estan reservados para procesos privilegiados (del sistema), donde se incluye la mayoria de los

servicios TCP/IP. Los demas puertos estan disponibles para uso temporaltıpicamente para

clientes. El sistema se encarga de no asignar aquellos puertos que ya estan ocupados.

En relacion al puerto TCP asociado al socket del cliente, vemos que no aparece citado

explicitamente en ninguna parte. Sin embargo, tiene que estar ahı, porque una conexion TCP

precisa de dos puertos. Al igual que pasaba en el caso de UDP, el sistema asigna dinamicamente

un puerto cualquiera, que este libre, dentro del rango no reservado. La asignacion se realiza

cuando se intenta establecer una conexion y no ha habido un bind() previo. Si tenemos interes

en saber el puerto del cliente, podemos obtenerlo usando la funcion getsockname().

24.14.2. servidor

El servidor se encarga de hacer las operaciones necesarias para atender a sus clientes : es

importante darse cuenta de que este servidor no se limita establecer una comunicacion con

el cliente y luego terminar. Es capaza de antender a multiples clientes aunque, eso si, de uno en

uno. Es un servidor iterativo. Mas adelante veremos como disenar servidores concurrentes que

permitan la atencion simultanea a varias conexiones o, lo que es lo mismo, a varios clientes.

El diseno de un servidor TCP es mas complejo que el de un cliente. Vamos enumerar ahora

los pasos fundamentales que se dan, y tras ver el codigo analizaremos mas a detalle algunos

aspectos del programa.

169

Page 179: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

1. Crea un socket, sock, en dominio PF INET, estilo SOCK STREAM.

2. Calcula la direccion y se la asocia a sock

3. Marca el sock como socket de escucha.

4. Se bloquea en sock, a las espera de una peticion de establecimiento de conexion por parte

de algun cliente.

5. Consigue una conexion a traves de un socket nuevo, msgsock , que es un socket de dialogo

6. Recibe informacion a traves de msgsock.

7. Cierra msgsock (y, con el, la conexion).

8. Se dispone a aceptar nuevas conexiones a traves de sock

/* serinetc.c */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <stdio.h>

#define STDOUT 1

#define SERV_ADDR(IPPORT_RESERVED+1)

main()

int rval;

int sock, length, msgsock;

struct sockaddr_in server;

char buf[1024];

/* creacion de un socket de escucha, de tipo "stream */

sock=socket(PF_INET, SOCK_STREAM,0);

if(sock<0)

perror("No hay socket de escucha");

exit(1);

/* voy a asignar a ese socket una direccion de transporte */

server.sin_family=AF_INET;

server.sin_addr.s_addr=htonl(INADDR_ANY);

170

Page 180: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

server.sin_port=htons(SERV_ADDR);

/*El puerto es uno concreto y fijo */

if(bind(sock,(struct sockaddr *)&server, sizeof server)<0)

perror("direccion no asignada");

exit(1);

/*Me dispongo a escuchar en el socket. */

listen(sock,1);

do

/* Me bloqueo esperando peticion de conexion */

/* Acepto y consigo un socket de dialogo "msgsock" */

msgsock=accept(sock, (struct sockaddr *)0, (int *)0);

if(msgsock==-1)

perror("Conexion no aceptada !!!");

else

do

/* me dispongo a leer datos por la conexion */

memset(buf,0, sizeof buf):

rval=read(msgsock, buf, 1024);

if(rval<0) perror("Mensaje no leido");

else write(STDOUT, buf, rval);

while(rval>0);

printf("\n Cerrando la conexion...\n");

close(msgsock);

while(1);

exit(0);

Lo mas peculiar del programa servidor es que no gestiona un unico socket, como pasa con

el cliente, sino dos (sock y msgsock), cada uno con una funcion diferente. Tambien cambia

la forma en la que se gestionan las conexiones. Hacden falta utilizar dos funciones: listen() y

accept().

24.14.3. Ejecucion de Ejemplo

1. Crear los ejecutables correspondientes al cliente (clinetc) y al servidor(serinetc)

171

Page 181: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

2. Lanzar el servidor como de fondo (recinetd &), se puede comprobar con el comando ps

para comprobar que esta activo.

3. Lanzar el cliente indicandole como parametro el nombre del computador donde se

encuentra el receptor (clinetc maquina_servidor). El servidor escribira entonces, en

su salida estandar, estas dos lıneas:

##--##--##----***----##--##--##

Cerrando la conexion..

4. El cliente termina inmediatamente, pero el servidor no: podemos comprobarlo utilizando

de nuevo el comando ps. Podemos realizar tantas ejecuciones del cliente como queramos.

Al terminar habra que matarlo explicitamente con el comando kill.

24.15. Conexion monoproceso

Ahora vamos aver como hacer un servidor que tenga un unico proceso antendiendo a la vez a

varias conexiones. La clave esta en el uso de una nueva llamada al sistema: select().

#include <sys/time.h>

#include <sys/types.h>

int select(int nfds, fd_set *readfds, fds, fd_set *writefds,

fd_set *exceptfds, struct timeval *timeout);

Con select podemos:

Bloquearnos en varios sockets a la vez, para despertarnos en cuando se pueda operar en

uno de ellos.

Hacer una encuesta simultanea a varios sockets, sin bloqueo.

Bloquearnos por un tiempo limitado

Para realizar estas operaciones, select() toma cinco argumentos. Los argumentos segundo,

tercero y cuarto son del tipo fd set , que es una forma de representar conjuntos de descriptores

(puede ser de cualquier objeto de E/S, sockets incluidos) constante, FD SETSIZE ). El ultimo

argumento representa un intervalo de tiempo (segundos+microsegundos).

struct timeval

long tv_sec; /* segundos */

172

Page 182: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

long tv_usec; /* y microsegundos */

;

Donde:

readfds Indica a select() que objetos tiene que examinar para ver si se puede realizar

una operacion de lectura en ellos.

writefds Indica que objetos tiene que examinar para ver si se puede realizar una operacion

de escritura en ellos

exceptfds Indica que objeto tiene que examinar para ver si se puede escribir por ellos

En este contexto, hay que indicar que la posibilidad de aceptar una conexion por un socket de

escucha tiene la misma consideracion que la posibilidad de leer por un socket de dialogo.

Select() monitoriza todos los objetos a la vez durante el tiempo maximo especificado en su

argumento timeout. En funcion del valor de este temporizador, se consigue tres componentes

distintos:

Si timeout es NULL (no se facilita temporizador), select() funciona de forma totalmente

bloqueante. Solo retorna en cuanto es posible realizar alguna operacion segura sobre alguno

o algunos de los descriptores a su cargo. Entendemos por operacion segura aquella que se

puede completar inmediatamente, sin bloqueo.

Si timeout indica tiempo 0, select() se limita a hacer una encuesta, retornando

inmediatamente.

Si timeout indica tiempo distinto de cero, select() se bloquea en sus descriptores, y

el momento de retornar depende de lo que ocurra antes: (1) que expire el temporizador o

que (2) se pueda realizar una operacion segura sobre algun descriptor.

Select() devuelve(-1) en caso de error; en caso contrario, devuelve el numero de descriptores

preparados para realizar una operacion. Para saber exactamente cuales son los descriptores

utilizables hay que examinar los conjuntos de descriptores, que habran sido convenientemente

modificados.

Para tratar con conjuntos de descriptores se dispone de estas cuatros macros:

void FD_SET(int fd, fd_set &fdset);

173

Page 183: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

void FD_CLR(int fd, fd_set &fdset);

void FD_ISSET(int fd, fd_set &fdset);

void FD_ZERO(fd_set &fdset);

FD SET() incluye un desriptor (fd) en un conjunto (fdset).

FD CLR() elimina un descriptor de un conjunto (fdset).

FD ISSET() devuelve 1 si un descriptor esta en un conjunto, y 0 en caso contrario. Por ultimo,

PD ZERO() vacıa un conjunto.

24.15.1. Ejemplo

/* serveco3.c */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

#define STDOUT 1

#define SERV_ADDR (IPPORT_RESERVED+1)

fd_set lecturas;

main()

int rvala, rvalb;

int sock, length, msgsocka, msgsockb;

int dispuestos;

struct sockaddr_in server;

char buf[1024];

sock = socket(PF_INET, SOCK_STREAM,0);

if(sock<0)

perror("no hay socket de escucha");

exit(1);

server.sin_family=AF_INET;

server.sin_addr.s_addr=htonl(INADDR_ANY);

server.sin_port=htons(SERV_ADDR);

if(bind(sock, (struct sockaddr *)&server, sizeof server)< 0)

174

Page 184: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

perror("direccion no asignada");

exit(1);

listen(sock, 1);

do

msgsocka=accept(sock, (struct sockaddr *)0, (int *)0);

if (msgsocka == -1)

perror("conxion no aceptada !!!");

exit(-1);

msgsockb=accept(sock, (struct sockaddr *)0, (int *)0);

if (msgsockb == -1)

perror("conxion no aceptada !!!");

exit(-1);

do

FD_ZERO(&lecturas);

FD_SET(msgsocka, &lecturas);

FD_SET(msgsockb, &lecturas);

dispuestos=select(FD_SETSIZE, &lecturas,(fd_set *)NULL,

(fd_set *)NULL, NULL);

if(FD_ISSET(msgsocka, &lecturas))

rvala=read(msgsocka, buf, 1024);

if(rvala<0) perror("mensaj no leido (1)");

else write(STDOUT, buf, rvala);

if(FD_ISSET(msgsockb,&lecturas))

rvalb=read(msgsockb, buf, 1024);

if(rvalb<0) perror("mensaj no leido (2)");

else write(STDOUT, buf, rvalb);

while((rvala>0) || (rvalb >0));

printf(" \n Cerrando las conexiones ... \n");

close(msgsocka);

close(msgsockb);

175

Page 185: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

while(1);

exit(0);

El ejemplo muestra un sevidor que, como hemos dicho atiende conexiones de dos en dos. En

primer lugar, se bloquea hasta recibir dos peticiones de conexion; el efecto es que un cliente

que se conecta a este servidor se queda “sin poder hacer nada” hasta que llega un segundo

cliente. Una vez que se ha establecido las dos conexiones, el sevidor utiliza select() para

gestinarlas simultaneamente. El bucle principal del servidor termina cuando ambos usuarios

dan por cerradas sus conexiones respectivamente; observese que, debido al comportamiento de

select(), si un usuario cierra su conexion el otro puede seguir aun trabajando con el servidor.

24.16. Conexiones simultaneas (multiprocesos)

Una alternativa al diseno de servidor concurrente consiste en disenar un programa multiproceso

de tal forma que el servidor tiene constantemente un proceso en cmarcha atendiendo al socket

de escucha. En cuanto se establece una comunicacion, el servidor realiza un fork() y crea un

proceso hijo que se dedica, en exclusiva, a atender el cliente con el que se tiene conexion a

traves del sockets de dialogo. Mientras tanto, el padre o proceso principal sigue atendiendo al

socket de escucha, ignorando los sockets de dialogo, creando tantos hijos como sea necesario.

Esta alternativa funciona bien siempre que los proceso hijos no necesiten compartir informacion

(variables), puesto que cada uno de ellos se ejecuta en un espacio de memoria separado. En

el caso de que el sistema operativo soporte threads el servidor puede ser multithread en vez

de multiprocesos, y la limitacion mencionada desaparece, puesto que diferentes threads sı que

puede compartir memoria.

24.16.1. Ejemplo

A continuacion incluimos un ejemplo completo: un servidor de eco concurrente multiproceso

/* serveco2.c */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <signal.h>

176

Page 186: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#include <errno.h>

extern int errno;

void do_echo(int);

main()

struct sockaddr_in sin, fsin;

int s, ssock, alen;

sin.sin_family=AF_INET;

sin.sin_addr.s_addr=htonl(INADDR_ANY);

sin.sin_port=htons(3500);

if((s=socket(PF_INET, SOCK_STREAM,0))<0)

perror("No se puede crear el socket");

exit(1);

if(bind(s,(struct sockaddr *)&sin, sizeof sin)<0)

perror("No se puede asignar direccion");

exit(2);

if(listen(s,5)<0)

perror("No puedo poner el socket en modo escucha");

exit(3);

signal(SIGCHLD,SIG_IGN);

while(1)

alen=sizeof(fsin);

if((ssock=accept(s,struct sockaddr *)&fin,&alen))<0)

if(errno==EINTR) continue;

perror("Fallo en accept");

exit(4);

177

Page 187: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

switch(fork())

case -1: perror("No se puede crear hijo");

exit(5);

case 0: close(s);

do_echo(ssock);

exit(0);

default:close(ssock);

break;

void do_echo(int fd)

char buf[4096];

int cc, org, faltan, cc2;

while (cc=read(fd,buf, sizeof buf))

if(cc<0)

perror("read");

exit(6);

org=0;

faltan=cc;

while(faltan)

if((cc2=write(fd,&buf[org],faltan))<0)

perror("Fallo al escribir");

exit(7);

org+=cc2;

faltan-=cc2;

178

Page 188: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

close(fd);

El servidor reside en puerto fijo, el 3500. Si unj cliente se pone en contacto con este servidor

establece una conexion, recibira por ella una copia de toda la informacion que envıe.

Interesa destacar unos cuantos aspectos del ejemplo:

El que se refiere al control de los errores. Observese que cada vez que se realiza una llamada

al sistema, se comprueba inmediatamente su resultado a fin de terminar el programa si

algo va mal. Esta estrategia es fundamental para conseguir aplicaciones robustas.

la siguiente lınea de codigo tambien resulta interesante:

signal(SIGCHLD, SIG_IGN);

El motivo de su presencia es el siguiente. El servidor esta creando procesos hijos

constantemente, uno por cada conexion. En cuanto una conexion se cierra, el proceso

que la atendia “muere”y devuelve un diagnostico (ejecutando exit(n)). Se supone que,

por cada hijo creado, el proceso padre de realizar una llamada a la funcion wait(), que le

bloquea hasta que el hijo muere y recupera el valor del diagnostico. Si el padre no realiza

esta accion, el resultado es que los hijos no desaparecen del sistema, si no que quedan

como procesos zombies. Adicionalmente cuando un hijo muere se envia al padre unaq

senal SIGCHLD que, en caso de que no se realice ninguna operacion especial, se ignora

y no tiene ningun efecto?

La segunda estrategia es asignar un gestor que atienda la SIGCHLD que se limite a

realizar llamadas a wait(), eliminado ası zombies.

La asignacion del gestor se hace con signal();

signal(SIGCHLD,terminator);

El gestor, terminador(), puede tener este aspecto:

int terminnador()

int status;

179

Page 189: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

while(wait(&status))>=0);

180

Page 190: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte VIII

Creacion de demonios

181

Page 191: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 25

Proceso para la creacion de un demonio

25.1. Demonios

Los demonios se inician (y se paran)

• Cuando un sistema cambia los niveles de ejecucion;

• Cuando un sistema arranca y, a menos que los obligue a terminar.

• Se ejecutan hasta que se cierra el sistema

No tienen terminal controlador, cualquier salida a stderr o a stdout, requiere una

manipulacion especial en consecuencia no son interactivos, es decir no necesitan entrada

de usuario.

Se ejecutan frecuentemente con privilegios de superusuario.

Generalmente son lideres de grupos de proceso y lideres de sesion.

El padre del proceso es el proceso init, que tiene un PID 1.

25.2. Como crear un demonio

1. Tenemos que hacer fork y hacer que el padre salga (exit), para deshacerse del terminal

controlador. Esto tiene sentido. Los demonios ni leen en una entrada estandar ni escriben

en un salida o error estandar, por lo que necesitan una interfaz de terminal el tiempo

suficiente para iniciarse

2. Crear una sesion en el hijo utilizando la llamada setsid. Con esta llamada conseguimos

varias cosas.

a) Crear una sesion nueva si el proceso que lo llama no es un lider de grupo de proceso.

b) Hace del proceso que llama el lider de grupo de proceso del nuevo grupo.

c) Establece el ID del grupo de proceso (PGID) y el ID de sesion (SID) en el ID de

proceso (PID) del proceso que llama.

182

Page 192: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

d) Separa la nueva sesion de cualquier tty y controladora.

3. Hacer del directorio raiz el directorio actual del proceso hijo.

4. Establecer la umask del proceso como 0. Este paso es necesario para evitar que la

umask heredada del demonio interfiera en la creacion de archivos y directorios.

5. Cerrar cuaquier descriptor de archivos innecesarios que haya heredado el hijo. (stdin,

stdout y stderr).

En resumen:

Fork y exit en el proceso padre.

Llamar a setsid en el proceso hijo Hacer el directorio raiz, /, el directorio activo del

proceso hijo.

Cambiar la umask del proceso hijo a 0

Cerrar cualquier descriptor de archivos innecesarios.

Ejemplo:

/*demonio sencillo que marca el tiempo */

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <fcntl.h>

#include <errno.h>

#include <unistd.h>

#include <time.h>

#include <syslog.h>

int main(void)

pid_t pid, sid;

time_t timebuf;

int fd, len;

pid=fork();

183

Page 193: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

if(pid<0)

perror("fork");

exit(EXIT_FAILURE);

if(pid>0) /*en el padre, liberamos */

exit(EXIT_SUCCESS);

/*EN EL HIJO*/

/* PRIMERO INICIAMOS UNA NUEVA SECCION */

if((sid=setsid())<0)

perror("setsid");

exit(EXIT_FAILURE);

/*A CONTINUACION, HACEMOS DE / EL DIRECTORIO ACTUAL */

if((chdir("/"))<0)

perror("chdir");

exit(EXIT_FAILURE);

/*RESTABLECEMOS EL MODO DE ARCHIVO */

umask(0);

/*CERRAMOS LOS DESCRIPTORES DE ARCHIVOS INNECESARIOS*/

close(STDIN_FILENO);

close(STDOUT_FILENO);

close(STDERR_FILENO);

/* POR ULTIMO, HACEMOS NUESTRO TRABAJO */

len=strlen(ctime(&timebuf));

while(1)

char *buf=malloc(sizeof(char)*(len + 1));

if(buf==NULL)

perror("malloc");

exit(EXIT_FAILURE);

184

Page 194: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

if((fd=open("/var/log/daemons.log",O_CREAT|O_WRONLY

|O_APPEND,0600))<0)

perror("open");

exit(EXIT_FAILURE);

time(&timebuf);

strncpy(buf,ctime(&timebuf),len + 1);

write(fd, buf,len+1);

close(fd);

sleep(60);

exit(EXIT_SUCCESS);

25.3. Como comunicarse con un demonio

Para comunicarnos con un demonio, le enviamos una senal que hara que responda de una

cierta manera.Por ejemplo, normalmente es necesario obligar a un demonio a que vuelva a leer

su archivo de configuracion. El modo mas comun de hacer esto es enviar una senal SIGHUP

al demonio (tanto el servidor HTTP Apache como el servidor de correo Sendmail interpretan

SIGHUP como la senal para volver a leer sus archivos de configuracion). Otra necesidad comun

es la de alterar el comportamiento de un demonio sin forza a leer su archivo de configuracion.

Los ejemplos que se presentan a continuacion modifican lpudated para que lea un archivo de

configuracion y responda a SIGHUP. Sin embargo primero aprenderemos a leer un archivo de

configuracion y a utilizarlo para controlar el comportamiento de un demonio.

25.3.1. Como leer un archivo de configuracion

La primerera tarea es ensenar a lpudated a leer un archivo de configuracion. Como

demostracion, el archivo de configuracion, /etc/lpudated.conf, considera de un unica lınea

de texto de menos 256 caracteres. en realidad, como veremos en el programa, /etc/lpudated

puede contener cualquier cantidad de texto en tanta lıneas como queremos, pero lpudated solo

leera los primeros 255 caracteres. El listado siguiente muestra el programa modificado y con

nuevo nombre lpudated-rc.

/* lpudated-rc.c Demonio sencillo de marcacion de tiempo

185

Page 195: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

que lee un archivo de configuracion en /etc

*/

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <fcntl.h>

#include <errno.h>

#include <unistd.h>

#include <time.h>

#include <syslog.h>

#define RCFILE "/etc/lpudated.conf" /*el archivo de configuracion */

#define BUFLEN 256 /*longitud de lectura de buffer*/

int main(void)

pid_t pid, sid;

time_t timebuf;

int fd, rcfd, len;

pid=fork();

if(pid<0)

syslog(LOG_ERR,"%s\n",perror);

exit(EXIT_FAILURE);

if(pid>0) /*En el padre, liberamos */

exit(EXIT_SUCCESS);

/* en el hijo */

/* abre el archivo de sistema */

openlog("lpudated",LOG_PID,LOG_DAEMON);

/* lee el archivo de configuracion */

if((rcfd =open(RCFILE, O_RDONLY))<0)

syslog(LOG_ERR,"%s\n", "error opening RCFILE \n");

exit(EXIT_FAILURE);

186

Page 196: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

else

/* lee el archivo de configuracion */

char rcbuf[BUFLEN]; /*mantiene el valor leido */

int len; /* la longitud del buffer */

if((len=read(rcfd,rcbuf,BUFLEN))<0)

syslog(LOG_ERR,"%s\n", "error opening RCFILE \n");

exit(EXIT_FAILURE);

rcbuf[len]=’\0’;

/*Cierra el archivo de configuracion */

if(close(rcfd)<0)

syslog(LOG_ERR,"%s\n", "error closing RCFILE \n");

exit(EXIT_FAILURE);

/*Escribe el valor leido en el registro del sistema */

syslog(LOG_INFO,"%s\n",rcbuf);

/* Primero inicamos una nueva sesion */

if((sid=setsid())<0)

syslog(LOG_ERR,"%s\n", "setsid");

exit(EXIT_FAILURE);

/* A continuacion, hacemos del / el directorio actual */

if((chdir("/"))<0)

syslog(LOG_ERR,"%s\n", "chdir");

exit(EXIT_FAILURE);

/* Restablecemos el modo de archivo */

umask(0);

/* Cerramos los descriptores de archivos innecesarios */

close(STDIN_FILENO);

close(STDOUT_FILENO);

close(STDERR_FILENO);

187

Page 197: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

/* Por ultimo, hacemos nuestro trabajo */

len=strlen(ctime(&timebuf));

while(1)

char *buf=malloc(sizeof(char) * (len + 1));

if(buf == NULL)

syslog(LOG_ERR,"malloc");

exit(EXIT_FAILURE);

if((fd=open("/var/log/lpudated.log",O_CREAT |

O_WRONLY|O_APPEND, 0600))<0)

syslog(LOG_ERR,"open");

exit(EXIT_FAILURE);

time(&timebuf);

strncpy(buf, ctime(&timebuf),len+1);

write(fd,buf,len+1);

close(fd);

sleep(60)

/* Cerramos el registro del sistema */

closelog();

exit(EXIT_SUCCESS);

Recordemos que el demonio debe iniciarlo el suario root.

25.3.2. Como anadir manipulacion de senales a un demonio

Una vez que el demonio sabe leer un archivo de configuracion, la siguiente leccion es posibilitar

la manipulacion de senales. En su proxima reencarnacion, lpudated-sig, lpudate aprendera

a responder a SIGUP, los que hace que vuelvaa leer su archivode configuracion.

/* lpudated-sig.c Demonio sencillo de marcacion de tiempo que lee un

archivo de configuracion en /etc y manipula se~nales

*/

#include <sys/stat.h>

#include <stdio.h>

#include <stdlib.h>

188

Page 198: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#include <string.h>

#include <fcntl.h>

#include <errno.h>

#include <unistd.h>

#include <time.h>

#include <syslog.h>

#include <signal.h>

#define RCFILE "/etc/lpudated.conf" /*el archivo de configuracion */

#define BUFLEN 256 /*longitud de lectura de buffer*/

#define NORMAL 1

#define ROT13 2

int main(void)

pid_t pid, sid;

time_t timebuf;

int rcfd, fd, len, o_style;

pid = fork();

if(pid <0)

sys

#define RCFILE "/etc/lpudated.conf" /*el archivo de configuracion */

#define BUFLEN 256 /*longitud de lectura de buffer*/

189

Page 199: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Parte IX

Driver

190

Page 200: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Capıtulo 26

Proceso para la creacion de un driver

26.1. El primer driver: carga y descarga del driver en el espacio de

usuario

Para ello se escribe este programa en un fichero llamado nada.c

#define MODULE

#include <linux/module.h>

Lo compilamos con el siguiente comando:

$ gcc -c nada.c

(en realidad, para que funcione siempre sin problemas el comando para compilar deberıa ser

$ gcc -I/usr/src/linux/include -O -Wall -c nada.c , donde detras de -I debe aparecer el

directorio donde se encuentren los ficheros include del kernel)

Este elemental modulo pertenece al espacio de kernel y entrara a formar parte de el cuando

se cargue. Dentro del espacio de usuario podemos cargar el modulo en el kernel en la lınea

de comandos como usuario root con # insmod nada.o (si este no funciona se puede probar

insmod -f nada.o).

El comando insmod permite instalar nuestro modulo en el kernel, aunque este en concreto

no tenga ninguna utilidad. Podemos comprobar que el modulo ha sido instalado mediante el

comando que lista todos los modulos instalados: # lsmod Finalmente podemos eliminar el

modulo del kernel con el comando # rmmod nada Podemos comprobar que el modulo ya no

esta instalado de nuevo con el comando lsmod.

26.2. El driver “Hola mundo”: carga y descarga del driver en el

espacio de kernel

Para ello existen dos funciones, ini module y cleanup module, dentro del espacio de kernel

correspondientes a las del espacio de usuario, insmod y rmmod, que se utilizan cuando se

instala o quita un modulo. Dichas funciones son llamadas por el kernel cuando se realizan estas

191

Page 201: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

operaciones.

Veamos un ejemplo practico con el clasico programa ”Hola mundo”:

#define MODULE

#include <linux/module.h>

int init_module(void)

printk("<1>Hola mundo\n");

return 0;

void cleanup_module(void)

printk("<1>Adios mundo cruel\n");

26.3. El driver completo “memoria”: parte inicial del driver

Ahora realizaremos un driver completo, memoria.c, utilizando la memoria del ordenador que

nos permitira escribir y leer un caracter en memoria. Este dispositivo, aunque no muy util, es

muy ilustrativo dado que es un driver completo y facil de implementar ya que no se necesita

un dispositivo real. Para realizar un driver, en la parte inicial de el, tendremos que definir las

constantes MODULE y KERNEL . Ademas tendremos que incluir, con #include, una serie

de ficheros habituales en los drivers:

<<memoria inicio>>=

/* Definiciones e includes necesarios para los drivers */

#define MODULE

#define __KERNEL__

#include <linux/config.h>

#include <linux/module.h>

#include <linux/kernel.h> /* printk() */

#include <linux/malloc.h> /* kmalloc() */

#include <linux/fs.h> /* everything... */

#include <linux/errno.h> /* error codes */

#include <linux/types.h> /* size_t */

#include <linux/proc_fs.h>

#include <linux/fcntl.h> /* O_ACCMODE */

#include <asm/system.h> /* cli(), *_flags */

192

Page 202: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

#include <asm/uaccess.h> /* copy_from/to_user */

/* Declaracion de funciones de memoria.c */

int memoria_open(struct inode *inode, struct file *filp);

int memoria_release(struct inode *inode, struct file *filp);

ssize_t memoria_read(struct file *filp, char *buf, size_t count,

loff_t *f_pos);

ssize_t memoria_write(struct file *filp, char *buf, size_t count,

loff_t *f_pos);

void cleanup_module(void);

/* Estructura que declara las funciones tipicas */

/* de acceso a ficheros */

struct file_operations memoria_fops = read: memoria_read,

write: memoria_write,

open: memoria_open,

release: memoria_release;

/* Variables globales del driver */

/* Numero mayor */

int memoria_major = 60; /* Buffer donde guardar los datos */

char *memoria\_buffer;

Detras de los ficheros # include, aparecen las declaraciones de las funciones que definiremos en

el programa mas adelante. Posteriormente aparece la definicion de la estructura file operations

que define las funciones tıpicas que se utilizan al manipular ficheros y que veremos despues.

Finalmente estan las variables globales del driver, una de ellas es el numero mayor del dispositivo

y la otra un puntero a la region de memoria, memoria buffer, que utilizaremos como almacen

de datos del driver.

26.4. El driver “memoria”: conexion de dispositivos con sus ficheros

En UNIX y Linux se accede a los dispositivos desde el espacio de usuario de identica forma a

como se hace con un fichero. Dichos ficheros suelen colgar del directorio dev. Para ligar ficheros

con dispositivos se utilizan dos numeros: numero mayor y numero menor. El numero mayor

es el que utiliza el kernel para relacionar el fichero con su driver. El numero menor es para

uso interno del dispositivo y por simplicidad no lo veremos aquı. Para conseguir este proposito

primero se tiene que crear el fichero que sirva como dispositivo con el comando, como usuario

root, # mknod devmemoria c 60 donde la c significa que se trata de un dispositivo tipo

193

Page 203: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

char, el 60 es el numero mayor y el 0 el numero menor. Para ligar un driver con su fichero dev

correspondiente se utiliza la funcion register chrdev que tiene como argumento el numero mayor

del dispositivo. Esta funcion se llama con tres argumentos: numero mayor, cadena de caracteres

indicando el nombre del modulo y una estructura file operations que asocia esta llamada con

las funciones aplicables a ficheros definida dentro de ella. Se invoca, al instalar el modulo, de

esta forma:

<<memoria init module>>=

int init_module(void)

int result; /* Registrando dispositivo */

result = register_chrdev(memoria_major, "memoria",

&memoria_fops);

if (result < 0) printk("<1>memoria: no puedo obtener numero

mayor %d\n",memoria_major);

return result;

/* Reservando memoria para el buffer */

memoria_buffer = kmalloc(1, GFP_KERNEL);

if (!memoria_buffer) result = -ENOMEM; goto fallo;

memset(memoria_buffer, 0, 1);

printk("<1>Insertando modulo\n");

return 0; fallo: cleanup_module();

return result;

Ademas reservamos espacio en memoria para el buffer de nuestro dispositivo, memoria buffer,

a traves de la funcion kmalloc, la cual es muy similar a la comun malloc. Finalmente actuamos

en consecuencia ante posibles errores al registrar el numero mayor o al reservar memoria.

26.5. El driver “memoria”: eliminando el modulo

Para eliminar el modulo, dentro de la funcion cleanup module, insertamos la funcion

unregister chrdev para liberar el numero mayor dentro del kernel.

<<memoria cleanup module>>=

void cleanup_module(void)

/* Liberamos numero mayor */

194

Page 204: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

unregister_chrdev(memoria_major, "memoria");

/* Liberamos memoria del buffer */

if (memoria_buffer)

kfree(memoria_buffer);

printk("<1>Quitando modulo\n");

En esta subrutina tambien liberamos la memoria del buffer del dispositivo para dejar el kernel

limpio al quitar el modulo.

26.6. El driver “memoria”: abriendo el dispositivo

La funcion en el espacio de kernel correspondiente a la apertura de un fichero en el espacio

de usuario (fopen) es el miembro open: de la estructura file operations en la llamada a

registe chrdev. En este caso se trata de memoria open. Tiene como argumentos una estructura

inode que pasa informacion del kernel al driver tal como el numero mayor y el numero menor

y una estructura file con informacion relativa a las distintas operaciones que se pueden realizar

con el fichero. Ninguna de estas dos funciones las veremos en profundidad aquı. El kernel lleva

un contador de cuantas veces esta siendo utilizado un driver. El valor para cada driver se puede

ver en la ultima columna numerica del comando lsmod. Cuando se abre un dispositivo para

leer o escribir en el, la cuenta de uso se debe incrementar, tal y como aparece en la funcion

memoria open. Ademas de esta operacion, en la apertura de un fichero, se suelen iniciar las

variables pertinentes al driver y el propio dispositivo en si. En este ejemplo, y debido a su

extrema sencillez, no realizaremos operaciones de este tipo en dicha funcion.

Podemos ver la funcion memoria open a continuacion: como fichero

<<memoria open>>=

int memoria_open(struct inode *inode, struct file *filp)

/* Aumentamos la cuenta de uso */

MOD_INC_USE_COUNT; /* Exito */

return 0;

195

Page 205: Notas de Programaci´on de Sistemashilario_sm/slide/notas-prog-sist.pdf · Edificio 135, 14 Sur y Av. San Claudio, Ciudad Universitaria Puebla, Pue. C.P. 72570 Notas de Programaci´on

Bibliografıa

[1] Alonso Jose Miguel, “TCP/IP en UNIX Programacion de aplicaciones distribuidas”,

Madrid Espana, Ed. RA-MA, 1999, ISBN 970-15-0368-6.

[2] Xavier Calbet “Breve Tutorial para escribir driver en Linux”, Espana, 2001.

driver en Linux

[3] Gonzalez Morcillo Carlos, Redondo Duque Miguel Angel “Punteros en C”, 2003.

Puntero en C

[4] Francisco M. Marquez: “UNIX Programacion avanzada ”, 3a Edicion, Madrid, 2004, Ed.

RA-MA.

ISBN 84-7897-603-5.

[5] Kurt Wall: “Progrmacion en Linux ”, 2a Edicion, Madrid, 2001, Ed. Prentice Hall.

ISBN:84-205-3014-X.

[6] Tejeda Villela Hector: “Manual de C”,

[7] Santiago Domınguez: “Diapositivas de Programacion Sistemas del Curso Propedeutico de

la Maestrıa”, BUAP, 2000.

196