Share a file descriptor between parent and child after fork and exec

14,145

Solution 1

File descriptors are always passed between a parent and child process

When you fork a process, the file descriptors that are open in the parent(at the time of fork()) are implicitly passed on to the child. There is no need to send them explicitly.

For example:

The pseudo-code looks as follows:

In process A:

fd = open_socket_or_file;
char str_fd[3];
str_fd[0]=fd;
str_fd[1]=fd;
str_fd[2]=0;
if(fork()==0)
{
     execl("./B",str_fd,NULL);
}

In the child process B you can do:

int fd = argv[1][0];
/* now do whatever you want with the fd...*/

EDIT:

In case the processes are different, you need to pass the file descriptor explicitly. This is generally done using UNIX-Domain Sockets(If you are using Linux Flavors). For code related to this, you can see this answer

Solution 2

Yes that is true that file descriptors remain open even after fork or exec or fork and exec.You only need to know the value of fd in the new process image that was replaced using exec else put that fd on the one which is already known to that process(ex:0,1,2). So you can do this in two ways:

  • Placing the fd on either one of standard file descriptors using dup2(note:as far as i know you will be unable to reset that standard file descriptor for which it was actually known for)

  • Passing the fd as string argument for one of 6 exec functions does the job

Therefore I suggest you to use second method in case if you want standard fds remain

These are the two methods of implementation:

P1.c(using argument passing)

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
    printf("Hello this is process 1\n");
    int fd=open("./foo",O_RDONLY);
    char buf[255];
    //int n=read(fd,buf,255);
    int h=fork();
    if(h==0)
    {
        char *fname="./p2";
        char *arg[3];
        char targ[10];
        sprintf(targ,"%d",fd);
        arg[0]=fname;
        arg[1]=targ;
        arg[2]=NULL;
        execvp(fname,arg);
    }
    else
    {
        printf("This is from p1 process\n");
        //write(1,buf,strlen(buf));
                    //do some process with p1
        printf("This is end of p1 process\n");
    }
}

P1.c(using dup2 with 0)

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
    printf("Hello this is process 1\n");
    int fd=open("./foo",O_RDONLY);
    int h=fork();
    if(h==0)
    {
        dup2(fd,0);//note we will be loosing standard input in p2
        execvp(fname,NULL);
    }
    else
    {
        printf("This is from p1 process\n");
        //write(1,buf,strlen(buf));
                    //do some process with p1
        printf("This is end of p1 process\n");
    }
}

P2.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc,char *argv[])
{
    int fd=atoi(argv[1]);      //here fd=0 in case dup2 in process ps1.c
    char buf[1024];
    int n=read(fd,buf,1024);
    buf[n]='\0';
    printf("This is from p2\n");
    write(1,buf,strlen(buf));
}
Share:
14,145
tversteeg
Author by

tversteeg

Organize code, write parties

Updated on June 05, 2022

Comments

  • tversteeg
    tversteeg almost 2 years

    I have two processes on Linux, A & B. I want to share the file descriptor from process A with process B, now I just serialize it to a char* and pass it to the execl parameters, but that doesn't work.

    A.c looks like this:

    union descriptor{
        char c[sizeof(int)];
        int i;
    } fd;
    pid_t pid;
    
    fd.i = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    // Perform other socket functions
    
    pid = fork();
    if(pid == 0){
        // Read data from socket
        if(execl("./B", fd.c, NULL) < 0){
            exit(EXIT_FAILURE);
        }else(
            exit(EXIT_SUCCESS);
        }
    }else if(pid < 0){
        exit(EXIT_FAILURE);
    }else{
        waitpid(pid, NULL, 0);
    }
    

    B.c looks like this:

    union descriptor{
        char c[sizeof(int)];
        int i;
    } fd;
    
    memcpy(&fd.c[0], argv[0], sizeof(int));
    
    write(fd.i, "TEST", 4);
    close(fd.i);
    

    But this doesn't work, and I don't really understand why not. How can I make this work? And if it works, is it the best solution to share a file descriptor between a parent and a child after a fork and a exec?

    Update

    The problem is unrelated to the question I asked, it is caused by a wrong way of passing an integer as pointed out by @OliCharlesworth. Please close this question.

  • tversteeg
    tversteeg about 10 years
    But the B process is a separate process. How do I access the file descriptor from it?
  • sergico
    sergico about 10 years
    As already mentioned in the above comment from @Oli, you should not use string releated function to manage a binary data :)
  • nitish712
    nitish712 about 10 years
    @sergico Updated the answer accordingly.. :)
  • tversteeg
    tversteeg about 10 years
    The question is if it is possible to share a file descriptor directly between two different processes, where process A is the parent of process B.
  • nitish712
    nitish712 about 10 years
    @ThomasVersteeg Yeah thats what I answered!
  • Greg A. Woods
    Greg A. Woods about 8 years
    I would strongly recommend against the variant using dup2() -- it's unnecessary complexity and either implicitly closes a widely known and potentially useful open descriptor, or relies on hard-coded assumptions about some magic number and whether or not the target descriptor might already be open and in use for some other purpose.
  • IronMan007
    IronMan007 about 8 years
    Yes!, agreed, even I suggest to use passing as argument instead of using dup2() :)
  • CMCDragonkai
    CMCDragonkai over 7 years
    Is it possible to pass a file descriptor from the child process to the parent process without a unix domain socket?
  • David Ranieri
    David Ranieri over 2 years
    @nitish712 I think you want execl("./B","B",str_fd,NULL);. The first argument must be the name of the program.