11.1.6 传递文件描述符的例子

11.1.6  传递文件描述符的例子

本节中使用一个实例来介绍进程间传递文件描述符的例子。分为两个进程,进程A中打开一个文件描述符,通过消息传送的方式将文件描述符传递给进程B

1.进程A的代码

进程A根据用户输入的文件名打开一个文件,将文件描述符打包到消息结构中,然后发送给进程B

 

01      #include <sys/types.h>

02      #include <sys/socket.h>

03      #include <Linux/un.h>

04      #include <string.h>

05      #include <signal.h>

06      #include <stdio.h>

07      #include <errno.h>

08      #include <unistd.h>

09     

10      ssize_t send_fd(int fd, void*data, size_t bytes, int sendfd)

11      {

12          struct msghdr msghdr_send;              /*发送消息*/

13          struct iovec iov[1];                    /*向量*/

14          size_t n;                               /*大小*/

15          int newfd;                              /*文件描述符*/

16          /*方便操作msg的结构*/

17          union{

18              struct cmsghdr cm;                  /*control msg结构*/

19              char control[CMSG_SPACE(sizeof(int))];
                                                    /
*字符指针,方便控制*/

20          }control_un;

21          struct cmsghdr*pcmsghdr=NULL;           /*控制头部的指针*/

22          msghdr_send.msg_control = control_un.control;   /*控制消息*/

23          msghdr_send.msg_controllen = sizeof(control_un.control);
                                                    /
*长度*/

24         

25          pcmsghdr = CMSG_FIRSTHDR(&msghdr_send); /*取得第一个消息头*/

26          pcmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); /*获得长度*/

27          pcmsghdr->cmsg_level = SOL_SOCKET;          /*用于控制消息*/

28          pcmsghdr->cmsg_type = SCM_RIGHTS;

29          *((int*)CMSG_DATA(pcmsghdr))= sendfd;       /*socket*/

30         

31         

32          msghdr_send.msg_name = NULL;                /*名称*/

33          msghdr_send.msg_namelen = 0;                /*名称长度*/

34         

35          iov[0].iov_base = data;                     /*向量指针*/

36          iov[0].iov_len = bytes;                     /*数据长度*/

37          msghdr_send.msg_iov = iov;                  /*填充消息*/

38          msghdr_send.msg_iovlen = 1;

39         

40          return (sendmsg(fd, &msghdr_send, 0));      /*发送消息*/

41      }

42     

43     

44      int main(int argc, char*argv[])

