How can I pass a socket from parent to child processes

不打扰是莪最后的温柔 提交于 2021-02-07 05:33:58

问题


I'm stuck on a problem in a C program on Linux.

I know that when a processes is forked the child process inherits some things from the parent, including open file descriptors.

The problem is that I'm writing a multi-process server application with a master process that accepts new connections and puts the descriptors into shared memory.

When the child process attempts to read from one of these descriptors from the shared memory, on select() i got an EBADF error!

How can the child process read and use a socket (or any file descriptor in general) created by a parent process after it has been forked?


回答1:


When you call fork, the child process inherits copies of all open file descriptors. The typical way of doing this is for a parent process to open a listening socket, call accept which blocks until a connection arrives and then calls fork after receiving the connection. The parent then closes it's copy of the file descriptor, while the new child process can keep using the file descriptor and do any processing which is needed. Once the child is done it also closes the socket. It's important to remember two things: 1. The file descriptor / socket is a resource in the operating system and after the fork the parent and child each have a handle to that resource, which is kind of like a reference counted smart pointer. I explain this in more detail here. The second thing is that only file descriptors which are opened before calling fork are shared, because after forking parent and child are completely separate processes, even though they may share some resources like file descriptors which existed prior to the fork. If you're using a model where you want to have a parent handing out work to worker processes, it may be better for you to consider using threads, and a thread pool.

By the way, you can download allot of nice examples of servers and clients from Unix Network Programming website.




回答2:


You cannot transmit a socket (or any other file descriptor) from one process to another through shared memory. A file descriptor is just a small integer. Placing this integer in shared memory and accessing it from another process does not automatically make the same integer into a valid file descriptor from the point of view of the other process.

The correct way to send a file descriptor from one process to another is to send it as SCM_RIGHTS ancillary data with sendmsg() through an existing socket communication channel between the two processes.

First, create your communication channel with socketpair() before you fork(). Now, in the parent, close one end of the socket pair, and, in the child, close the other end. You can now sendmsg() from the parent on one end of this socket and receive with recvmsg() in the child using the other end.

Sending a message with SCM_RIGHTS looks something like this:

struct msghdr m;
struct cmsghdr *cm;
struct iovec iov;
char buf[CMSG_SPACE(sizeof(int))];
char dummy[2];    

memset(&m, 0, sizeof(m));
m.msg_controllen = CMSG_SPACE(sizeof(int));
m.msg_control = &buf;
memset(m.msg_control, 0, m.msg_controllen);
cm = CMSG_FIRSTHDR(&m);
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
cm->cmsg_len = CMSG_LEN(sizeof(int));
*((int *)CMSG_DATA(cm)) = your_file_descriptor_to_send;
m.msg_iov = &iov;
m.msg_iovlen = 1;
iov.iov_base = dummy;
iov.iov_len = 1;
dummy[0] = 0;   /* doesn't matter what data we send */
sendmsg(fd, &m, 0);

Receiving a message with SCM_RIGHTS in it goes something like this:

struct msghdr m;
struct cmsghdr *cm;
struct iovec iov;
struct dummy[100];
char buf[CMSG_SPACE(sizeof(int))];
ssize_t readlen;
int *fdlist;

iov.iov_base = dummy;
iov.iov_len = sizeof(dummy);
memset(&m, 0, sizeof(m));
m.msg_iov = &iov;
m.msg_iovlen = 1;
m.msg_controllen = CMSG_SPACE(sizeof(int));
m.msg_control = buf;
readlen = recvmsg(fd, &m, 0);
/* Do your error handling here in case recvmsg fails */
received_file_descriptor = -1; /* Default: none was received */
for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) {
    if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS) {
        nfds = (cm->cmsg_len - CMSG_LEN(0)) / sizeof(int);
        fdlist = (int *)CMSG_DATA(cm);
        received_file_descriptor = *fdlist;
        break;
    }
}


来源:https://stackoverflow.com/questions/14427898/how-can-i-pass-a-socket-from-parent-to-child-processes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!