Programación de redes TCP/IP Capítulo 11: Comunicación entre procesos

Conceptos básicos de la comunicación entre procesos

En el capítulo anterior, mencionamos que el proceso en sí tiene su propio espacio de memoria independiente, y los procesos existen independientemente unos de otros. Por lo tanto, sin ningún mecanismo que lo soporte, podemos considerar que los procesos existen aislados unos de otros.

Pero hasta cierto punto, los procesos también necesitan "comunicarse". A continuación se presenta formalmente el método de comunicación entre procesos.

Comunicación entre procesos a través de tuberías.

Para lograr la comunicación entre procesos, se debe crear una tubería. La tubería no es un recurso que pertenezca al proceso, pero al igual que un socket, pertenece al sistema operativo (es decir, no es el objeto de copia de la función de bifurcación). Entonces, dos procesos se comunican a través del espacio de memoria proporcionado por el sistema operativo. La función que crea la canalización se describe a continuación.

#include<unistd.h>
int pipe(int filedes[2]);//成功时返回0,失败时返回-1。
    filedes[0]  //通过管道接收数据时使用的文件描述符,即管道出口。
    filedes[1]  //通过管道传输数据时使用的文件描述符,即管道入口。

Cuando se llama a la función anterior con un valor de dirección de matriz int de 2 elementos como parámetro, hay dos descriptores de archivo almacenados en la matriz, que se utilizarán como salida y entrada de la canalización. Cuando el proceso principal llame a esta función, creará una tubería y obtendrá el descriptor de archivo correspondiente a la entrada y salida. En este momento, el proceso principal puede leer y escribir la misma tubería (creo que todos han hecho tales experimentos). Pero el propósito del proceso principal es intercambiar datos con el proceso secundario, por lo que se debe pasar un descriptor de archivo en la entrada o salida al proceso secundario. ¿Cómo completar la transferencia? La respuesta es llamar a la función fork. Demuéstrelo con el siguiente ejemplo.

#include <stdio.h>
#include <unistd.h>#
define BUF_SIZE 30

int main(int argc, char *argv[]){
    int fds[2];
    char str[]="who are you?";
    char buf[BUF_SIZE];
    pid_t pid;

    pipe(fds);
    pid=fork();
    if(pid==0)write(fds[1], str, sizeof(str));
    else{
       read(fds[0], buf, BUF_SIZE);
       puts(buf);
    }
    return 0;
}

El punto clave del código anterior es que tanto el proceso principal como el secundario pueden acceder a la ruta de E/S de la canalización, pero el proceso secundario solo usa la ruta de entrada y el proceso principal solo usa la ruta de salida.

Comunicación bidireccional entre procesos a través de pipes

Primero dé un código de muestra para una pequeña discusión

#include<stdio.h>
#include<unistd.h>
#define BUF_SIZE 30

int main(int argc, char *argv[]){
    int fds[2];
    char str1[]="who are you?";
    char str2[]="Thank you for your message";
    char buf[BUF_SIZE];
    pid_t pid;

    pipe(fds);
    pid=fork();
    if(pid==0){
       write(fds[1], str1, sizeof(str1));
       sleep(2);
       read(fds[0], buf, BUF_SIZE);
       printf("Child proc output: %s \n",buf);
    }
    else{
       read(fds[0], buf, BUF_SIZE);
       printf("Parent proc output: %s \n",buf);
       write(fds[1], str2, sizeof(str2));
       sleep(3);
    }
return 0;
}

Los resultados de la ejecución deben ser consistentes con las expectativas de todos. Esta vez comente la línea 18 de código y ejecútelo nuevamente (asegúrese de hacerlo usted mismo). Aunque
esta línea de código solo retrasa el tiempo de ejecución en 2 segundos, ha arrojado un error de tiempo de ejecución. ¿Cuál es la razón?
"Al pasar datos a la tubería, el proceso que lee primero se llevará los datos".

Aquí, el monitor es un espacio de direcciones asignado por el sistema operativo, que no pertenece a esos dos procesos. Luego, cuando se ejecutan dos programas, un proceso pone contenido en esta "tierra de nadie". Si la parte que lo puso no espera unos segundos sino que se agota directamente, entonces el contenido que puso se quitará por sí solo. Luego de agotada la porción de tiempo, cuando sea el turno de otro proceso, se bloqueará indefinidamente en la función de lectura, generando así un error.

