Mecanismos de comunicación entre procesos (IV)

Pipes

El tercer mecanismo IPC que vamos a tratar en esta saga son las tuberías (Pipes) y cuya principal característica es que son unidireccionales. Tal y como hace el shell en una orden del estilo ls | grep txt, el proceso a la izquierda de ‘|’ escribe en la tubería y el proceso de la derecha, lee.

Para hacer un uso correcto de las tuberías, debemos saber y comprender el funcionamiento  de los descriptores de archivo en el mundo Unix y, en particular, Linux (haremos uso de las funciones pipe y close).

Un ejemplo clásico de uso en programación de este tipo de IPC es cuando tenemos que desarrollar una aplicación que necesita comunicar a un padre con sus procesos hijos (o threads diferentes):

#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
 
#define SIZE 1024
 
int main( int argc, char **argv )
{
    pid_t pid;
    int p[2], readbytes;
    char msg[SIZE];
    int state, i=0;
    pipe(p);
    if ( (pid=fork()) == 0 ){ 
      /* Child process*/
      /* First: We close pipe write side*/
      close(p[1]);
      while((readbytes=read(p[0], msg, SIZE)) > 0)
       /* We write it in stdout*/
          write(1, msg, readbytes);
      close(p[0]);
    }
    else {
        /* Father proccess*/
        /* Firts: We close pipe read side*/
        close(p[0]); 
        while (i<10) {
            sprintf(msg, "Message number %dn", i);
            write(p[1], msg, strlen(msg));
            i++;
        }
        close(p[1]);
        wait(&state);
    }
    exit(0);
}

Las funciones importantes del ejemplo son:

  • pipe: Crea la tubería. Necesita un vector de 2 elementos de enteros. El primero (posición 0) será la salida de la tubería (lectura) y el segundo, la entrada (escritura)
  • close: Cierra los descriptores que no vamos a usar. La lectura en el proceso que escriba y la escritura en el que lea. Además, cuando los procesos terminan, cierran los descriptores abiertos.
  • read y write: usadas para leer y escribir tomando como argumentos los descriptores de archivo apropiados (exactamente igual que haríamos con cualquier fichero)

 Referencias:

  1. Unix Programación avanzada, Fco. Manuel Márquez García

Mecanismos de comunicación entre procesos (III)

En esta entrada vamos a ver en qué consiste el siguiente IPC listado en la primera entrada de esta saga: mapped memory. Con este mecanismo podemos lograr comunicar procesos que se ejecutan en un mismo sistema operativo via un fichero compartido (usualmente este mecanismo se estudia como un modo de acceder de una forma rápida y fácil al contenido de ese fichero). Conceptualmente, es muy parecido a la memoria compartida. La función que se utiliza para mapear un fichero en memoria es mmap(), con el flag MAP_SHARED para que pueda ser compartida por varios procesos, y munmap() para el proceso inverso. El uso de estas funciones se puede consultar en la página man del sistema o en http://man7.org/linux/man-pages/man2/mmap.2.html

