這是一個有效的實現。它需要一些注意力來建立連接,如評論中所述,而不是在這個答案中重複。寫入順序也必須與打開序列匹配:
- P0將向前寫入,向前讀取,向後讀取,向後寫入。
- PN將向後讀取,向後寫入,向前寫入,向前讀取。
請注意,子代碼基本上是同步的 - 沒有太多可能發生的重新排序。調用nanosleep()
只是確保啓動消息按順序排列。如果您放棄它,只有報告輸出可能會變得混亂。
我用我的標準錯誤報告包"stderr.h"
and stderr.c
來處理來自代碼的大部分消息。 (目前,如果您使用stderr.c
,則還需要來自同一目錄的kludge.h
和kludge.c
。)該軟件包具有便於使用的功能(使用err_setlogopts()
包含PID和微秒時序,但省略程序名稱,和err_settimeformat()
只打印時間而不打印信息的日期部分)。
我還使用了許多函數 - 我不夠聰明,可以在單個函數中編寫代碼。通常,對於簡單的親子關係,我將有一對函數be_childish()
和be_parental()
來封裝子進程和父進程完成的工作。在這段代碼中,我並不需要單獨的父母功能。
下面的代碼中有大約200行 - 包括空白行和註釋。
#include "stderr.h"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#define MIN_PROC 2
#define MAX_PROC 20
enum { MAX_TUBENAMELEN = 20 };
enum { MAX_MSGLEN = 256 };
enum Mode { M_WF, M_RF, M_WB, M_RB };
/*
** You have to split the connections into 'forward' and 'backward'
** around the loop. P0 (zeroth child) needs to connect forward first,
** then connect backward; all other children need to connect backward
** first, then connect forward. Although the order is arbitrary, when
** a child connects backward, it should open the read FIFO before the
** write FIFO; when it connects forward, it should open the write FIFO
** before the read FIFO.
**
** Child process Pn (for n = 0; n < N; n++) connects forward to tube
** T[(2n)%(2N)] for writing, T[(2n+1)%(2N)] for reading; and connects
** backward to T[(2n-2+2N)%(2N)] for reading, and to T[(2n-1+2N)%(2N)]
** for writing. The +2N terms ensure that the LHS of % is not
** negative. This sequencing should ensure no blockage
**
** When it comes to reading and writing, the rules will be similar.
** P0 will write forward, read forward, read backward, write backward.
** PN will read backward, write backward, write forward, read forward.
*/
static inline int tube_index(int num, int max)
{
int idx = (num + 2 * max) % (2 * max);
return idx;
}
static int open_fifo(enum Mode mode, int max, int num, char tubes[][MAX_TUBENAMELEN])
{
int fd;
int idx = 0;
switch (mode)
{
case M_WF: idx = tube_index(2 * num + 0, max); break;
case M_RF: idx = tube_index(2 * num + 1, max); break;
case M_RB: idx = tube_index(2 * num - 2, max); break;
case M_WB: idx = tube_index(2 * num - 1, max); break;
default: assert(0);
}
const char *fifoname = tubes[idx];
int o_mode = O_RDONLY;
if (mode == M_WF || mode == M_WB)
o_mode = O_WRONLY;
err_remark("Opening FIFO %s with mode %d\n", fifoname, o_mode);
if ((fd = open(fifoname, o_mode)) < 0)
err_syserr("Failed to open %s with mode %d: ", fifoname, o_mode);
err_remark("Opened FIFO %s with mode %d - fd %d\n", fifoname, o_mode, fd);
return fd;
}
static inline void recv_info(int num, int fd)
{
char buffer[MAX_MSGLEN];
int nbytes;
if ((nbytes = read(fd, buffer, sizeof(buffer))) <= 0)
err_syserr("P%d failed to read anything on fd %d: ", num, fd);
err_remark("P%d received %d bytes: [%.*s]\n", num, nbytes, nbytes, buffer);
}
static inline void send_info(int num, int fd, const char *dir)
{
char buffer[MAX_MSGLEN];
int buflen = snprintf(buffer, sizeof(buffer), "P%d (PID %d) sent this message %s",
num, (int)getpid(), dir);
int nbytes;
if ((nbytes = write(fd, buffer, buflen)) != buflen)
err_syserr("Failed to write properly on fd %d (%d vs %d wanted): ", fd, nbytes, buflen);
err_remark("P%d sent %d bytes: [%.*s]\n", num, nbytes, nbytes, buffer);
}
static void be_childish(int max, int num, char tubes[][MAX_TUBENAMELEN])
{
int wf; /* Descriptor for writing forwards */
int wb; /* Descriptor for writing backwards */
int rf; /* Descriptor for reading forwards */
int rb; /* Descriptor for reading backwards */
if (num == 0)
{
/* Child zero connects forwards then backwards */
wf = open_fifo(M_WF, max, num, tubes);
rf = open_fifo(M_RF, max, num, tubes);
rb = open_fifo(M_RB, max, num, tubes);
wb = open_fifo(M_WB, max, num, tubes);
send_info(num, wf, "forwards");
recv_info(num, rf);
recv_info(num, rb);
send_info(num, wb, "backwards");
}
else
{
/* Other children connect backwards then forwards */
rb = open_fifo(M_RB, max, num, tubes);
wb = open_fifo(M_WB, max, num, tubes);
wf = open_fifo(M_WF, max, num, tubes);
rf = open_fifo(M_RF, max, num, tubes);
recv_info(num, rb);
send_info(num, wb, "backwards");
send_info(num, wf, "forwards");
recv_info(num, rf);
}
close(wf);
close(wb);
close(rf);
close(rb);
}
int main(int argc, char **argv)
{
int n;
err_setarg0(argv[0]);
err_setlogopts(ERR_NOARG0|ERR_PID|ERR_MICRO);
err_settimeformat("%H:%M:%S");
if (argc < 2 || (n = (int)strtol(argv[1], NULL, 0)) < MIN_PROC || n > MAX_PROC)
{
fprintf(stderr, "Usage : %s <nombre>\n"
"Avec <nombre> compris entre %d et %d.\n",
argv[0], MIN_PROC, MAX_PROC);
exit(1);
}
char tubes[2 * n][MAX_TUBENAMELEN];
pid_t pid;
pid_t pids[n];
for (int i = 0; i < n * 2; i++)
{
snprintf(tubes[i], sizeof(tubes[i]), "tube%d", i);
printf("Fifo %d: [%s]\n", i, tubes[i]);
}
printf("Nombre de processus à engendrer : %d\n", n);
for (int k = 0; k < 2*n; k++)
{
printf("Create fifo: %s\n", tubes[k]);
if (mkfifo(tubes[k], 0666) != 0)
err_syserr("Failed to create FIFO %s: ", tubes[k]);
}
fflush(0);
for (int i = 0; i < n; i++)
{
pid = fork();
if (pid > 0)
{
pids[i] = pid;
err_remark("Création du processus fils #%d : PID %d\n", i, (int)pid);
}
else if (pid == 0)
{
usleep((i + 1) * 100000); // Tenths of a second
err_remark("Child process #%d (PID %d) at work\n", i, (int)getpid());
be_childish(n, i, tubes);
int status = (i + 1) * 16;
err_remark("Child process #%d (PID %d) exiting with status 0x%.2X\n", i, (int)getpid(), status);
exit(status);
}
else
{
err_sysrem("Failed to fork child %d: ", i);
for (int j = 0; j < i; j++)
{
err_remark("Killing %d\n", pids[j]);
kill(SIGTERM, pids[j]);
}
for (int j = 0; j < 2 * n; j++)
unlink(tubes[j]);
err_error("Terminating!\n");
}
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
err_remark("Child %d died with status 0x%.4X\n", corpse, status);
for (int j = 0; j < 2 * n; j++)
unlink(tubes[j]);
return 0;
}
輸出示例:
Fifo 0: [tube0]
Fifo 1: [tube1]
Fifo 2: [tube2]
Fifo 3: [tube3]
Fifo 4: [tube4]
Fifo 5: [tube5]
Nombre de processus à engendrer : 3
Create fifo: tube0
Create fifo: tube1
Create fifo: tube2
Create fifo: tube3
Create fifo: tube4
Create fifo: tube5
16:19:57.312293 - pid=89807: Création du processus fils #0 : PID 89810
16:19:57.314294 - pid=89807: Création du processus fils #1 : PID 89811
16:19:57.314500 - pid=89807: Création du processus fils #2 : PID 89812
16:19:57.413772 - pid=89810: Child process #0 (PID 89810) at work
16:19:57.415148 - pid=89810: Opening FIFO tube0 with mode 1
16:19:57.515290 - pid=89811: Child process #1 (PID 89811) at work
16:19:57.515558 - pid=89811: Opening FIFO tube0 with mode 0
16:19:57.515771 - pid=89810: Opened FIFO tube0 with mode 1 - fd 3
16:19:57.515788 - pid=89810: Opening FIFO tube1 with mode 0
16:19:57.515764 - pid=89811: Opened FIFO tube0 with mode 0 - fd 3
16:19:57.515883 - pid=89811: Opening FIFO tube1 with mode 1
16:19:57.516011 - pid=89810: Opened FIFO tube1 with mode 0 - fd 4
16:19:57.516020 - pid=89810: Opening FIFO tube4 with mode 0
16:19:57.516010 - pid=89811: Opened FIFO tube1 with mode 1 - fd 4
16:19:57.516120 - pid=89811: Opening FIFO tube2 with mode 1
16:19:57.615230 - pid=89812: Child process #2 (PID 89812) at work
16:19:57.615451 - pid=89812: Opening FIFO tube2 with mode 0
16:19:57.615582 - pid=89812: Opened FIFO tube2 with mode 0 - fd 3
16:19:57.615593 - pid=89811: Opened FIFO tube2 with mode 1 - fd 5
16:19:57.615678 - pid=89812: Opening FIFO tube3 with mode 1
16:19:57.615747 - pid=89811: Opening FIFO tube3 with mode 0
16:19:57.615852 - pid=89811: Opened FIFO tube3 with mode 0 - fd 6
16:19:57.615881 - pid=89812: Opened FIFO tube3 with mode 1 - fd 4
16:19:57.615986 - pid=89812: Opening FIFO tube4 with mode 1
16:19:57.616078 - pid=89810: Opened FIFO tube4 with mode 0 - fd 5
16:19:57.616090 - pid=89810: Opening FIFO tube5 with mode 1
16:19:57.616071 - pid=89812: Opened FIFO tube4 with mode 1 - fd 5
16:19:57.616153 - pid=89812: Opening FIFO tube5 with mode 0
16:19:57.616240 - pid=89810: Opened FIFO tube5 with mode 1 - fd 6
16:19:57.616277 - pid=89810: P0 sent 41 bytes: [P0 (PID 89810) sent this message forwards]
16:19:57.616236 - pid=89812: Opened FIFO tube5 with mode 0 - fd 6
16:19:57.616312 - pid=89811: P1 received 41 bytes: [P0 (PID 89810) sent this message forwards]
16:19:57.616444 - pid=89810: P0 received 42 bytes: [P1 (PID 89811) sent this message backwards]
16:19:57.616437 - pid=89811: P1 sent 42 bytes: [P1 (PID 89811) sent this message backwards]
16:19:57.616530 - pid=89811: P1 sent 41 bytes: [P1 (PID 89811) sent this message forwards]
16:19:57.616535 - pid=89812: P2 received 41 bytes: [P1 (PID 89811) sent this message forwards]
16:19:57.616660 - pid=89812: P2 sent 42 bytes: [P2 (PID 89812) sent this message backwards]
16:19:57.616665 - pid=89811: P1 received 42 bytes: [P2 (PID 89812) sent this message backwards]
16:19:57.616772 - pid=89812: P2 sent 41 bytes: [P2 (PID 89812) sent this message forwards]
16:19:57.616881 - pid=89810: P0 received 41 bytes: [P2 (PID 89812) sent this message forwards]
16:19:57.616893 - pid=89810: P0 sent 42 bytes: [P0 (PID 89810) sent this message backwards]
16:19:57.616817 - pid=89811: Child process #1 (PID 89811) exiting with status 0x20
16:19:57.617243 - pid=89810: Child process #0 (PID 89810) exiting with status 0x10
16:19:57.617501 - pid=89812: P2 received 42 bytes: [P0 (PID 89810) sent this message backwards]
16:19:57.617726 - pid=89807: Child 89811 died with status 0x2000
16:19:57.618114 - pid=89812: Child process #2 (PID 89812) exiting with status 0x30
16:19:57.618313 - pid=89807: Child 89810 died with status 0x1000
16:19:57.618635 - pid=89807: Child 89812 died with status 0x3000
您可以在GitHub找到這段代碼。
爲什麼使用'mkfifo()'而不是'pipe()'?你的流程都是密切相關的,所以不需要清理的管道似乎是比需要清理的FIFO更好的選擇。過程如何決定何時圍繞環順時針通信,何時逆時針通信?進程如何檢測是否有輸入等待順時針輸入或逆時針輸入?如果任何一個流程做出錯誤的決定,事情將會鎖定。 (請注意,您應該將ASCII圖像作爲代碼包含在代碼中,而不是作爲外部圖像。) –
感謝編輯@ JonathanLeffler.i已經使用pipe做了它,但現在需要更好的了,我想要使用命名管道製作相同的程序 – Goolger
您的'管道'名稱數組是'char tubes [2 * n];' - 您實際上需要類似'char tubes [2 * n] [20];'有足夠的空間來寫出不同的名字。你的代碼可能會崩潰,因爲你正在寫出那個數組的邊界。當你編寫'mkfifo(tubes [i],0666)時,你應該從你的編譯器中聽到憤怒的聲音;'因爲你將'char'傳遞給了一個需要'char *'的函數。注意你的編譯器警告 - 並確保你已經得到了警告級別。你在Linux上;你可能使用GCC。我用'gcc -Wall -Wextra -Werror ...'編譯代碼等等。 –