當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 使用unix域套接字在進(jìn)程之間傳遞文件描述符
在多進(jìn)程的程序中經(jīng)常需要在不同的進(jìn)程之間傳遞文件描述符,但是不同的進(jìn)程之間文件描述代表的是不同的對(duì)象。那么如何在不同的進(jìn)程中使用相同的文件描述符,而且代表的是相同的對(duì)象呢?
在linux中可以使用unix的域套接字方法來(lái)實(shí)現(xiàn)在不同的進(jìn)程之間傳遞文件描述符, 需要使用socketpair函數(shù)創(chuàng)建一個(gè)套接字管道,該管道是雙向的,每一端都是可讀可寫(xiě)的。
socketpair的 函數(shù)原型:
int socketpair(int domain, int type, int protocol, int sv[2]);
參數(shù):
Domain: 通信類(lèi)型比如AF_UNIX
type:套接字類(lèi)型比如 SOCK_STREAM、 SOCK_DGRAM
protol:只能為0
sv: 包含兩個(gè)元素的數(shù)組名
函數(shù)執(zhí)行完成之后會(huì)得到sv[0]和sv[1]兩個(gè)套接字描述符。在不同的進(jìn)程之間進(jìn)行通信時(shí)可以使用如下的方法:
每個(gè)進(jìn)程關(guān)閉一個(gè)描述符,然后使用一個(gè)描述符通信。那么有了管道后,如何傳遞文件描述符呢?那就得需要使用sendmsg、recvmsg函數(shù)。
sendmsg函數(shù)用來(lái)給一個(gè)特性的套接字描述符發(fā)送消息。
recvmsg 函數(shù)用來(lái)從一個(gè)特定的套接字中讀取消息。
函數(shù)原型如下:
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
這兩個(gè)函數(shù)的使用關(guān)鍵是struct msghder和 struct cmsghdr?兩個(gè)結(jié)構(gòu)體的使用。
首先, stuct msghdr結(jié)構(gòu)體是用來(lái)發(fā)送和接收消息的結(jié)構(gòu)體,成員如下:struct msghdr {
void *msg_name; //套接字的地址
socklen_t msg_namelen;//套接字地址長(zhǎng)度
struct iovec *msg_iov;//消息結(jié)構(gòu)體的地址
size_t msg_iovlen;//msg_iov結(jié)構(gòu)體的個(gè)數(shù)
void *msg_control;//消息控制緩沖區(qū)
size_t msg_controllen;//消息控制緩沖區(qū)的長(zhǎng)度
int msg_flags;//接收消息時(shí)的標(biāo)志位
};
stcut cmsghdr結(jié)構(gòu)體成員如下:
struct cmsghdr
{
cmsg_len // 附屬數(shù)據(jù)的字節(jié)計(jì)數(shù),這包含結(jié)構(gòu)頭的尺寸。這個(gè)值是由CMSG_LEN()宏計(jì)算的。
cmsg_level // 這個(gè)值表明了原始的協(xié)議級(jí)別(例如,SOL_SOCKET)。
cmsg_type // 這個(gè)值表明了控制信息類(lèi)型(例如,SCM_RIGHTS)。
}
示例代碼如下:
1)接收描述符代碼
int my_recv();
int main(int argc, const char *argv[])
{
int fd;
char buf[32] = {0};
if ((fd = my_recv()) < 0)
{
printf("fail to my_recv\n");
return -1;
}
read(fd, buf, sizeof(buf));
puts(buf);
close(fd);
return 0;
}
int my_recv()
{
int sockfd[2];
int status = -1;
pid_t pid;
char itoa_fd[10] = {0};
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
{
perror("fail to socketpair");
return -1;
}
pid = fork();
if (pid < 0)
{
perror("Fail to fork");
return -1;
}
else if (pid == 0)
{
close(sockfd[0]);
sprintf(itoa_fd, "%d", sockfd[1]);
if (execl("./sendmsg", "sendmsg", itoa_fd, NULL) < 0)
{
perror("fail to execl");
exit(-1);
}
}
else
{
close(sockfd[1]);
waitpid(pid, &status, 0);
if (WEXITSTATUS(status) != 0)
{
close(sockfd[0]);
printf("sendmsg fail to exit\n");
return -1;
}
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iv;
char buf[CMSG_SPACE(sizeof(int))] = {0};
char recv_buf[32] = {0};
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
//用來(lái)接收sendmsg發(fā)送的消息
iv.iov_base = recv_buf;
iv.iov_len = sizeof(recv_buf);
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (recvmsg(sockfd[0], &msg, 0) < 0)
{
perror("fail to recvmsg");
return -1;
}
if ((cmsg = CMSG_FIRSTHDR(&msg)) != NULL &&cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
{
close(sockfd[0]);
return *(int *)CMSG_DATA(cmsg);
}
close(sockfd[0]);
return -1;
}
}
2)發(fā)送描述符代碼
int my_send(int sockfd, int file);
int main(int argc, const char *argv[])
{
int fd;
if ((fd = open("file", O_RDONLY)) < 0)
{
perror("fail to open the file");
return -1;
}
if (my_send(atoi(argv[1]), fd) < 0)
{
puts("fail to my_send");
close(fd);
return -1;
}
return 0;
}
int my_send(int sockfd, int file)
{
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iv;
char buf[CMSG_SPACE(sizeof(int))] = {0};
char send_buf[32] = "helloworld";
bzero(&msg, sizeof(msg));
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
//必須要添加消息這一部分,否則sendmsg無(wú)法發(fā)送
iv.iov_base = send_buf;
iv.iov_len = sizeof(send_buf);
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int*)CMSG_DATA(cmsg) = file;
return sendmsg(sockfd, &msg, 0);
}