Ejemplo de uso

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define FILE_LENGTH 0x1000
int main (int argc, char* const argv[])
{
 int fd;
 void* file_memory;
 char msg[10]="";
 int state,pid;
if ((pid=fork()) != -1) {
 if (pid == 0) {
 /* Open the file. */
 fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
 write (fd, "", 10); /* The file can not be empty*/
 lseek (fd, 0, SEEK_SET);
 /* Create the memory mapping. */
 file_memory = mmap(0, FILE_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 close (fd);
 printf("Writingn");
 sprintf ((char*)file_memory, "%sn", "Message 1n");
 munmap (file_memory, FILE_LENGTH);
 }
 else {
 /* Open the file. */
 printf("Opening %sn", argv[1]);
 fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
 lseek (fd, 0, SEEK_SET);
 /* Read the integer, print it out, and double it. */
 file_memory = mmap(0, FILE_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 if (file_memory!= NULL) {
 close (fd);
 printf("Wating child endn");
 /*wait(&state);*/
 sleep(1);
 printf("Readingn");
 sscanf(file_memory, "%9s", msg);
 printf("I've read: %sn", msg);
 sprintf ((char*)file_memory, "%sn", "Reaaadn");
 /* Release the memory */
 munmap (file_memory, FILE_LENGTH);
 wait(&state);
 }
 else {
 printf("mmap error");
 }
 }
 }
 else {
 printf("Error making child");
 }
exit(0);
}

Al ejecutar este código, podemos ver como la variable msg toma, en el código ejecutado por el padre,  el valor escrito en la memoria por el hijo («Message«).

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel

Mecanismos de comunicación entre procesos (II)

Los mecanismos IPC del Unix System V (semáforos para sincronizar, memoria compartida y colas de mensajes) están implementados como una unidad y, por tanto, comparten características comunes de tal forma que, cada mecanismo dispone de una función:

  • get para crear o recuperar uno de estos elementos de comunicación (por ejemplo, para C, shmget, semget, msgget)
  • de control que permite leer y modificar el estado del elemento.

Existen más características que se pueden consultar en la documentación referenciada. Existen, además, 2 utilidades que nos permiten, desde el shell, obtener información y controlar los mecanismos usados en un sistema GNU/Linux concreto: ipcs e ipcrm. El primero sirve para saber los IPCs asignados junto con cierta información. El segundo permite borrarlos. Para saber más sobre ellos: man ipcs o man ipcrm.

Shared Memory

Quizás sea la forma más rápida de comunicar 2 procesos ya que para ello, solo debemos escribir en la zona de memoria compartida por ambos. Para poder utilizar este mecanismo, tenemos las funciones (en C):

  • shmget para crear (o habilitar si ya lo está) una zona de memoria
  • shmctl para acceder y modificar la información administrativa y de control asociada a la memoria
  • shmat para asociar una zona de memoria a un proceso
  • shmdt para desasociar la memoria del proceso

Para sincronizar el acceso a la memoria compartida entre distintos procesos debemos usar un mecanismo de sincronización: semáforos.

Ejemplo de uso

 int segment_id;
 char* shared_memory;
 struct shmid_ds shmbuffer;
 int segment_size;
 const int shared_segment_size = 0x6400;
 
 /* Allocate a shared memory segment. */
 segment_id = shmget (IPC_PRIVATE, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
 /* Attach the shared memory segment. */
 shared_memory = (char*) shmat (segment_id, 0, 0);
 printf ("shared memory attached at address %pn"€, shared_memory);
 /* Determine the segment'™s size. */
 shmctl (segment_id, IPC_STAT, &shmbuffer);
 segment_size = shmbuffer.shm_segsz;
 printf ("€œsegment size: %dn"€, segment_size);
 /* Write a string to the shared memory segment. */
 sprintf (shared_memory, "€œHello, world"€);
 /* Detach the shared memory segment. */
 shmdt (shared_memory);
 /* Reattach the shared memory segment, at a different address. */
 shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0);
 printf ("shared memory reattached at address %pn"€, shared_memory);
 /* Print out the string from shared memory. */
 printf ("€œ%sn"€, shared_memory);
 /* Detach the shared memory segment. */
 shmdt (shared_memory);
 /* Deallocate the shared memory segment.
 shmctl (segment_id, IPC_RMID, 0);

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel
  2. Unix Programación avanzada, Fco. Manuel Márquez García
  3. Para Python: https://docs.python.org/2/library/multiprocessing.html

Mecanismos de Comunicación entre procesos (I)

En un estado normal de un sistema operativo, pueden cohabitar diferentes procesos en ejecución y que necesiten comunicarse. Por ejemplo, en esta entrada vimos que para la implementación de un sencillo servidor concurrente, podíamos crear un proceso hijo encargado de atender la petición del cliente. Dependiendo del servicio, podríamos necesitar la intercomunicación del proceso padre con los hijos* o, incluso, entre hijos.

En la próxima serie de entradas, comentaré los distintos tipos de IPC** que existen, excepto uno: sockets, porque de éste, ya hemos hablado aquí, aquí y aquí):

  • Shared Memory: Memoria compartida entre procesos que les permite comunicarse leyendo y escribiendo en una zona específica de memoria. Quizás sea el mecanismo IPC más simple de comunicación.
  • Mapped Memory: similar al anterior pero asociado a un fichero.
  • Pipes: canal secuencial entre procesos (es la ‘barra’ de esta orden: ‘ls | grep *.exe’)
  • FIFO: similar a las tuberías excepto que los procesos no tienen que estar relacionados debido a que la tubería se «construye» sobre un nombre en el sistema de archivos.

Si nos fijamos, la principal diferencia entre los 5 tipo de IPC (los 4 anteriores más los sockets) está en (1) función de si los procesos están o no relacionados (o si están en el mismo nodo de red), (2) si pueden escribir, leer o ambos y (3) el número de procesos que pueden comunicar.

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel

* En este caso no me refiero a, simplemente, saber el estado de finalización de los hijos que, en si mismo, podríamos aceptar como comunicación entre procesos aunque muy simple.

** Hay quien considera las señales como un mecanismo IPC. En mi opinión, éstas están destinadas a proporcionar una forma de control sobre un proceso, no como un mecanismo IPC. Además, debemos tener en cuanta que no contienen datos a excepción de ella misma (la señal que es) y, si lanzamos una gran cantidad de ellas, el sistema se verá interrumpido constantemente por algo que, encima, necesita de un tratamiento especial.

 


Timeouts de TCP

En TCP(1) existen 4 tipos de timeouts (tiempos de espera) y cada uno de ellos se aplica en diferentes momentos y con diferente duración. Algunos, incluso, podemos evitarlos si es lo que deseamos…

Conocer estos tiempos es importante tanto para administradores de sistemas como para programadores de aplicaciones distribuidas (¡¡¡pocas no lo son ya!!!)

Leer más


Como saber las IPs que han realizado más de N peticiones en S segundos a nuestro servicio Apache

Tras una pregunta de un alumno en clase sobre control y detección de peticiones abusivas, recordé el guión que desarrollé, cómo no con Python ;), para detectar «ráfagas» de conexiones. Con Python es muy fácil analizar los logs del servidor Apache y consultar qué IPs nos han realizado más de X peticiones (entendiendo por X un parámetro que introducimos como argumento) en un número de segundos determinado (también introducido como argumento del guión)

Dependiendo de qué información queramos extraer, ajustaremos el número de peticiones y segundos. Por ejemplo, ante subidas en la carga del servidor, podemos comprobar qué IPs cumplen un patrón y, si consideramos que se están realizando con mala fe, bloquear el tráfico proveniente de estas IPs.

Aunque en EPSAlicante rotamos los logs, por si no se hace o por si la información que queremos analizar está en un periodo de tiempo determinado, esto también puede indicarsele al guión (D días, H horas y M minutos antes).

El desarrollo de este guión apenas me llevó 2 horas (y con interrupciones) y, sin embargo creo que es muy útil. Podemos hasta ejecutarlo periódicamente para ver qué obtenemos. ¡Nunca se sabe las sorpresas que nos podemos llevar!

Creo que incluso sería bueno (posible mejora) que la información la proporcionara en XML para integrarlo con otros sistemas de administración y/o seguridad.

Justo debajo de este párrafo está el guión. La función más importante es readHistoryIP que devuelve un diccionario con clave IP y valor otro diccionario cuya clave es la fecha de registro del evento y el valor el número de veces que para esa IP aparece un evento +/- segundos con respecto a la fecha del evento. En el cuerpo principal se muestra la información de las IPs que aparecen más veces que del valor indicado por parámetro para el intervalo de tiempo establecido.

#!/usr/bin/python
#encoding:utf-8
try:
 import sys,optparse, datetime,re
except:
 print("Error running 'import sys,optparse,datetime,re'. Maybe you have to install some python library")

def isValid(ip):
 if (re.match("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",ip)):
 return True
 elif re.match(r'^[a-zA-Z0-9_]{,63}(.[a-zA-Z0-9_]{,63}){3}$',ip): #("^W+(.W)$", ip):
 return True
 else:
 #print "Name/IP host wrong. You have to write a FQDN name or a IP direction"
 return False
 return False

def readHistoryIP(logfile,sec, deltaday,deltahour,deltaminutes):
 months = {"Ene":1, "Feb":2, "Mar":3, "Abr":4, "May":5, "Jun":6, "Jul":7, "Ago":8, "Sept":9, "Oct":10, "Nov":11, "Dic":12}
 today = datetime.datetime.now()
 refDay= datetime.timedelta(days=int(deltaday), hours=int(deltahour), minutes=int(deltaminutes))

 ipHitListing = { }
 try:
 contents = open(logfile, "r")
 except IOError, e:
 print 'Error openning file '+ logfile +": "+e.strerror
 raise
 except:
 print 'Error openning file '+ logfile
 raise
 # go through each line of the logfile
 for line in contents:
 # split the string to isolate the IP address and date
 ip = line.split(" ", 1)[0]
 dateRecord=line.split(" ",5)[4][1:]
 date = dateRecord.split(":",1)[0]
 day=date.split("/",1)[0]
 month=date.split("/",2)[1]
 year =date.split("/",3)[2]
 hour = dateRecord.split(":",2)[1]
 minute = dateRecord.split(":",3)[2]
 seconds = dateRecord.split(":",4)[3]
 eventdate=datetime.datetime(int(year),int(months[month]),int(day),int(hour),int(minute),int(seconds))
 #Check if ip is right
 #print "Analizing IP: '" + ip + "'"
 if isValid(ip):
 if eventdate > today - refDay:
 intervalDelta=datetime.timedelta(seconds=int(sec))
 count=False
 if (ip in ipHitListing): 
 for ipData in ipHitListing[ip].items():
 if eventdate < ipData[0] + intervalDelta:
 ipHitListing[ip]= {ipData[0]:int(ipData[1])+1}
 count=True
 break
 if count == False:
 ipHitListing[ip]= {eventdate:1}
 else:
 ipHitListing[ip]= {eventdate:1}
 return ipHitListing
def main():
 parser = optparse.OptionParser("usage%prog " + "[-f <file>] -n Number of events -s seconds to find events -d number of days to analice -H number of hours to analice -m number of minutes to analice")
 parser.add_option('-f', dest = 'file', type = 'string', help = 'Please, specify the Apache access file', default="/var/log/apache2/access.log")
 parser.add_option('-n', dest = 'number', type = 'string', help = 'Please, specify the number of connections to detect. By default 10', default="10")
 parser.add_option('-d', dest = 'deltaday', type = 'string', help = 'Please, specify the number of days to check. By default 10', default="10")
 parser.add_option('-H', dest = 'deltahour', type = 'string', help = 'Please, specify the number of hours to check. By default 10', default="10")
 parser.add_option('-m', dest = 'deltaminute', type = 'string', help = 'Please, specify the number of minutes to check. By default 10', default="10")
 parser.add_option('-s', dest = 'seconds', type = 'string', help = 'Please, specify the seconds. By default: 1"', default="1")
 (options, args) = parser.parse_args()
 HitsDictionary = readHistoryIP(options.file,options.seconds, options.deltaday, options.deltahour, options.deltaminute)
 for ip in HitsDictionary.keys():
 if HitsDictionary[ip].values()[0] > int(options.number):
 print "Attention---> IP: " + ip + ", " + str(HitsDictionary[ip].values()[0]) + " access in " + options.seconds + " seconds"
if __name__ == "__main__":
 main()

Referencias:

  1. Python para todos, de Raúl González Duque
  2. Python cookbook, de Alex Martelli, Anna Martelli y David Ascher, O’Reilly

¿Cómo se distribuyen los estados de las peticiones que recibe nuestro servidor Apache2?

Si queremos saber cuál es el porcentaje, sobre el total de respuestas, de un estado HTTP determinado (o todos los posibles) dentro de las peticiones de servicio que recibe nuestro servidor Web Apache, con el guión escrito en Python al final de esta entrada, lo podemos obtener fácilmente.

¿Para qué nos puede servir esta información? En principio, para saber cómo está trabajando nuestro servidor y cómo se está relacionando con los clientes:

  • Los estados de error, bien del cliente o del servidor, nos permitirán conocer los porcentajes de error y detectar problemas
  • Estados específicos, como el 304 que nos permitirá saber el porcentaje de páginas solicitadas a pesar de que el cliente tiene una copia en cache vigente (y analizar rendimientos)
  • Porcentajes de accesos no autorizados, solicitudes incorrectas,…
#!/usr/bin/python
#encoding:utf-8
try:
 import sys,optparse
except:
 print("Error running 'import sys,optparse'. Maybe you have to install some python library")
def clientStatePercentage(logfile,state):
 try:
 contents = open(logfile, "r")
 except IOError, e:
 print 'Error openning file '+ logfile +": "+e.strerror
 raise
 except:
 print 'Error openning file '+ logfile
 raise
 totalRequests = 0
 requests = 0
 for line in contents:
 totalRequests += 1
 if line.split(" ")[8] == state:
 requests += 1
 return int(0.5+float(100*requests)/totalRequests)
parser = optparse.OptionParser("usage%prog " + "[-f <file>] [-e HTTP STATE[,HTTP_STATE2,[...]]]")
parser.add_option('-f', dest = 'file', type = 'string', help = 'Please, specify the Apache access file', default="/var/log/apache2/access.log")
parser.add_option('-e', dest = 'state', type = 'string', help = 'Please, specify the HTTP State', default="304")
(options, args) = parser.parse_args()
log=options.file
states=options.state.split(',')
for state in states:
 print 'Percentage of requests with ' + state + ' state: ' + str(clientStatePercentage(log,state)) + '%'

El cliente en Python

Y para terminar con el ejemplo de un esqueleto cliente-servidor en Python, el cliente:

#!/usr/bin/python
#encoding:utf-8
try:
 import socket
 import optparse,sys
except:
 print("Error running 'import optparse,socket,sys'. Maybe you have to install some python library :)")
parser = optparse.OptionParser("usage%prog " + "-s <target server> -p <target port>")
parser.add_option('-s', dest = 'server', type = 'string', help = 'Please, specify the target server '-s server'')
parser.add_option('-p', dest = 'port', type = 'string', help = 'Please, specify the target port '-p port'')
(options, args) = parser.parse_args()
if (options.server == None):
 print '[-] You must specify a target server: -s server.'
 exit(0)
if (options.port == None):
 print '[-] You must specify a target port: -p port'
 exit(0)
server = options.server
port = int(options.port)

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the port on the server given by the caller
server_address = (server, port)
print('connecting to %s port %s' % server_address)
try:
 sock.connect(server_address)
except socket.error , msg:
 print 'Connect failed. Error code: ' + str(msg[0]) + 'Error message: ' + msg[1]
 sys.exit()
try:
 message="Speaking with server "
 print("sending %s" % message)
 sock.sendall(message)
 amount_received = 0
 amount_expected = len(message)
 while amount_received < amount_expected:
 data = sock.recv(16)
 amount_received += len(data)
 print("received %s" % data)
finally:
 sock.close()

Creo que el código se explica solo. Tras realizar una correcta conexión, el cliente intercambiará un mensaje con el servidor. En ese trozo es donde se debería implementar el protocolo de aplicación que necesitemos.

La ejecución del cliente en el servidor implementado en las imágenes

Captura de pantalla de 2014-02-14 18:42:56

Referencias:

  1. Python para todos, de Raúl González Duque
  2. Entrada con la versión sencilla del servidor
  3. Entrada con la versión concurrente del servidor

SD: Esqueleto de servidor concurrente con Python (y2)

Continuando con programas en Python (que para eso es el lenguaje de programación de moda ), veremos un servidor que acepta múltiples peticiones de clientes, cada una de ellas, atendidas por un servicial hijo. Esta constituye la principal mejora que debíamos añadir  al código de servidor básico que vimos en la entrada referenciada.

Aunque sea solo sea un esqueleto de un proceso servidor, para que cumpla un mínimo, debe ser concurrente para que admita más de un cliente. Pensando en estos términos, en seguida nos puede venir a la memoria los Threads, pero con estos Python nos está “engañando” de forma vil. Si consultamos la información sobre implementación y threads en [1], nos daremos cuenta que, al usarlos, lo que Python estará haciendo es que en único proceso irá intercambiando la ejecución de los “hilos” que ha creado para dar la sensación que todo se está ejecutando en paralelo, pero no lo es.

Por esto, para que sea concurrente, con Python he usado procesos y no hilos (aunque podréis encontrar versiones con hilos como esta, esta y esta)

El código:

#!/usr/bin/python
#encoding:utf-8
try:
    import socket,sys,optparse
except:
    print("Error running 'import socket,sys,optparse'. Maybe you have to install some python library")
try:
    import sys,os
except:
    print("Error running 'import  sys,os'. Maybe you have to install some python library")

def comunication(connection, addr):
    print 'Connected with ' + addr[0] + ':' + str(addr[1])
    while True:
        #receive data
        try:
            data = connection.recv(1024)
            #process data
            if not data:
                break
            #elif re.match(data, "QUITn."):
            elif data == "QUITn":
                print 'Received data: ' + data + " from " + addr[0] + ':' + str(addr[1])
                reply = 'BYE'
                connection.send(reply) #send reply
                break
            else:
                print 'Received data: ' + data + " from " + addr[0] + ':' + str(addr[1])
                reply = 'OK...' + data
                connection.send(reply) #send reply
        except KeyboardInterrupt:
            print
            print "Stopped server."
            break
    connection.shutdown(socket.SHUT_RDWR)    
    return

def main():
    parser = optparse.OptionParser("usage%prog " + "-d <ip> -p <target port>")
    parser.add_option('-d', dest = 'ip', type = 'string', help = 'Please, specify the target server')
    parser.add_option('-p', dest = 'port', type = 'string', help = 'Please, specify the target port')
    parser.add_option('-q', dest = 'queue', type = 'string', help = 'Please, specify the queue size')
    parser.add_option('-P', dest = 'process', type = 'string', help = 'Please, specify the maximun number of process')
    (options, args) = parser.parse_args()
    if (options.ip == None):
        print '[-] You must specify a ip direction to listen to.'
        exit(0)
    if (options.port == None):
        print '[-] You must specify a port.'
        exit(0)
    HOST=options.ip
    PORT=int(options.port)
    if (options.queue == None):
        QUEUE=1
    else:
        QUEUE=int(options.queue)
    if (options.process == None):
        PROCESS=2
    else:
        PROCESS=int(options.process)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # Create a socket object
    print 'Socket created'
    try:
        s.bind((HOST, PORT)) # Bind to the port
    except socket.error , msg:
        print 'Bind failed. Error code: ' + str(msg[0]) + 'Error message: ' + msg[1]
        sys.exit()
    print 'Socket bind complete'
    s.listen(QUEUE) # Now wait for client connection
    print 'Socket now listening'
    while True:
        try:
            conn, addr = s.accept()
        except KeyboardInterrupt:
            print
            print "Stopped server."
            s.close()
            break
        except socket.error, msg:
            print "Socket error! %s" % msg
            s.close()
            break
        try:
            pid=os.fork()
            if (pid==0): #Child
                comunication(conn, addr)
                conn.close()
                s.close()
                print("Bye child")
                exit(0)
            else: #Father
                print("Made child %s..." % pid)
        except OSError, e:
            sys.stderr.write("Error making process child (fork)")
            break
if __name__ == "__main__":
    main()

Aspectos que debemos tener en cuenta de este esqueleto:

  • En la función comunication(…) es donde pondremos el manejo de la comunicación entre cliente y servidor: es decir, se implementará el protocolo de aplicación que permitirá la conversación entre cliente (para solicitar un recurso/know how) y el servidor.
  • Debemos finalizar educadamente (shutdown)
  • El código que ejecutará el hijo es el comprendido dentro del ‘if PID==0′

En la siguiente imagen se puede comprobar que al recibir 4 peticiones de servicio, el número de procesos serán 5: 4 hijos, uno por cada conexión (y que se encargan de gestionarla) y el padre que está a la espera de más peticiones. Podemos limitar el número máximo de procesos que creamos, si lo consideramos conveniente (se programa y punto 😉 )

Captura de pantalla de 2014-02-03 17:27:42Referencias:

  1. Python para todos, de Raúl González Duque
  2. Entrada con la versión sencilla del servidor

Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más información.