Como puede ver en el ejemplo anterior, la comunicación bidireccional con solo 1 tubería no es trivial. Para lograr esto, el programa necesita predecir y controlar el flujo de operación, que es diferente en cada sistema y puede considerarse una tarea imposible. Siendo ese el caso, ¿cómo llevar a cabo una comunicación bidireccional?
"Crear 2 canalizaciones".

Es muy simple, una canalización no puede completar la tarea de comunicación bidireccional, por lo que debe crear dos canalizaciones, cada una responsable de diferentes flujos de datos
.

A continuación se muestra el código de muestra

#include<stdio.h>
#include<unistd.h>
#define BUF_SIZE 30

int main(int argc, char *argv[]){
    int fds1[2], fds2[2];
    char stri[]="who are you?";
    char str2[]="Thank you for your message";
    char buf[BUF_SIZE];
    pidt pid;

    pipe(fds1), pipe(fds2);
    pid=fork();
    if(pid==0){
       write(fds1[1], str1, sizeof(str1));
       read(fds2[0], buf, BUF_SIZE);
       printf("Child proc output: %s \n", buf);
    }
    else{
       read(fds1[0], buf, BUF_SIZE);
       printf("Parent proc output: %s \n", buf);
       write(fds2[1], str2, sizeof(str2));
       sleep(3);
    }
return 0;
}

utilizar la comunicación entre procesos

Lado del servidor Echo que guarda mensajes

Expandamos el servidor de eco en el Capítulo 10 y agreguemos la siguiente función: "Guarde las cadenas transmitidas por el cliente de eco en archivos en secuencia".


Deseo delegar esta tarea a otro proceso. En otras palabras, cree un proceso separado y lea la información de la cadena de caracteres del proceso que brinda servicios al cliente
. Por supuesto, este proceso requiere la creación de una tubería para recibir datos.
A continuación se dan ejemplos. Este ejemplo funcionará con cualquier cliente de eco.

#include <'头声明与第10章的示例一致。'>
#define BUF_SIZE 100
void error_handling(char *message);
void read_childproc(int sig);

int main(int argc, char *argv[]){
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, cint_adr;
    int fds[2];
    pid_t pid;
    struct sigaction act;
    socklen_t adr_sz;
    int str_len, state;
    char buf[BUF_SIZE];

    if(argc!=2) {
       printf("Usage : %s <port>\n", argv[0]);
       exit(1);
    }

    act.sa_handler=read_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    state=sigaction(SIGCHLD, &act, 0);

    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));

    if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
        error_handling("bind() error");
    if(listen(serv_sock,5)==-1)
        error_handiing("listen() error");

    pipe(fds);
    pid=fork();
    if(pid==0){
         FILE *fp=fopen("echomsg.txt","wt");
         char msgbuf[BUF_SIZE];
         int i, len;
         for(i=0;i<10;i++){
             len=read(fds[0], msgbuf, BUF_SIZE);
             fwrite((void*)msgbuf,1, len, fp);
         }
         fclose(fp);
         return 0;
    }


    while(1){
         adr_sz=sizeof(clnt_adr);
         clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr, &adr_sz);
         if(clnt_sock==-1)
             continue;
         else
             puts("new client connected...");
         pid=fork();
         if(pid==0){
             close(serv_sock);
             while((str_len=read(clnt_sock, buf, BUF_SIZE))!=0){
             write(clnt_sock, buf, str_len);//回声
             write(fds[1], buf, str_len);   //记录消息
         }
         close(clnt_sock);
         puts("client disconnected...");
         return 0;
         }
         else close(clnt_sock);
         }
         close(serv_sock);
         return 0;
}

void read_childproc(int sig){
//与上一章示例一致,故省略。
}

void error_handling(char* message){
//与上一章示例一致,故省略。
}

Supongo que te gusta

Origin blog.csdn.net/Reol99999/article/details/131748024
Recomendado
Clasificación