Está en la página 1de 18

UNIVERSIDAD MARIANO GÁLVEZ DE GUATEMALA

SEDE UNIVERSITARIA DE AMATITLÁN


INGENIERÍA EN SISTEMAS DE LA INFORMACIÓN
SISTEMAS OPERATIVOS I
ING. ERICK JOSUÉ VILLAGRAN TORRES

ALGORITMO DE PETERSON

ANDY FERNANDO SANTOS PAIZ 6590-19-7488


EMILIA GEOVANA LIMA PALENCIA 6590-19-112
HÉCTOR DANIEL MIJANGOS LÓPEZ 6691-19-515
PABLO ANTONIO DE LA ROCA GODOY 6590-13-5868
INTRODUCCIÓN

El algoritmo de Peterson es un algoritmo de programación concurrente para


exclusión mutua, que permite a dos o más procesos o hilos de ejecución compartir
un recurso sin conflictos, utilizando sólo memoria compartida para la comunicación.
Peterson desarrolló el primer algoritmo (1981) para dos procesos que fue una
simplificación del algoritmo de Dekker para dos procesos. Posteriormente este
algoritmo fue generalizado para que funcione para N procesos.
El algoritmo de Peterson

Desarrolló en 1981, es también conocido como la solución de Peterson, consiste en


un algoritmo de programación concurrente para la exclusión mutua, este se creó
como una simplificación del algoritmo de Dekker.

Permite a dos o más procesos o hilos de ejecución compartir un recurso sin


conflictos, utilizando sólo memoria compartida para la comunicación. El algoritmo
básico puede generalizarse fácilmente a un número arbitrario de procesos.

En el algoritmo para N procesos las variables c[i] además de valer "verdadero" y


"falso", pueden valer desde 1 hasta N. El procedimiento en si es una generalización
de este.

Este se ha vuelto un algoritmo básico para dos procesos, puede generalizarse


fácilmente a un número arbitrario de procesos de programación concurrente para
exclusión mutua, además impide el interbloque, el algoritmo de peterson da
prioridad a los procesos que requieren el recurso de manera inmediata.

Veamos un pequeño ejemplo, tener tres pizarras afuera del baño es decir una
bandera para cada uno de ellos y una tercera pizarra que va a ser la del desempate
que va a ser tú por lo tanto estas variables que van a ser compartidas pues va a ser
el arreglo de dos banderas cuyos valores iníciales los vamos a establecer con falso
y la variable turno que puede tener el valor inicial de cero y de uno pero para este
ejemplo le vamos a establecer el valor de cero ahora cuando un proceso quiere
entrar a la sección crítica lo que va a hacer es que va a poner su bandera en
verdadero y va a poner la variable turno con el número del otro proceso es decir
podríamos decir que van a ser muy educados porque lo que están diciendo es yo
quiero entrar pero en el caso de que tú también quieres entrar tú vas a tener tu turno,
entonces va a tener el valor de 0 o de 1 antes de entrar a la sección crítica.
Ahora comprueban si la bandera del otro proceso es verdadera y además es su
turno entonces me tengo que esperar en este ciclo while de acuerdo cuando
finalmente salgo de éste while puedo entrar a la sección crítica.

Algoritmo de Peterson para 2 procesos:

Este algoritmo es más simple que el de Dekker, garantiza la exclusión mutua.


Cada proceso tiene un turno para entrar en la sección crítica.
Si un proceso desea entrar en la sección crítica, debe activar su señal y si aplica el
caso esperar a que llegue su turno.
Impide el interbloqueo ya que, si un proceso se encuentra esperando Enel while,
entonces la señal y el turno del otro proceso están activadas. El proceso que está
esperando entrara en la sección crítica cuando la señal o el turno del otro se
desactiva.

Pseudocódigo
bandera[0] = 0
bandera[1] = 0
turno =0
p0: bandera[0] = 1 p1: bandera[1] = 1
turno = 1 turno = 0
while( bandera[1] && turno == 1 ); while( bandera[0] && turno == 0 );
//no hace nada; espera. //no hace nada; espera.
// sección crítica // sección crítica

// fin de la sección crítica // fin de la sección crítica


bandera[0] = 0 bandera[1] = 0

Los procesos p0 y p1 no pueden estar en la sección crítica al mismo tiempo: si p0