45      {

46          int fd;

47          ssize_t n;

48         

49          if(argc != 4){

50              printf("socketpair error/n");

51          if((fd = open(argv[2],atoi(argv[3])))<0) /*打开输入的文件名称*/

52              return(0);

53             

54          if((n =send_fd(atoi(argv[1]),"",1,fd))<0)   /*发送文件描述符*/

55              return(0);

56                     }

 

分为如下的步骤:

q      1041行为函数send_fd(),它向文件描述符fd发送消息,将sendfd打包到消息体中。

q      12行建立一个消息,之后填充此消息的成员数据,并发送给fd

q      13行为向量,消息的数据在此项两种保存。

q      1720行建立一个联合结构,便于进行消息的处理。

q      22行填充消息的控制部分,第23行为控制部分的长度。

q      25行取得消息的第一个头部。

q      26行为长度,由于发送的是一个文件描述符,所以长度为一个int类型的长度。

q      27行设置消息的levelSOL_SOCKET,第28行填充消息的类型为SCM_RIGHTS

q      32行和第33行用于将消息的名称置空。

q      35行和第36行将传入的数据和长度传递给向量成员。

q      37行将向量填充给消息,第38行设置向量的个数。

q      40行将消息发送给fd

q      4456行为main()函数,用于将打开的文件描述符传递给输入的socket

q      51行打开传入路径的文件。

q      54行将打开的文件传递给输入的某个套接字文件描述符。

2.进程B的代码

进程B获得进程A中发送过来的消息,并从中取得文件描述符。根据获得的文件描述符,直接从文件中读取数据,并将数据在标准输出打印出来。

 

001     #include <sys/types.h>

002     #include <sys/socket.h>

003     #include <Linux/un.h>

004     #include <string.h>

005     #include <signal.h>

006     #include <stdio.h>

007     #include <errno.h>

008     #include <unistd.h>

009    

010     /*

011     *   fd中接收消息,并将文件描述符放在指针recvfd

012     */

013     ssize_t recv_fd(int fd, void*data, size_t bytes, int*recvfd)

014     {

015         struct msghdr msghdr_recv;                  /*接收消息接收*/

016         struct iovec iov[1];                        /*接收数据的向量*/

017         size_t n;

018         int newfd;

019        

020         union{

021             struct cmsghdr cm;

022             char control[CMSG_SPACE(sizeof(int))]; 

023         }control_un;

024         struct cmsghdr*pcmsghdr;                        /*消息头部*/

025         msghdr_recv.msg_control = control_un.control;   /*控制消息*/

026         msghdr_recv.msg_controllen = sizeof(control_un.control);   
                                                        /
*控制消息的长度*/

027        

028         msghdr_recv.msg_name = NULL;    /*消息的名称为空*/

029         msghdr_recv.msg_namelen = 0;    /*消息的长度为空*/

030        

031         iov[0].iov_base = data;         /*向量的数据为传入的数据*/

032         iov[0].iov_len = bytes;         /*向量的长度为传入数据的长度*/

033         msghdr_recv.msg_iov = iov;      /*消息向量指针*/

034         msghdr_recv.msg_iovlen = 1;     /*消息向量的个数为1*/

035         if((n = recvmsg(fd, &msghdr_recv, 0))<=0)   /*接收消息*/

036             return n;

037            

038         if((pcmsghdr = CMSG_FIRSTHDR(&msghdr_recv))!= NULL &&
                                            /
*获得消息的头部*/

039             pcmsghdr->cmsg_len == CMSG_LEN(sizeof(int))){  
                                            /
*获得消息的长度为int*/

040             if(pcmsghdr->cmsg_level != SOL_SOCKET)
                                            /
*消息的level应该为SOL_SOCKET*/

041                 printf("control level != SOL_SOCKET/n");

042            

043             if(pcmsghdr->cmsg_type != SCM_RIGHTS)   /*消息的类型判断*/

044                 printf("control type != SCM_RIGHTS/n");

045                

046                 *recvfd =*((int*)CMSG_DATA(pcmsghdr));
                                            /
*获得打开文件的描述符*/

047         }else

048             *recvfd = -1;

049            

050         return n;                       /*返回接收消息的长度*/

051     }

052    

053     int my_open(const char*pathname, int mode)

054     {

055         int fd, sockfd[2],status;

056         pid_t childpid;

057         char c, argsockfd[10],argmode[10];

058        

059         socketpair(AF_LOCAL,SOCK_STREAM,0,sockfd);  /*建立socket*/

060         if((childpid = fork())==0){                 /*子进程*/

061             close(sockfd[0]);                       /*关闭sockfd[0]*/

062             snprintf(argsockfd, sizeof(argsockfd),"%d",sockfd[1]);                                                          /*socket描述符*/

063             snprintf(argmode, sizeof(argmode),"%d",mode);
                                                        /
*打开文件的方式*/

064             execl("./openfile","openfile",argsockfd, pathname,
                argmode,(char
*)NULL)                    ;/*执行进程A*/

065             printf("execl error/n");

066         }  

067         /*父进程*/

068         close(sockfd[1]);

069         /*等待子进程结束*/

070         waitpid(childpid, &status,0);

071        

072         if(WIFEXITED(status)==0){               /*判断子进程是否结束*/

073             printf("child did not terminate/n") ;

074         if((status = WEXITSTATUS(status))==0){  /*子进程结束*/

075             recv_fd(sockfd[0],&c,1,&fd);    /*接收进程A打开的文件描述符*/

076         }   else{

077             errno = status;

078             fd = -1;   

079         }  

080        

081         close(sockfd[0]);                   /*关闭sockfd[0]*/

082         return fd;                          /*返回进程A打开文件的描述符*/

083    

084         }

085     }

086    

087     #define BUFFSIZE 256                    /*接收的缓冲区大小*/

088     int main(int argc, char*argv[])

089     {

090         int fd, n;

091         char buff[BUFFSIZE];                /*接收缓冲区*/

092        

093         if(argc !=2)

094             printf("error argc/n");

095            

096         if((fd = my_open(argv[1], O_RDONLY))<0)
                                                /
*获得进程A打开的文件描述符*/

097             printf("can't open %s/n",argv[1]);

098            

099         while((n = read(fd, buff, BUFFSIZE))>0) /*读取数据*/

100         write(1,buff,n);                            /*写入标准输出*/

101        

102         return(0); 

103     }

 

分为如下的步骤:

q      013051行为函数recv_fd(),它从fd接收消息,并返回获得消息中的信息:打开文件的描述符。

q      015行建立一个消息,之后填充此消息的成员数据,并发送给fd

q      016行为向量,消息的数据在此项中保存。

q      020023行建立一个联合结构,便于进行消息的处理。

q      025行填充消息的控制部分,第026行为控制部分的长度。

q      028行和第029行用于将消息的名称置空。

q      031行和第032行将传入的数据和长度传递给向量成员。

q      033行将向量填充给消息,第034行设置向量的个数。

q      035行接收消息。

q      038行取得消息的第一个头部。

q      039行判断消息长度是否为int长度。

q      040行判断消息level是否为SOL_SOCKET

q      043行判断消息的类型是否为SCM_RIGHTS

q      046行获得传入的文件描述符。

q      053085行为my_open()函数,按照传入的路径和模式打开文件。

q      059行调用socketpair()函数获得socket对。

q      060行为fork()进程

q      061066行在子进程中调用外部进程打开文件。

q      069084行为父进程处理过程。等待子进程处理函数的结束,并接收传过来       的值。

q      087103行为主函数。它调用my_open()获得进程A传入的文件描述符,从文件中读取数据并显示到标准输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值