【发布时间】:2020-07-21 08:37:04
【问题描述】:
我有一个进程 P,它必须跟踪从 P1 到 Pn 的进程列表。
程序使用 P1 进行初始化,列表的头部已经创建。在特定事件上(不在问题范围内),P 必须通过管道写入列表中的当前最后一个进程 Pi 和这个进程,在收到消息,不得不分叉。新创建的 Pi+1 必须作为新的最后一个进程添加到由 P 管理的链表中。
我尝试实现的方式如下:
- P 初始化一个空链表List,创建一个管道childs_to_p 来接收来自所有后代的消息,创建一个管道fd em>,分叉 P1 并将其附加到 List。
- P1 开始在 fd 上监听。
- 如果P要添加另一个进程,它会创建一个新管道fd2,并通过fd与P1(第一次迭代的 List 中的最后一个进程)fd2 的读取端(P2 必须监听的读取端来自)。
P1 从 fd 读取,分叉并告诉新创建的 P2 fd2的读取端>。然后继续听fd上的指令。
-
P2 通过管道 childs_to_p 将其 pid 发送到 P。
- P 将 P2'pid 添加到 List。
- 重复 n 次。
请记住,我是 C 语言的新手,但这是我目前实现的。
这是P的初始化:
void setupProcessManager(int we, int re, int msg_size) {
write_to_m = we; // Setup of pipe to external
read_from_m = re; // process that gives instructions
message_size = msg_size; // to P.
initList(&l); // Linked list initialization
open_childs_to_pm_pipe(); // Open pipe to read from processes in list
addp1(); // Initialize P1
pthread_t t;
pthread_create(&t, NULL, PMListen, NULL); // P starts listening from external process
pthread_join(t,NULL); // Wait for thread termination
close(write_to_m); // Close pipe to external process
close(read_from_m); // Close pipe from external process
}
初始化P1的函数:
void addp1() {
int to_p1[2];
pipe(to_p1); // Create new pipe to P1
pid_t pid = fork(); // Fork P1
if(pid == 0) {
PListen(to_p1[0]); // P1 starts listening
exit(0);
} else if(pid>0) {
insertEnd(&l, pid, to_p1[1], to_p1[0]); // P appends P1 to list
}
}
P 执行的功能(仅考虑 switch 中的 'A' 情况):
void *PMListen() {
int message;
int listen = 1;
while(listen) {
read(read_from_m,&message,message_size);
switch(message) {
// OTHER CASES
case 'A':
int new_pipe[2]; // P creates a new pipe
pipe(new_pipe);
write(getLastProcessWritingEnd(&l), &new_pipe[0], message_size); // P writes reading end of new pipe to P1
pid_t pid;
read(childs_to_pm[0], &pid,sizeof(pid_t)); // P reads P2 pid from childs_to_p
insertEnd(&l, pid, new_pipe[1], new_pipe[0]); // P appends P2 to list
break;
}
}
}
最后是列表中的进程执行的任务:
void PListen(int read_from_here) {
int message;
int listen = 1;
while(read(read_from_here,&message,message_size) != 0) {
switch(message) {
default: // Pi reads integer (reading end of pipe P-Pi+1)
pid_t pid = fork(); // Fork Pi+1
if(pid == 0) {
pid_t mypid = getpid();
write(childs_to_pm[1], &mypid, sizeof(int)); // Pi+1 writes its pid to P through childs_to_p
PListen(message); // Pi+1 starts listening on its pipe from P
exit(0);
}
break;
}
}
}
一切都按预期进行。 P1 被创建并添加到链表中。当 P 尝试添加 P2 时,它会这样做,但函数 PListen 会一直循环,永远分叉。
我知道代码写得不好,但正如我提到的,我不熟悉 C。我需要算法方面的帮助,而不是代码的美感。
【问题讨论】:
-
AFAIK,您不能通过管道发送文件描述符。可以pass file descriptors over sockets,但不能通过管道。所以,我认为这个设计存在一些问题。共享管道的进程必须有一个在创建进程之前创建管道的共同祖先(或者其中一个必须是创建管道的祖先)。
-
我认为您将需要创建一个 MCVE (Minimal, Complete, Verifiable Example)(或 MRE 或 SO 现在使用的任何名称)或一个 SSCCE (Short, Self-Contained, Correct Example)。很难看出一切是如何工作的,特别是因为有很多全局变量我们看不到定义。
-
PMListen()应该是一个线程函数,但它的签名不正确。这可能不是致命的,但你不应该冒险。将线程与分叉混合通常不是一个好主意。无论如何,你需要谨慎。 -
如果您只能使用未命名的管道,那么祝您好运,正如临时评论所暗示的那样。那我看看你是怎么解决问题的。 Linux 上可能有一些我不熟悉的可能性,但我认为你前面的路很坎坷。
-
@JonathanLeffler 事实上,对于这个程序,我应该只使用未命名的管道,所以这就是我决定进行这个实现和设计的原因。在我的情况下,P 确实是创建管道的祖先