我试图在同一台机器上运行的两个不同程序之间实现IPC(在我的情况下是一个CentOS7)。为了获得一种松耦合,我决定使用一个命名管道进行IPC。因此,我正在玩下面的例子并遇到了不同的问题。
创建并写入管道:
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
using namespace std;
main() {
int fd;
char * myfifo = new char [12];
strcpy(myfifo, "./tmp/myfifo1");
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
/* write "Hi" to the FIFO */
fd = open("./tmp/myfifo1", O_WRONLY ); //open(myfifo, O_WRONLY | O_NONBLOCK);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}
printf("File open\n");
write(fd, "entry [1]", sizeof("entry [1]"));
sleep(1);
write(fd, "entry [2]", sizeof("entry [2]"));
sleep(2);
write(fd, "entry [3]", sizeof("entry [3]"));
printf("Content written\n");
close(fd);
printf("Connection closed\n");
/* remove the FIFO */
unlink(myfifo);
return 0;
}
读管道:
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;
main() {
int fd;
fd_set set_a;
char * myfifo = new char [12];
strcpy(myfifo, "./tmp/myfifo1");
char buffer[1024];
fd = open("./tmp/myfifo1", O_RDONLY | O_NONBLOCK);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}
ssize_t bytes;
size_t total_bytes = 0;
printf("\nDropped into read pipe\n");
while(1){
if((bytes = read(fd, buffer, sizeof(buffer))) > 0){
std::string message(&buffer[22]);
total_bytes += (size_t)bytes;
printf("%i", bytes);
printf("Message: %s\n", message.c_str());
memset(&buffer[0], 0, sizeof(buffer));
}else{
if (errno == EWOULDBLOCK) {
printf("\ndone reading (%d bytes)\n", (int)total_bytes);
//break;
}
printf("No message\n");
sleep(2);
}
}
return EXIT_SUCCESS;
}
我觉得命名管道的行为非常不灵活,我用我的测试程序想出来了。首先,如果没有读取进程附加到fifo管道,除了写入管道的最后一个消息之外的所有消息都会丢失(或者一般来说,只有在读取过程附加到管道之后才能读取最后一条消息)。如果你在管道中写入多条消息,那么读取之前的所有消息(例如轮询)将被解释为一条消息(我知道它们可以被\ 0分割)。
命名管道的主要目标是a)系统日志和b)用户身份验证的类型。命名管道的异步完全符合我的需要。但无论如何,我不确定命名管道是否是不同程序之间IPC的最佳解决方案。此外,我不确定上面描述的行为是否正常,或者我是否以错误的方式使用命名管道。我也考虑过套接字,但后来我会遇到很大的阻塞问题。
感谢您的帮助。
答案 0 :(得分:3)
“首先,如果没有读取进程附加到fifo管道,除了写入管道的最后一个消息之外的所有消息都会丢失”。
不,他们没有。使用cat
代替您(糟糕的写作:D)阅读流程,您将获得所有消息。
你是对的。管道是面向字节的,而不是面向消息的。如果你想要消息,还有其他的IPC(例如,SysV消息队列)。
Beej's Guide to Unix IPC是一个优秀的Unix ipc介绍,如果你想要更高级的东西,那么理查德史蒂文斯的 Unix环境中的高级编程非常好。
就用户身份验证而言,请查看Unix套接字。
答案 1 :(得分:2)
看起来你正在尝试使用管道来实现它们不适合的设计。首先,有来阅读流程&#34;附加&#34;对于管道的另一侧。如果您尝试打开管道进行写入且没有读取过程,open
将挂起等待它或返回-1并将errno
设置为ENXIO
(当使用O_NONBLOCK标志时)。所以命名管道是系统中的一种流行点,其中两个进程相遇以交换数据;)
第二 - 不要将写入管道视为发送消息。管道是一个&#34;字节流&#34;。你可以写一次10个字节,阅读器可以进行5次读取,每次2个字节,反之亦然:你可以写5次2个字节,读者可以一次读取它们。所以,如果你打算发送某种&#34;消息&#34;在管道上你应该开发一些最小的传输协议(例如使用&#39; \ 0&#39;字节作为分隔符,如上所述)。
所以,也许您应该查看System V消息队列(请参阅msgget
,msgctl
,msgrcv
,msgsnd
或POSIX消息队列(请参阅{{1} },mq_open
,mq_unlink
,mq_send
等),这使您可以发送和接收原子消息。