How to transfer a socket from parent to child processes

I am stuck in a problem in a Linux program.

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

The problem is that I am writing a multiprocessor server application with a main process that accepts new connections and puts descriptors in shared memory.

When a child process tries to read from one of these descriptors from shared memory, I got an EBADF error on select() !

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

+8
c webserver file-descriptor fork sockets
source share
2 answers

When you call fork, the child process inherits copies of all open file descriptors. A typical way to do this is through the parent process to open a listening socket, accept a call that blocks until a connection is received, and then call fork after receiving the connection. The parent then closes its copy of the file descriptor, while the new child process can continue to use the file descriptor and perform any processing that is necessary. As soon as the child is completed, he also closes the socket. It is important to remember two things: 1. A file descriptor / socket is a resource in the operating system, and after fork, the parent and child have a handle to this resource, which looks like a smart pointer with reference counting. I explain this in more detail here . Secondly, only the file descriptors that open before the fork call are shared, because after forking the parent and child are completely separate processes, although they can share some resources, such as the file descriptors that existed before fork appeared. If you are using a model in which you want the parent process to execute workflows, you might be better off looking at the use of threads and the thread pool .

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

+9
source share

You cannot transfer 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 in a valid file descriptor from the point of view of another process.

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

First create your socketpair() communication channel before fork() . Now, in the parent, close one end of the socket pair, and in the child case, close the other end. Now you can sendmsg() from the parent at one end of this socket and get recvmsg() to get the child using the other end.

Sending a message using 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 happens 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; } } 
+11
source share

All Articles