Linux provides a series of system calls that allow us to pass file descriptors between processes. Instead of simply passing the file descriptor, which is a 32-bit integer, we actually pass the file handle to the target process so that it can read and write to the file. Now suppose process B wants to send a file descriptor to process A. Let’s see how to do that.
1. UNIX domain socket
To pass file descriptors, you need to establish inter-process communication. UNIX domain socket is a way to communicate between processes, it is similar to a normal socket, except that it does not use an ip address, but a socket file. We want to use it to send control messages and thus pass file descriptors. First we will have process A create a socket:
Note the arguments to the
AF_UNIX specifies the protocol family as a UNIX domain socket. Here we use the datagram socket
SOCK_DGRAM , which is similar to UDP and does not require a link to be established, but is sent directly through the address. Of course, you can also use
SOCK_STREAM, which is similar to TCP. We won’t list them here.
Next we bind the address:
Note that the address here is no longer an ip address, but a file path. Here we specify the address as
process_a, and call
bind to bind the address, which creates a socket file called
This way, other processes can send messages to this process through a special address like
process_a. It is the same as sending UDP messages, except that
struct sockaddr_un is used to define the address instead of
struct sockaddr_in or
struct sockaddr_in6. In addition, it is important to note that other processes can pass file descriptors to this process by sending control messages.
Linux provides a pair of system calls:
recvmsg . Unlike our usual
recv, they can be used to send or receive control messages in addition to regular data, which is the key to passing file descriptors; they can also be used to send or receive a discrete piece of data.
Let’s look at the declarations of these two system calls
recvmsg use a structure
struct msghdr to describe the data sent. The structure is defined as follows:
msg_nameDestination address. This is optional, if the protocol is connection-oriented, you don’t need to specify an address; otherwise, you need to specify an address. This is similar to
msg_iovthe data to be sent. This is an array, the length of which is specified by
msg_iovlen, and the elements of the array are a
struct iovecstructure that specifies the starting address (
iov_base) and length (
iov_len) of a contiguous piece of data. In other words, it can send more than one continuous piece of data; or, it can send a discontinuous piece of data.
msg_controlcontrols the message. This is what we are talking about today. We can’t set it directly, we have to use a series of macros to set it.
msg_control points to a sequence consisting of a
struct cmsghdr structure and its additional data. The definition of
struct cmsghdr is as follows:
struct cmsghdr actually defines the header of the data, which should be followed by an
unsigned char array that holds the actual data of the control information. This is often referred to as a
msg_control points to a sequence of such variable-length structures. The memory structure is shown in the figure below:
We need to use the following macros:
CMSG_FIRSTHDR()returns the first element of the sequence pointed to by
CMSG_SPACE()The length of the actual data of the incoming control message, returning the space required for the variable-length structure
CMSG_LEN()The length of the actual data of the control message, and the length of the variable-length structure.
CMSG_DATA()Returns the first address of the actual data that holds the control information.
Note the difference between
CMSG_LEN(): the former contains the length of the padding, which is the actual space occupied; the latter does not contain the length of the padding, and is used to assign the value to
Next, let’s have process B pass a file descriptor to process A. First set the address of process A, which is “process_a”:
We only need to send control messages, not regular data, so we leave the regular data empty:
Next, we allocate space for the control data, since we only pass one file descriptor, so the length is
Then you can set the
struct msghdr structure:
Next we get
struct cmsghdr and set it:
Finally, just send it out:
Now we ask process A to receive the file descriptor passed to it. Here we call
Thus, process A receives the file descriptor from process B, and process B opens the file and can perform read and write operations on it.