El problema con la función fork de UNIX/LINUX no es adecuada para compartir

Introducción a la causa

Un amigo me hizo una socketpregunta relacionada con la comunicación, el problema que hay que resolver es el siguiente:

Es necesario que exista un proceso de servidor. El proceso de servidor escuchará y será responsable de establecer una conexión de socket con el cliente. Pueden existir múltiples procesos de cliente al mismo tiempo. Los procesos de cliente pueden comunicarse entre sí, pero no se establecerán conexiones de socket entre clientes La comunicación se realiza mediante el envío de información al proceso del servidor, y el proceso del servidor busca el indicador de socket establecido con el cliente de destino para enviar la información.

Para resolver el problema anterior, este amigo personalizó una lista vinculada en el proceso del servidor para guardar el socketnombre del cliente y socketel identificador correspondiente de la conexión establecida con el servidor. Cuando la conexión se establece con éxito, se inserta una lista vinculada en la lista vinculada. .Elemento, cuando un cliente envía un comando de salida, el correspondiente se socketcierra y la información se elimina de la lista vinculada. Cada vez que un cliente necesita enviar datos, debe recorrer la lista vinculada y luego encontrar el objetivo para la comunicación socket.

forkAl mismo tiempo, para cumplir con los requisitos de comunicación, se debe utilizar multiproceso o subproceso múltiple. Este amigo crea un subproceso para manejar la comunicación con un cliente cada vez que escucha el código del servidor. , este Un amigo solicitó una memoria compartida en el código para guardar el encabezado de la lista vinculada.

Problemas que surgen

socketLa descripción anterior parece muy razonable, pero ocurrió un fenómeno extraño después de su ejecución: cuando se inició el proceso del servidor, se iniciaron dos procesos del cliente al mismo tiempo para establecer conexiones con el proceso del servidor. Al enviar un mensaje client1aclient2

¿Dónde aparece el bucle infinito? Según la depuración de un amigo, descubrió que en el proceso de consultar el propósito en la lista vinculada socket, apareció un bucle infinito. Descubrió que la lista vinculada se convirtió en un anillo y, al mismo tiempo, solo había un nodo en la lista vinculada. es decir, solo tenía su propia información de nodo, lo que daba como resultado que la lista vinculada siempre estuviera en ciclo.

La causa del problema

¿Por qué ocurre un bucle infinito? La clave del problema radica en el uso que hace forkde esta función. Para describir nuestro avance de manera más simple, se realiza un experimento simple:

Escriba un programa, cree una variable al mismo tiempo, asígnele un valor y luego llame a fork() para ver si tanto el proceso padre como el hijo pueden obtener el valor correcto de la variable e intente modificarlo al mismo tiempo. el valor de la variable en el proceso hijo. El proceso padre espera a que se complete el proceso hijo. Luego verifica el valor de la variable para ver si el valor se ha modificado correctamente.

Nota: La pregunta anterior en realidad proviene de "Introducción a los sistemas operativos" , que es un libro muy bueno y se recomienda que todos lo lean.

El código para el experimento anterior se puede escribir fácilmente:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
    
    
    int x = 137;
    int ret = fork();
    if (ret < 0) {
    
    
        printf("one error occurs when fork(), the ret: %d.\n", ret);
        exit(ret);
    } else if (ret == 0) {
    
    
        printf("child process (%d), the value of x is: %d.\n", getpid(), x);
        x *= 2;
    } else {
    
    
        printf("parent process (%d), the value of x is: %d.\n", getpid(), x);
        ret = wait(NULL);
        printf("after child process write the varible, parent process (%d), the value of x is: %d.\n", getpid(), x);
    }
    return 0;
}

Los resultados de ejecución son los siguientes:

proceso padre (110), el valor de x es: 137.
proceso hijo (111), el valor de x es: 137.
después del proceso hijo escribe la variable, proceso padre (110), el valor de x es: 137.

Se puede ver que el proceso hijo no conoce la modificación de variables por parte del proceso padre. Este es el problema: forkel proceso creado copiará toda la información del proceso padre (tenga en cuenta que se copia en lugar de compartirse) y forkla modificación de variables por parte de un proceso no es visible para el otro proceso.

La copia mencionada anteriormente se refiere a copiar el proceso por completo y colocarlo en otra área de memoria para su ejecución, hay un problema, es decir, todos los valores de los dos procesos son iguales inicialmente y las direcciones virtuales también son lo mismo (Esto se refleja en el hecho de que si dos procesos solicitan memoria del montón inmediatamente, obtendrán la misma dirección. Esto también es fácil de entender. Dado que es una copia, el estado natural del montón también es el mismo (Y se obtiene la misma dirección. El espacio virtual también es razonable, pero el espacio físico real es diferente).

Con la base anterior, echemos un vistazo y pensemos por qué ocurre un bucle infinito.

La clave del problema es que la memoria compartida y la memoria compartida se usan al mismo tiempo fork. La memoria compartida utilizada puede garantizar que las modificaciones se realicen en el proceso secundario y puedan ser vistas por otros procesos. De hecho, simulémoslo:

  • Primero, el proceso del servidor se inicia y solicita la memoria compartida. Registramos la dirección como 0 y usamos headp_addressuna variable para guardar la dirección (es decir headp_address=0), dado que la lista vinculada en este momento está vacía, el encabezado está inicialmente vacío, es decir, ejecución *headp_address=NULL;
  • client1Inicio. En este momento, se inserta un nuevo nodo en la lista vinculada. El nuevo nodo solicita mallocmemoria. Suponiendo que la dirección solicitada en este momento es 4 , luego insertamos el nodo en el encabezado de la lista vinculada y lo ejecutamos *headp_address=new_node. En este momento new_node=4;
  • client2Inicio, en este momento también se realiza (aprobado malloc) la aplicación de memoria del nuevo nodo, según la conclusión anterior, en este momento se solicitará la misma memoria, es decir, se seguirá solicitando 4(aquí está la memoria virtual ), pero debido a la modificación de la memoria compartida, los dos procesos pueden encontrar que ya hay un elemento en la lista vinculada en este momento, por lo que utilizan el método de inserción de encabezado para insertar el nodo actual al comienzo de la lista vinculada , es decir, ejecutar:
    new_node->next = *headp_address;
    *headp_address=new_node;
    
    De acuerdo a la información anterior *headp_address=4, sabemos que después de ejecutar la declaración anterior, encontramos que *headp_address=4, *headp_address->next=4; es decir, la lista enlazada se convierte en un anillo en este momento, por lo que ocurrirá un bucle infinito cuando la información se envíe a través de un cliente más adelante. .

resolución de problemas

Varias soluciones están disponibles aquí:

  • Al utilizar pthreadel reemplazo fork(), pthreadcada proceso puede ver las modificaciones realizadas por subprocesos, lo que significa que está más sesgado hacia la realización de funciones compartidas en lugar de fork()copiar. (De hecho, ambas funciones llaman a llamadas al sistema clone, pero pthread_createse agregan indicadores al llamar CLONE_VMpara permitir el uso compartido).
  • Si es necesario usarlo fork(), el intercambio debe lograrse a través de la memoria compartida, es decir, la aplicación y liberación de la lista vinculada también debe realizarse en la memoria compartida. En este momento, se debe utilizar una lista vinculada estática para la administración (es decir , una lista enlazada con un tamaño de espacio fijo).
  • Si desea utilizar fork()listas vinculadas dinámicas al mismo tiempo, no se me ha ocurrido ningún método mejor.

Supongo que te gusta

Origin blog.csdn.net/qq_45523675/article/details/131783250
Recomendado
Clasificación