2014-04-24 59 views
1

有一種情況,一個進程打開串口並配置它的終端參數。如何將這個文件描述符共享到另外兩個也將通過串口傳輸數據的進程。 是否可以共享打開的文件描述符?如果是的話,那怎麼可能?在不同進程之間共享打開的文件描述符

編輯:注意一件事這三個過程是不相關的進程(不是父/子進程)

+0

您必須擁有一個擁有串口的主進程,另一個進程將連接到主進程並請求主進程通過套接字發送數據。 –

+1

請參閱此鏈接http://www.lst.de/~okir/blackhats/node121.html在此答案中給出:http://stackoverflow.com/a/9913281/694576 – alk

+2

可能的重複[傳遞文件描述符從一個程序到另一個在同一主機上使用UNIX套接字](http://stackoverflow.com/questions/9911613/passing-file-descriptor-from-one-program-to-another-on-the-same-host-using- unix) – alk

回答

3

最簡單的事情是在分離其他進程之前打開並配置文件描述符。這將爲您節省最多的麻煩。

假設這是不可能的,有一種方法。

創建一個unix域套接字。讓主進程在該套接字上偵聽。只要有人連接到它,發送SCM_RIGHTS cmsg。在我的Linux系統上,這是在手冊頁「cmsg(3)」中記錄的。在我的MacOS(我編寫示例代碼的地方),我無法找到它的記錄位置。

現在,在需要特殊文件描述符副本的進程中,打開unix域套接字,讀取cmsg,並且您現在擁有文件描述符。

下面是一些示例代碼。這很長,因爲整個過程非常冗長。它會做的是不共享任何文件描述符,它將描述符發送到文件'/ tmp/magic'到客戶端,客戶端將寫入一條消息給它。傳遞的文件描述符位於客戶端和服務器的本地變量magic中。

#include <sys/socket.h> 
#include <sys/un.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <err.h> 
#include <fcntl.h> 
#include <assert.h> 
#include <string.h> 
#include <stdlib.h> 

#define SOCKET_NAME "/tmp/socket" 

#define SPECIAL_FILE "/tmp/magic" 

void 
server(void) 
{ 
    struct sockaddr_un sun = { 0 }; 
    socklen_t sunlen; 
    int l, fd, magic; 

    if ((magic = open(SPECIAL_FILE, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) 
     err(1, "open"); 

    if ((l = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 
     err(1, "server socket"); 

    unlink(SOCKET_NAME); 

    sun.sun_family = AF_UNIX; 
    snprintf(sun.sun_path, sizeof(sun.sun_path), SOCKET_NAME); 

    if (bind(l, (struct sockaddr *)&sun, sizeof(sun)) == -1) 
     err(1, "bind"); 

    if (listen(l, 5) == -1) 
     err(1, "listen"); 

    sunlen = sizeof(sun); 

    while ((fd = accept(l, (struct sockaddr *)&sun, &sunlen)) != -1) { 
     struct msghdr msg = { 0 }; 
     struct cmsghdr *cmsg; 
     struct iovec iov; 
     char buf[CMSG_SPACE(sizeof(magic))]; 

     iov.iov_base = (void *)"ok"; 
     iov.iov_len = 2; 

     msg.msg_iov = &iov; 
     msg.msg_iovlen = 1; 
     msg.msg_control = buf; 
     msg.msg_controllen = sizeof(buf); 
     cmsg = CMSG_FIRSTHDR(&msg); 
     cmsg->cmsg_level = SOL_SOCKET; 
     cmsg->cmsg_type = SCM_RIGHTS; 
     cmsg->cmsg_len = CMSG_LEN(sizeof(magic)); 

     *((int *)(void *)CMSG_DATA(cmsg)) = magic; 

     if (sendmsg(fd, &msg, 0) == -1) 
      err(1, "sendmsg"); 
     close(fd); 
    } 

    close(l); 
    unlink(SOCKET_NAME); 
} 

void 
client(void) 
{ 
    struct sockaddr_un sun = { 0 }; 
    struct msghdr msg = { 0 }; 
    struct cmsghdr *cmsg; 
    struct iovec iov; 
    int fd, magic; 
    char cbuf[CMSG_SPACE(sizeof(int))]; 
    char buf[16]; 

    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { 
     err(1, "socket"); 
    } 

    sun.sun_family = AF_UNIX; 
    snprintf(sun.sun_path, sizeof(sun.sun_path), SOCKET_NAME); 

    if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 
     err(1, "connect"); 
    } 

    iov.iov_base = buf; 
    iov.iov_len = sizeof(buf); 
    msg.msg_iov = &iov; 
    msg.msg_iovlen = 1; 
    msg.msg_control = cbuf; 
    msg.msg_controllen = sizeof(cbuf); 

    if (recvmsg(fd, &msg, 0) == -1) 
     err(1, "recvmsg"); 

    cmsg = CMSG_FIRSTHDR(&msg); 
    assert(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS); 
    magic = *(int *)(void *)CMSG_DATA(cmsg); 

    if (write(magic, "hello", strlen("hello")) != strlen("hello")) 
     err(1, "write(magic)"); 

    close(magic); 
    close(fd); 
} 

int 
main(int argc, char **argv) 
{ 
    switch (fork()) { 
    case 0: 
     sleep(2);  /* poor mans synchronization */ 
     printf("starting client\n"); 
     client(); 
     _exit(0); 
     break; 
    case -1: 
     err(1, "fork"); 
    default: 
     printf("starting server\n"); 
     server(); 
     exit(1); 
    } 
    return 1; 
} 

有關的例子一些注意事項:

  • 它適用於MacOS的,我沒有測試它在其他系統上,但是同樣的方法肯定能行在* BSD和Linux。我沒有在其他系統上這樣做的經驗。據稱這是POSIX,所以它應該在更多的系統上工作。
  • 應該不需要在消息中包含任何有效載荷(因此iovec中的「ok」不應該是必需的),但由於某種原因,我無法讓它在沒有消息的情況下工作。
  • 你想要一個更好的位置和權限的套接字,「/ tmp/socket」是一個真正的系統上可怕的硬編碼名稱。
  • 根據經驗我知道CMSG_ *宏在某些操作系統中導致了對齊問題。我相信在分配緩衝區和所有魔術來回之前,我做的一切都是正確的,但是在嚴格的對齊體系結構上YMMV。您可能需要分配與malloc甚至posix_memalign更好的對齊緩衝區。
+0

雖然我不是OP,但是謝謝你的努力!已經學會了東西.. :) – hek2mgl

+0

@藝術感謝您的幫助 –

0

有一種方法可以做到這一點無論是使用管道或Unix域套接字。詳細信息請見here

相關問題