我正在编写一个用于进程间通信的程序,但我遇到了一个问题,即使管道中有足够的空间,写入操作也会阻塞进程.
我正在使用一个管道缓冲区大小为8192的远程主机,这要归功于以下几点:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd[2];
pipe(fd);
printf("Pipe size: %d\n", fcntl(fd[1], F_GETPIPE_SZ));
close(fd[1]);
close(fd[0]);
return 0;
}
在下面的示例中,我创建了16个进程,每个进程都有自己的管道. 然后,每个进程将512B写入其子进程的管道. 子元素们读到了这些信息. 根被标记为0,子进程被连续编号为2k+1, 2k+2,其中k是进程号. 最后,每个进程向所有管道发送一条消息.
因此,16*512B=8192将被写入根的管道,并被写入每隔一个管道(16+1)*512B=8192+512,但是将读取一条额外的消息,因此整个消息应该可以放入管道中.
MRE(此示例没有做任何有用的事情;它只说明了我的问题):
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#define NO_OF_PROCESSES 16
#define NO_OF_MESSAGES 1
#define ROOT 0
#define ERROR_CHECK(result) \
do { \
if ((result) == -1) { \
fprintf(stderr, "Error at line %d\n", __LINE__); \
exit(1); \
} \
} while (0)
#define NOT_PARTIAL(result) \
do { \
if ((result) != 512) { \
fprintf(stderr, "Error at line %d\n", __LINE__); \
exit(1); \
} \
} while (0)
void close_pipes(int fd[NO_OF_PROCESSES][2]) {
for (int i = 0; i < NO_OF_PROCESSES; i++) {
ERROR_CHECK(close(fd[i][0]));
ERROR_CHECK(close(fd[i][1]));
}
}
void child_code(int fd[NO_OF_PROCESSES][2], int child_id) {
void* message = malloc(512);
if (message == NULL)
exit(EXIT_FAILURE);
memset(message, 0, 512);
int l = 2 * child_id + 1;
int r = 2 * child_id + 2;
// Every process sends two messages to each of its children.
if (child_id == ROOT || l < NO_OF_PROCESSES) { // Root or any other parent.
if (child_id != ROOT)
for (int i = 0; i < NO_OF_MESSAGES; i++)
NOT_PARTIAL(read(fd[child_id][0], message, 512));
if (l < NO_OF_PROCESSES)
for (int i = 0; i < NO_OF_MESSAGES; i++)
NOT_PARTIAL(write(fd[l][1], message, 512));
if (r < NO_OF_PROCESSES)
for (int i = 0; i < NO_OF_MESSAGES; i++)
NOT_PARTIAL(write(fd[r][1], message, 512));
}
else { // Leaf.
for (int i = 0; i < NO_OF_MESSAGES; i++)
NOT_PARTIAL(read(fd[child_id][0], message, 512));
}
printf("Ok, process %d\n", child_id);
// Process sends one message to every other process.
for (int i = 0; i < NO_OF_PROCESSES; i++) {
int pipe_size = 0;
ioctl(fd[i][1], FIONREAD, &pipe_size);
printf("Check_1, process %d, there are %d bytes in the pipe, iteration %d\n", child_id, pipe_size, i);
NOT_PARTIAL(write(fd[i][1], message, 512));
printf("Check_2, process %d\n", child_id);
fflush(stdout);
}
free(message);
printf("Finished, process %d\n", child_id);
}
int main() {
// Each child has its own pipe.
int fd[NO_OF_PROCESSES][2];
for (int i = 0; i < NO_OF_PROCESSES; i++) {
ERROR_CHECK(pipe(fd[i]));
}
// Creating children processes.
for (int i = 0; i < NO_OF_PROCESSES; i++) {
int fork_result = fork();
ERROR_CHECK(fork_result);
if (fork_result== 0) { // Child process.
child_code(fd, i);
close_pipes(fd);
return 0;
}
}
close_pipes(fd);
// Waiting for all children to finish.
for (int i = 0; i < NO_OF_PROCESSES; i++) {
ERROR_CHECK(wait(NULL));
}
return 0;
}
目前,结果是程序不会因为某些进程挂起而终止.
输出的最后几行:
Ok, process 12
Check_1, process 2, there are 7168 bytes in the pipe, iteration 15
Check_2, process 2
Check_1, process 12, there are 7680 bytes in the pipe, iteration 0
Finished, process 2
Check_2, process 12
Check_1, process 12, there are 7680 bytes in the pipe, iteration 1
正如您所看到的,Check_2, process 12
丢失了,进程在写入时挂起,即使在完整的输出中Ok
出现了16次,这从理论上意味着应该读取"树中"的所有消息.
该程序适用于15个或更少的进程,因为这样最多就有8192B进入管道.类似地,代码在管道容量较大的系统上运行.
我在哪里犯了错?为什么这一过程会暂停? 如果我的代码适合您,那么您可能在管道中有不同的缓冲区大小.
最近(相当笨拙地)我问了一个类似的问题.我正在添加一个新的帖子,而不是编辑旧的帖子,因为整个内容都会改变,现有的答案将不再有意义. 我希望这个帖子更好.
非常感谢.