通过共享内存在两个进程之间传递套接字描述符

3
我正在尝试通过两个进程间的共享内存区域和Linux内核来传递 socket 描述符,我的目标是在一个进程中打开 TCP socket,并使用第二个进程在同一个 socket 中写入数据。如果没有 socket 管道,这是否可能?
socket 描述符是一个 int 类型,在 Linux 中如何获取描述符的本机形式?如果我只是将本机描述符从一个进程传递到另一个进程,我能写入已打开的 TCP socket 吗?

3
这只是一个数字。你需要以正确的方式传递它,才能使它有任何意义。 - Ignacio Vazquez-Abrams
4
不可能使用共享内存在两个进程之间传递套接字描述符。请使用UNIX域套接字(AF_UNIX)上的sendmsg()调用。 - alk
相关链接:https://dev59.com/QnNA5IYBdhLWcg3wku3O - alk
如果我只是从一个进程将原生描述符传递到另一个进程,这也不起作用。请参见此处:https://dev59.com/xW435IYBdhLWcg3wuicn - alk
请纠正我,如果我错了:UNIX 域套接字只是进程间通信的一种解决方案,它传递有关文件描述符或套接字描述符的某些信息。如果我将 UNIX 套接字传输的完整信息放入共享内存中,并由第二个进程读取此信息,这能行吗? - Mondher123
不,它不会。将文件描述符从一个进程传输到另一个进程的唯一方法是通过使用sendmsg请求内核执行该特定操作。 - zwol
3个回答

3
不,您不能仅使用某些替代方法来传输与“sendmsg”调用相同的“内容”。当您“传递文件描述符”时,实际上正在传输的是对内核内部文件对象的访问权限。cmsg结构只是一种格式化请求的方法,其中您说“我想复制此打开的文件对象,并允许读取此套接字的进程访问它”。名称“SCM_RIGHTS”是一个提示,您传输的本质上是“权限”。由于请求涉及具有安全影响的内核内部对象的操作,因此您不能绕过它。您必须进行系统调用。而“sendmsg”就是这个系统调用。 (还有其他将fd传递给API的方式...我认为在SysV上有一些流之类的东西。我不知道那个是否仍然存在于任何最近的操作系统中。至少对于BSD和Linux,“sendmsg”与“SCM_RIGHTS”是正确的方法。)总体而言,这正是msg和cmsg之间的区别:cmsg用于内核执行更多操作而不仅仅是从套接字的一端复制一些字节到另一端的操作。

我认为这个问题还有另一个方面,不是关于跳过sendmsg()的问题,而是关于是否可以在同一文件上最终执行sendmsg()recvmsg(),该文件已经对两个进程共享(即避免使用pipe()/socketpair())。 - init_js

0
使用Unix套接字发送文件描述符
static int
send_file_descriptor(
  int socket, /* Socket through which the file descriptor is passed */
  int fd_to_send) /* File descriptor to be passed, could be another socket */
{
 struct msghdr message;
 struct iovec iov[1];
 struct cmsghdr *control_message = NULL;
 char ctrl_buf[CMSG_SPACE(sizeof(int))];
 char data[1];

 memset(&message, 0, sizeof(struct msghdr));
 memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int)));

 /* We are passing at least one byte of data so that recvmsg() will not return 0 */
 data[0] = ' ';
 iov[0].iov_base = data;
 iov[0].iov_len = sizeof(data);

 message.msg_name = NULL;
 message.msg_namelen = 0;
 message.msg_iov = iov;
 message.msg_iovlen = 1;
 message.msg_controllen =  CMSG_SPACE(sizeof(int));
 message.msg_control = ctrl_buf;

 control_message = CMSG_FIRSTHDR(&message);
 control_message->cmsg_level = SOL_SOCKET;
 control_message->cmsg_type = SCM_RIGHTS;
 control_message->cmsg_len = CMSG_LEN(sizeof(int));

 *((int *) CMSG_DATA(control_message)) = fd_to_send;

 return sendmsg(socket, &message, 0);
}

我的意思是将完整的“消息”信息放入共享内存中


0

在之前的回答中,您必须在两个进程之间建立一个套接字对。

pid_t pid;
int streamfd[2];
int n;
int conn_fd;
char buffer; // dummy byte character received from parent

if (socketpair(AF_UNIX, SOCK_STREAM, 0, streamfd) < 0) {
        return -1;
}

接下来,一个进程必须发送描述符,而另一个进程必须接收它。这是接收端(此代码改编自: https://www.amazon.ca/UNIX-Network-Programming-Richard-Stevens/dp/0139498761

int receiveDescriptor(int streamPipe, int* descriptor, void* pBuffer, size_t bytes) {

  struct msghdr msgInstance;
  struct iovec iov[1];
  int n;

  // ancillary (control) data
  // This is where the descriptor will be held

  #ifdef MSGHDR_MSG_CONTROL

  union {
    struct cmsghdr cm;
    char control[CMSG_SPACE(sizeof(int))];
  } control_un;

  struct cmsghdr* cmptr;

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

#else

  int receivedFD;
  msgInstance.msg_accrights = (caddr_t)&receivedFD;
  msgInstance.msg_accrightslen = sizeof(int);

#endif

  msgInstance.msg_name = NULL;
  msgInstance.msg_namelen = 0;

  iov[0].iov_base = pBuffer;
  iov[0].iov_len = bytes;

  msgInstance.msg_iov = iov;
  msgInstance.msg_iovlen = 1;

  n = recvmsg(streamPipe, &msgInstance, 0);

  if ( n <= 0) {
    return n;
  }

  // assume descriptor will not be received

  *descriptor = -1;

  // get the descriptor

#ifdef MSGHDR_MSG_CONTROL

  if ( (cmptr = CMSG_FIRSTHDR(&msgInstance)) != NULL) {

    if (cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {

        if (cmptr->cmsg_level == SOL_SOCKET && cmptr->cmsg_type == SCM_RIGHTS) {

            *descriptor = *((int*)CMSG_DATA(cmptr));

        }
    }
  }

#else

  if (msgInstance.msg_accrightslen == sizeof(int)) {

    *descriptor = receivedFD
  }

#endif

  return n;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接