How can I pass a socket from parent to child processes

10,386

Solution 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.

Solution 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;
    }
}
Share:
10,386
user1995143
Author by

user1995143

Updated on June 27, 2022

Comments

  • user1995143
    user1995143 almost 2 years

    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?

  • user1995143
    user1995143 over 11 years
    i was talking about parent/child process, so the question is: How to child access to file descriptor made by the parent after a fork?
  • Robert S. Barnes
    Robert S. Barnes over 11 years
    +1 For a nice answer, but It sounds to me like he'd be better off using threads.
  • Celada
    Celada over 11 years
    @user1995143 That is exactly the question I answered. It doesn't matter what the parent/child relationship of the 2 processes is. As long as you have a UNIX domain socket as a communication channel, you can transmit file descriptors using this technique. Or you can follow Robert S. Barnes's suggestion and use multiple threads instead of multiple processes. Then you don't have to worry about it because file descriptors are shared between all threads of a single process.