está en la sección crítica, entonces bandera[0] = 1, y ocurre que bandera[1] = 0, con
lo que p1 ha terminado la sección crítica, o que la variable compartida turno = 0, con
lo que p1 está esperando para entrar a la sección crítica. En ambos casos, p1 no
puede estar en la sección crítica...

Código en C++:

#include <cstdlib>
#include <iostream>
#include <thread>
#include <time.h>
#include <curses.h>
#include <stdio.h>
#include <string.h>

using namespace std;

bool proceso1_desea_entrar;
bool proceso2_desea_entrar;
int proceso_favorecido;
bool cancelar;

WINDOW * winA;
WINDOW * winB;

WINDOW * winTop;
WINDOW * winBottom;

void retardar_unos_milisegundos(int velocidad) {


for (int i = 0; i < velocidad && !cancelar; i++) {
for (int j = 0; j < velocidad && !cancelar; j++) {
/* no hace nada */
}
}
}

void retardo_aleatoreo()
{
srand(time(NULL));

int tiempo = (rand() % 100) * 50;

retardar_unos_milisegundos(tiempo);
}

void ejecutar_seccion_critica_1() {
waddstr(winA, "#");
wrefresh(winA);
retardar_unos_milisegundos(15000);
}

void proceso1() {

while (!cancelar) {

proceso1_desea_entrar = true;
proceso_favorecido = 2;

while (proceso2_desea_entrar && proceso_favorecido == 2 && !cancelar) {


/* esperar */
}

if (cancelar) break;

ejecutar_seccion_critica_1();
proceso1_desea_entrar = false;
}

waddstr(winA, "Ha terminado el proceso 1");


wrefresh(winA);
retardar_unos_milisegundos(20000);

void ejecutar_seccion_critica_2() {
waddstr(winB, "@");
wrefresh(winB);
retardar_unos_milisegundos(15000);
}

void proceso2() {

while (!cancelar) {

proceso2_desea_entrar = true;
proceso_favorecido = 1;

while (proceso1_desea_entrar && proceso_favorecido == 1 && !cancelar) {


/* esperar */
}

if (cancelar) break;

ejecutar_seccion_critica_2();

proceso2_desea_entrar = false;
}

waddstr(winB, "Ha terminado el proceso 2");


wrefresh(winB);
retardar_unos_milisegundos(5000);
}

void inicializar_pantallas() {
setlocale(LC_ALL, "spanish");
initscr();
erase();
refresh();
cbreak();
noecho();
keypad(stdscr, TRUE);
int h, w;
getmaxyx(stdscr, h, w);

winA = newwin(h - 2, (w / 2) - 1, 1, 0);


winB = newwin(h - 2, (w / 2) - 1, 1, (w / 2));

winTop = newwin(1, w, 0, 0);


winBottom = newwin(1, w, h-1, 0);

const char * titulo = "::::::::: Peterson para 2 Procesos :::::::::";

wmove(winTop, 0, (w / 2) - strlen(titulo));
waddstr(winTop, titulo);
wrefresh(winTop);

void esperar_enter_del_usuario() {
waddstr(winBottom, "Presione la tecla [Enter] para salir.");
wrefresh(winBottom);
getch();
refresh();
}

int main(int argc, char** argv) {

inicializar_pantallas();

cancelar = false;
proceso1_desea_entrar = false;
proceso2_desea_entrar = false;
proceso_favorecido = 1;

thread p1(proceso1);
thread p2(proceso2);

esperar_enter_del_usuario();

cancelar = true;

p1.join();
p2.join();

endwin();
return EXIT_SUCCESS;
}

Programa en Ejecución:

En este programa Podemos apreciar que ambos procesos no pueden estará en la


sección crítica, únicamente 1 a la vez.

Al iniciar el proceso indica que desea entrar a su sección crítica, pero a la vez
favorece al otro proceso.
Se crea un ciclo de espera hasta que otro proceso haya salido de su sección crítica
y que el proceso favorecido sea el proceso actual.
Se ejecuta la sección crítica.
Se indica que el proceso ya salió de la sección crítica
(procesoX_desea_entrar=false).
Se incluye una variable cancelar que se establece a true cuando el usuario presiona
la tecla Enter para poder finalizar los procesos en cualquier momento.
SECCIÓN CRÍTICA

Es la parte del proceso en la cual se tiene acceso a un recurso compartido. Para


evitar problemas en situaciones relacionadas con recursos compartidos, la clave es
determinar una forma de prohibir que más de un proceso lea o escriba en los datos
compartidos a la vez, en otras palabras, lo que se necesita es una forma de
garantizar que, si un proceso utiliza una variable o archivo compartido, los demás
procesos no puedan utilizarlos. A esto se le llama exclusión mutua. Se necesitan 4
condiciones para tener una buena solución. Los cuales son los siguientes:

Dos procesos no deben encontrarse al mismo tiempo dentro de sus secciones


críticas.

No se debe hacer hipótesis sobre la verdad o el número de procesadores.

Ninguno de los procesos que estén en ejecución fuera de su sección critica puede
bloquear a otros procesos.

Ningún proceso debe esperar demasiado tiempo para entrar en su sección critica.

Si los procesos comparten recursos no deben estar en su sección critica al mismo


tiempo, ya que puede provocar un conflicto; pero si ambos procesos son
independientes no de haber problema, si un proceso desea entrar a su secón critica
no deben hacerlo mucho para acceder al recurso compartido.
EL PROBLEMA DE LA SECCION CRÍTICA:

Consideremos un sistema compuesto por N procesos. Cada proceso tiene un


segmento de código, llamado sección crítica, en el cual el proceso puede estar
modificando variables comunes, actualizando una tabla, escribiendo en un archivo,
etc. Cuando un proceso se ejecuta en su sección crítica, no se permite que ningún
otro proceso se ejecute en su sección. De esta manera, la ejecución de las
secciones críticas de los procesos es mutuamente excluyente en el tiempo. El
problema de la sección crítica consiste en diseñar un protocolo que los procesos
puedan usar para cooperar. Cada proceso debe solicitar permiso para entrar en su
sección crítica; la sección de código que implanta esta solicitud es la sección de
entrada. A la sección crítica puede seguir una sección de salida y el código que
queda es la sección restante.

Una solución al problema de la sección crítica debe cumplir los tres requisitos
siguientes:

1. Exclusión mutua: si un proceso se está ejecutando en su sección crítica, entonces


ningún otro proceso se puede estar ejecutando en la suya.

2. Progreso: si ningún proceso se está ejecutando en su sección crítica y hay otros


procesos que desean entrar en las suyas, entonces solo aquellos procesos que no
se están ejecutando en su sección restante pueden participar en la decisión de cuál
será el siguiente en entrar en la sección crítica, y esta decisión no puede
postergarse indefinidamente.

3. Espera limitada: debe haber un límite en el número de veces que se permite que
los demás procesos entren en su sección crítica después de que un proceso haya
efectuado una solicitud para entrar en la suya y antes de que se con cada esa
solicitud.
EN QUE CONSISTE EL ALGORITMO DE PETERSON Y GENERALIZACIÓN
PARA N PROCESOS:

Ilustramos aquí en que consiste el algoritmo de Perterson para dos procesos y el


correspondiente algoritmo para N procesos es una generalización del mismo.

Utilizamos las siguientes variables:

c[i], que es un array y turno, que solo puede valer 1o 2.

Inicialmente, c[0]=c[1]= falso, y el valor de turno no tiene relevancia (pero de be ser


0 o 1). Para entrar en la seccion crítica, el proceso Pi primero asigna el valor
verdadero a c[i] y luego afirma que es el turno del otro proceso para entrar si así lo
desea (turno = j). Si ambos procesos tratan de entrar a la vez, se asignará turno
como i y j aproximadamente al mismo tiempo. Sólo una de estas asignaciones
durará; la otra ocurrirá, pero será reemplazada de inmediato. El valor eventual de
turno decide a cuál de los dos procesos se le permitirá entrar primero en su sección
crítica.

En el algoritmo para N procesos las variables c[i] además de valer "verdadero" y


"falso", pueden valer "en sección critica" y turno desde 1 hasta N. El procedimiento
es una generalización de este.
Algoritmo para N procesos
ELPROGRAMA:

/**************************************************************************************

******************** ALGORITMO DE PETERSON PARA N PROCESOS


****************************

**************************************************************************************/

#include <stdlib.h>

#include "rshmem.h"

#define Nproc 3

#define ENSECR 'S'

void incrementa(int *mem, int k){

int i;

i=*mem;TP

i=i+k;TP

*mem=i;

int main(int argn, char **argv){

FILE *fsal; /* puntero al fichero de salida */

char *marcaFin[Nproc], /* array que tiene los contenidos de marcafin1,...,marcafinN


*/

*c[Nproc]; /* array que tiene los contenidos de c1,c2,...,cN */

int *turno, /* variable turno de entrada del proceso */

*recurso, /* puntero a zona de memoria compartida */

nIteraciones=0,/* contador de iteraciones del proceso */


respuesta=0, /* varable indicadora */

i, /* variable contador */

j, /* variable contador */

k=0, /* variable contador */

l; /* variable contador */

/* comprobacion del numero de argumentos */

if(argn!=3){

printf("Error en la entrada de argumentos\n");

exit(1);

/* crear zona de memoria compartida */

if(!crearMemoria()){

fprintf(stderr,"Error de crearMemoria()\n");

nIteraciones=atoi(argv[1]);

recurso=(int*)memoria;

turno=(int*)recurso+sizeof(int);

marcaFin[0]=(char*)turno+sizeof(int);

for(i=1;i<Nproc;i++)

marcaFin[i]=(char*)marcaFin[i-1]+sizeof(int);

c[0]=(char*)marcaFin[Nproc-1]+sizeof(int);

for(i=1;i<Nproc;i++)

c[i]=(char*)c[i-1]+sizeof(int);
*recurso=0;

for(i=0;i<Nproc;i++)

*(marcaFin[i])='p';

for(i=0;i<Nproc;i++)

*(c[i])='F';

/* creacion de tantos procesos como indique Nproc */

for(i=0;i<Nproc;i++){

if(fork()==0){

/* codigo para los prodesos hijo */

/* Apertura del fichero de salida para el proceso i */

if((fsal=fopen(argv[2],"a+"))==NULL){

printf("Error en la apertura del fichero de salida.\n");

exit(-1);

for(j=0;j<nIteraciones;j++){

/* seccion de entrada */

do{

*(c[i])='T';

k=*turno;

while(k!=i){

if(*(c[k])!='F')

k=*turno;

else
k=(k+1)%Nproc;

*(c[i])=ENSECR;

k=0;

while((k<Nproc)&&((k==i)||((*(c[k]))!=ENSECR)))

k=k+1;

}while(!((k>=Nproc)&&((*turno==i)||(*(c[*turno])=='F'))));

*turno=i;

/* seccion critica */

incrementa(recurso,5);

fprintf(fsal,"P%d: recurso[%d]= %d\n",i,j,*recurso);

fflush(fsal);

/* seccion de salida */

k=(*turno+1)%Nproc;

while(*(c[k])=='F')

k=(k+1)%Nproc;

*turno=k;

*c[i]='F';

}/* fin for (j) */

/* cierre del fichero de salida del proceso i */

fclose(fsal);

/* marca de terminacion y terminacion de procesos hijo */

*(marcaFin[i])='x';
exit(0);

}/* fin if, fin procesos hijo */

}/* fin for (i) */

/* espera activa del padre por los procesos hijo */

for(l=0;l<Nproc;l++)

while(*(marcaFin[l])!='x');

/* Apertura del fichero de salida de los resultados */

if((fsal=fopen(argv[2],"a+"))==NULL){

printf("Error en la apertura del fichero de salida\n");

exit(-1);

fprintf(fsal,"El recurso valia 0 y ahora vale %d y tiene que valer


%d\n",*recurso,nIteraciones*Nproc*5);

fflush(fsal);

/* cierre del fichero de salida */

fclose(fsal);

/* eliminacion de la memoria compartida */

if(!eliminarMemoria())

fprintf(stderr,"Error de eliminarMemoria\n");

/* terminacion del proceso padre */

exit(0);

}/* fin main */


CONCLUSIÓN

En conclusión, el algoritmo de Dekker y Peterson tienen un fin común que ambos


son algoritmos de programación concurrente para exclusión mutua. Ambos
permiten dos o más procesos de ejecución y pueden compartir recursos de
ejecución sin conflictos. El algoritmo de Peterson es una simplificación del algoritmo
de Dekker.

También podría gustarte