2010-03-01 13 views

Respuesta

46

Puede pasar un descriptor de archivo a otro proceso a través de sockets unix domain. Aquí está el código para pasar un descriptor de tal archivo, tomada de Unix Network Programming

ssize_t 
write_fd(int fd, void *ptr, size_t nbytes, int sendfd) 
{ 
    struct msghdr msg; 
    struct iovec iov[1]; 

#ifdef HAVE_MSGHDR_MSG_CONTROL 
    union { 
     struct cmsghdr cm; 
     char    control[CMSG_SPACE(sizeof(int))]; 
    } control_un; 
    struct cmsghdr *cmptr; 

    msg.msg_control = control_un.control; 
    msg.msg_controllen = sizeof(control_un.control); 

    cmptr = CMSG_FIRSTHDR(&msg); 
    cmptr->cmsg_len = CMSG_LEN(sizeof(int)); 
    cmptr->cmsg_level = SOL_SOCKET; 
    cmptr->cmsg_type = SCM_RIGHTS; 
    *((int *) CMSG_DATA(cmptr)) = sendfd; 
#else 
    msg.msg_accrights = (caddr_t) &sendfd; 
    msg.msg_accrightslen = sizeof(int); 
#endif 

    msg.msg_name = NULL; 
    msg.msg_namelen = 0; 

    iov[0].iov_base = ptr; 
    iov[0].iov_len = nbytes; 
    msg.msg_iov = iov; 
    msg.msg_iovlen = 1; 

    return(sendmsg(fd, &msg, 0)); 
} 
/* end write_fd */ 

Y aquí está el código para recibir el descriptor de archivo

ssize_t 
read_fd(int fd, void *ptr, size_t nbytes, int *recvfd) 
{ 
    struct msghdr msg; 
    struct iovec iov[1]; 
    ssize_t   n; 
    int    newfd; 

#ifdef HAVE_MSGHDR_MSG_CONTROL 
    union { 
     struct cmsghdr cm; 
     char    control[CMSG_SPACE(sizeof(int))]; 
    } control_un; 
    struct cmsghdr *cmptr; 

    msg.msg_control = control_un.control; 
    msg.msg_controllen = sizeof(control_un.control); 
#else 
    msg.msg_accrights = (caddr_t) &newfd; 
    msg.msg_accrightslen = sizeof(int); 
#endif 

    msg.msg_name = NULL; 
    msg.msg_namelen = 0; 

    iov[0].iov_base = ptr; 
    iov[0].iov_len = nbytes; 
    msg.msg_iov = iov; 
    msg.msg_iovlen = 1; 

    if ((n = recvmsg(fd, &msg, 0)) <= 0) 
     return(n); 

#ifdef HAVE_MSGHDR_MSG_CONTROL 
    if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL && 
     cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { 
     if (cmptr->cmsg_level != SOL_SOCKET) 
      err_quit("control level != SOL_SOCKET"); 
     if (cmptr->cmsg_type != SCM_RIGHTS) 
      err_quit("control type != SCM_RIGHTS"); 
     *recvfd = *((int *) CMSG_DATA(cmptr)); 
    } else 
     *recvfd = -1;  /* descriptor was not passed */ 
#else 
/* *INDENT-OFF* */ 
    if (msg.msg_accrightslen == sizeof(int)) 
     *recvfd = newfd; 
    else 
     *recvfd = -1;  /* descriptor was not passed */ 
/* *INDENT-ON* */ 
#endif 

    return(n); 
} 
/* end read_fd */ 
+13

Sin embargo, tenga en cuenta que el valor numérico real del descriptor de archivo será, en general, diferente en los dos procesos. – caf

+1

Puede pasar el * número * de esta manera. Eso no significa mágicamente que funcione como un descriptor de archivo para el mismo archivo en ambos extremos. – EJP

+4

@EJP La idea con SCM_RIGHTS es que lo hará. Aunque ninguno viene a la mente, estoy seguro de que hay algunas advertencias. (es decir, el concepto funciona más o menos como dup(), pero entre procesos no relacionados) – nos

2

Puede utilizar los números método descrito en este tema, con el (más convencional) forma, al compartirlo entre los procesos relacionados (típicamente padres-hijos o hermanos) al tenerlo creado, los procesos bifurcados reciben automáticamente una copia.

De hecho, los procesos bifurcados obtienen todos sus FD y pueden usarlos a menos que los cierren (lo que generalmente es una buena idea).

Por lo tanto, si un padre usa dos hijos, si ambos tienen un descriptor de archivo que no cerraron, ahora se comparte (incluso si el padre lo cierra posteriormente). Esto podría, por ejemplo, ser una tubería de un niño a otro. Así es como redireccionamientos shell como

ls -l | more 

Trabajo. No se requiere

msg.msg_name = NULL; 
msg.msg_namelen = 0; 

iov[0].iov_base = ptr; 
iov[0].iov_len = nbytes; 
msg.msg_iov = iov; 
msg.msg_iovlen = 1; 

:

2

Tenga en cuenta que en el ejemplo de arriba, el ajuste de las variables cuando se recibe, como. La idea de una estructura de mensaje con encabezados es que el sitio receptor no tiene que saber lo que lee y puede verificar el (primer) encabezado, qué tipo de mensaje es y qué esperar.

+0

Aunque es técnicamente correcto, hay una buena razón para especificar un búfer en este caso: para enviar un mensaje OOB (el mensaje de control de socket) en este caso), debe especificar un mensaje no vacío (consulte unix_stream_sendmsg, por ejemplo, http://lxr.free-electrons.com/source/net/unix/af_unix.c#L1836). Al recibir sin un iovec, Linux entregará ese mensaje una y otra vez. Por lo tanto, para leer más de un mensaje OOB, DEBE leer los datos del mensaje en algún momento. – Michael

1

Si ambos procesos pertenecen al mismo usuario, entonces simplemente puede hacer uso de los procfs.

char fd_path[64]; // actual maximal length: 37 for 64bit systems 
snprintf(fd_path, sizeof(fd_path), "/proc/%d/fd/%d", SOURCE_PID, SOURCE_FD); 
int new_fd = open(fd_path, O_RDWR); 

Por supuesto que tendría que algún mecanismo IPC para compartir el valor de SOURCE_FD. Ver p. "Linux C: upon receiving a signal, is it possible to know the PID of the sender?".

+0

¿Quiere decir "/ proc /% d/fd /% d" dentro de snprintf? – kaiwan

+0

@kaiwan, sí. Reparado, gracias! – kay

Cuestiones relacionadas