2015-11-29 1 views
1

Je suis en train d'envoyer des messages entre deux processus de Mach (Pour être précis c'est Debian GNU/Hurd avec un micronoyau Mach), ce qui est le code que j'ai:Impossible d'envoyer des messages entre deux processus Mach

#define _GNU_SOURCE 

#include "machheader.h" 

void 
send_integer(mach_port_t destination, int i) 
{ 
    kern_return_t err; 
    struct integer_message message; 

    /* (i) Form the message : */ 

    /* (i.a) Fill the header fields : */ 
    message.head.msgh_bits = 
     MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND); 
    message.head.msgh_size = sizeof(struct integer_message); 
    message.head.msgh_local_port = MACH_PORT_NULL; 
    message.head.msgh_remote_port = destination; 

    /* (i.b) Explain the message type (an integer) */ 
    message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32; 
    message.type.msgt_size = 32; 
    message.type.msgt_number = 1; 
    message.type.msgt_inline = TRUE; 
    message.type.msgt_longform = FALSE; 
    message.type.msgt_deallocate = FALSE; 
    /* message.type.msgt_unused = 0; */ /* not needed, I think */ 

    /* (i.c) Fill the message with the given integer : */ 
    message.inline_integer = i; 

    /* (ii) Send the message : */ 
    err = mach_msg(&(message.head), MACH_SEND_MSG, 
      message.head.msgh_size, 0, MACH_PORT_NULL, 
      MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 

    /* (iii) Analysis of the error code; 
    if succes, print and acknowledge message and return */ 
    if(err == MACH_MSG_SUCCESS) 
     { 
     printf("success: the message was queued\n"); 
     } 
    else 
     { 
     perror("error: some unexpected error ocurred!\n"); 
     exit(err); 
     } 

    return; 
} 

/* receive_integer is a function that receives an integer from some 
    mach port; it also hides the complexity of using the mach_msg 
    primitive to the user. 

    receive_integer takes two arguments; the port where the message is going 
    to come from with an integer inside, and a pointer to an integer in where 
    the integer contained in the mentioned message will be stored. 
*/ 
void 
receive_integer(mach_port_t source, int *ip) 
{ 
    kern_return_t err; 

    struct integer_message message; 

    /* (i) Fill the little thing we know about the message : */ 
    /* message.head.msgh_size = sizeof(struct integer_message); */ 

    /* (ii) Receive the message : */ 
    err = mach_msg(&(message.head), MACH_RCV_MSG, 0, 
      message.head.msgh_size, source, 
      MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 

    if(err == MACH_MSG_SUCCESS) 
     { 
     printf("success: the message was received\n"); 
     } 
    else 
     { 
     perror("error: Some unexpected error ocurred\n"); 
     exit(err); 
     } 

    *ip = message.inline_integer; 

    return; 
} 

/* main function of the program; it does the following : 

    (i) allocate a port for receiving a message 
    (ii) send a message containing an integer; 
    it uses the send_integer function 
    (iii) receive the message and display it; 
    it uses the receive_integer function 
    (iv) deallocate the port 
*/ 
int 
main(void) 
{  
    //int s, r; /* s -> int sent, r -> int received */ 
    //mach_port_t destination;  

    kern_return_t err; 

    /* Allocate a port to receive the bootstrap message : */ 
    err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, 
     &destination); 

    if(err != KERN_SUCCESS) 
     { 
     perror("Error : could not allocate any port\n"); 
     exit(err); 
     } 

    if(!fork()){ 
     s=7; 
     send_integer(destination, s); 
    }else{ 
     receive_integer(destination, &r); 
     printf("The received integer is : %d\n", r); 
    } 

    mach_port_deallocate(mach_task_self(), destination); 

    return(r); 
} 

Et voici machheader.h:

#include <mach.h> 

#include <stdio.h> 
#include <error.h> 

#include <errno.h> 
#include <stdlib.h> 
#include <string.h> 

struct integer_message 
{ 
    mach_msg_header_t head; 
    mach_msg_type_t type; 

    int inline_integer; 
}; 

int s, r; /* s -> int sent, r -> int received */ 
mach_port_t destination; 

Quand je lance l'application, il me donne:

success: the message was queued 

wh Cela me dit que le message a été mis en file d'attente avec succès mais s'arrête là et ne continue pas à lire de la file d'attente sur le processus parent. Une idée?

Répondre

1

Ce code ne fonctionne pas à cause de la façon dont Hurd gère les ports Mach pendant fork(). Un port Mach ne peut avoir qu'un seul droit de réception, donc les ports avec droits de réception sont dupliqués pendant un fork(), tandis que les droits d'envoi sont copiés (comme les descripteurs de fichiers). Dans ce cas, vous avez un droit de réception dans destination lorsque vous fork(), donc l'enfant obtient un port complètement nouveau. Chaque processus a alors un droit de réception sur son propre port, et il n'y a pas de connexion entre les deux ports.

La façon la plus simple que j'ai trouvé à faire ce que vous voulez est Kalle Niemitalo's suggestion: Utiliser Hurd serveur proc, qui détient envoyer des droits aux ports de travail de tous les processus dans le système, et leur donnera à tout processus avec correspondance UID. Un port de tâche vous permet de faire presque tout ce que vous voulez pour un processus: modifier sa mémoire, démarrer et arrêter ses threads ... et changer son espace de port. Par conséquent, dans ce cas, vous voulez que l'enfant envoie un message au parent. L'enfant peut utiliser le PID du parent pour fournir un port de tâche au parent, puis extraire un droit d'envoi au port destination du parent, puis envoyer un message à ce port. Comme ceci:

if(!fork()){ 
    /* fork allocated a receive right in the child process, but it's not 
     for the same port as in the parent process. Deallocate that. */ 
    mach_port_mod_refs (mach_task_self(), destination, 
         MACH_PORT_RIGHT_RECEIVE, -1); 

    /* get a send right to the parent's task port */ 
    pid_t parent_pid = getppid(); 
    task_t parent_task = pid2task (parent_pid); 

    /* extract a send right to the parent's destination port */ 
    mach_port_t send_right; 
    mach_port_type_t send_type; 

    mach_port_extract_right (parent_task, destination, 
          MACH_MSG_TYPE_MAKE_SEND, 
          &send_right, &send_type); 

    /* transmit to "send_right", not "destination" */ 
    s=7; 
    send_integer(send_right, s); 
}else{ 
    receive_integer(destination, &r); 
    printf("The received integer is : %d\n", r); 
} 

Il peut sembler un peu étrange qu'un processus fils puisse fonctionner sur un processus parent comme celui-ci; mais c'est une caractéristique de Hurd. proc est un serveur hautement privilégié; il autorise l'accès car les deux processus ont le même UID.

Une alternative serait de modifier le comportement de Hurd sur fork(), mais si peu d'applications échangent des ports Mach bruts que je ne pense pas que cela ait de sens. En tout cas, fork() sur Hurd n'est pas un processus trivial: vous pouvez trouver les détails here. Les lignes 207 à 449 sont celles où les droits de port sont copiés sur l'enfant; juste un bref coup d'œil devrait vous donner une idée à la fois de la complexité de la fourche de Hurd() et de la raison pour laquelle votre idée originale n'a pas fonctionné.