fork和exec之后在父级和子级之间共享文件描述符

时间:2014-02-02 15:26:43

标签: c linux exec fork file-descriptor

我在Linux上有两个进程,A& B. 我想与进程B共享进程A中的文件描述符,现在我只是将其序列化为char*并将其传递给execl参数,但这不起作用。

A.c看起来像这样:

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看起来像这样:

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);

但这不起作用,我真的不明白为什么不行。我怎样才能做到这一点?如果它有效,它是在forkexec之后在父母和孩子之间共享文件描述符的最佳解决方案吗?

更新

问题与我提出的问题无关,它是由@OliCharlesworth指出的传递整数的错误方式引起的。请关闭这个问题。

2 个答案:

答案 0 :(得分:6)

File descriptors are always passed between a parent and child process

当您fork进程时,父进程中打开的文件描述符(在fork()时)被隐式传递给子进程。没有必要明确发送它们。

例如:

伪代码如下所示:

正在处理 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);
}

在子流程 B 中,您可以执行以下操作:

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

修改

如果进程不同,则需要传递文件描述符显式。这通常使用UNIX-Domain套接字完成(如果您使用的是Linux Flavors)。对于与此相关的代码,您可以看到this answer

答案 1 :(得分:3)

是的,即使在fork或exec或fork和exec之后文件描述符仍保持打开。你只需要知道在使用exec替换的新进程映像中fd的值将else放入fd就是那个已知该过程(例如:0,1,2)。所以你可以用两种方式做到这一点:

  • 使用dup2将fd放置在任一标准文件描述符上(注意:据我所知,您将无法重置其实际已知的标准文件描述符)

  • 将fd作为6个exec函数之一的字符串参数传递完成作业

因此,如果您希望保留标准fds,我建议您使用第二种方法

这是两种实施方法:

P1.c(使用参数传递)

#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(使用dup2和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));
}