2010-03-01 7 views
44

Powiedz, że mam 2 procesy, ProcessA i ProcessB. Jeśli wykonuję int fd=open(somefile) w ProcessA, czy mogę przekazać wartość deskryptora pliku fd przez IPC do ProcessB i zmusić go do manipulowania tym samym plikiem?Czy mogę udostępnić deskryptor pliku innemu procesowi na Linuksie lub czy jest on lokalny w procesie?

+1

Duplikat: http://stackoverflow.com/questions/1997622 – Zitrax

+1

Zobacz [to pytanie] (http://stackoverflow.com/questions/1997622). – bmargulies

Odpowiedz

46

Możesz przekazać deskryptor pliku do innego procesu przez gniazda unix domain. Oto kod przekazać taką deskryptor pliku, zaczerpnięte z 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 */ 

A oto kod aby otrzymać deskryptor

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

Należy jednak zauważyć, że rzeczywista wartość liczbowa deskryptora pliku będzie na ogół różna w obu procesach. – caf

+1

Możesz przekazać * * w ten sposób. Nie oznacza to jednak, że będzie działać jako deskryptor pliku dla tego samego pliku po obu stronach. – EJP

+4

@EJP Pomysł z SCM_RIGHTS jest taki, że będzie. Chociaż nikt nie przychodzi mi do głowy, jestem pewien, że są pewne zastrzeżenia. (tzn. koncepcja działa prawie jak dup(), ale między niezwiązanymi procesami) – nos

2

Można użyć NOS metoda opisana w tym wątku, albo (bardziej konwencjonalny) sposób, dzieląc go między powiązanymi procesami (zazwyczaj rodzic-dziecko lub rodzeństwo) poprzez jego utworzenie, rozwidlone procesy automatycznie otrzymują kopię.

Rzeczywiście, procesy rozwidlone pobierają wszystkie twoje dyski FD i mogą z nich korzystać, chyba że je zamkną (co jest generalnie dobrym pomysłem).

Dlatego jeśli rodzic wyświetli dwoje dzieci, jeśli oba mają deskryptor pliku, który nie został zamknięty, jest on udostępniany (nawet jeśli rodzic następnie je zamknie). Może to być na przykład fajka z jednego dziecka na drugie. W ten sposób powłoka przekierowuje, jak

ls -l | more 
.

2

Należy zauważyć, że w przykładzie powyżej, ustawienie zmiennych przy odbiorze, jak:

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; 

nie jest wymagane. Cała idea struktury wiadomości z nagłówkami polega na tym, że strona odbierająca nie musi wiedzieć, co czyta, i może poprzez sprawdzenie (pierwszego) nagłówka, jakiego rodzaju wiadomości jest i czego się spodziewać.

+0

Chociaż technicznie poprawne, istnieje dobry powód, aby określić bufor w tym przypadku: w celu wysłania wiadomości OOB (komunikat kontrolny gniazda w tym przypadku), musisz podać niepusty komunikat (zobacz unix_stream_sendmsg, np. http://lxr.free-electrons.com/source/net/unix/af_unix.c#L1836). Podczas odbierania bez iovec, Linux będzie dostarczał tę wiadomość w kółko. Dlatego, aby przeczytać więcej niż jedną wiadomość OOB, MUSISZ przeczytać dane wiadomości w pewnym momencie. – Michael

1

Jeśli oba procesy należą do tego samego użytkownika, można po prostu skorzystać z 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); 

Oczywiście potrzebny jest jakiś mechanizm IPC, aby udostępnić wartość SOURCE_FD. Zobacz np. "Linux C: upon receiving a signal, is it possible to know the PID of the sender?".

+0

Czy chcesz powiedzieć "/ proc /% d/fd /% d" w snprintf? – kaiwan

+0

@kaiwan, tak. Naprawiono, dziękuję! – kay

Powiązane problemy