2017-04-03 58 views
1

目前我正在学习C,我想用叉子和管子制作n个孩子的过程,其中n是参数中输入的数字。每个孩子可以用下一子在两个方向上像this通信:如何用fork和mkfifo制作过程环?

p1 --->--- p2 
    ---<--- 
||   || 
^v   ^v 
||   || 
    --->--- 
p4 ---<--- p3 

我试图做到这一点,每个孩子送到下一个孩子的PID或消息,但我没有得到我想要的实例。当我运行该程序,我有两个问题:

  • 第一个问题是,如果我在5进入的说法,我得到分割故障(核心转储)错误

  • 第二个问题是当一个进程在环内发送它的pid时,它似乎是唯一一个读取它的人。

怎么回事?

我对循环中的多个fifos并不是很舒服。

#define _XOPEN_SOURCE 500 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <sys/stat.h> 
#include <fcntl.h> 


#define MIN_PROC 2  // Nombre minimum de processus 
#define MAX_PROC 20  // Nombre maximum de processus 

int main (int argc,char ** argv) 
{ 
int n; // Nombre de processus dans l'anneau 

/* Lecture du paramètre */ 
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); 
} 
else 
{ 
    char tubes[2*n]; 
    pid_t pid; 
      // Réception du PID lors de fork() 
    int  p[n][4]; // Place pour n paires de descripteurs (VLA) 
    int  i,pos =0,k;   // Indice de boucle 

    /* Indication du nombre de processus à engendrer */ 
    for (int i = 0; i < n*2; i++) 
    { 
     pos += sprintf(&tubes[pos],",tube%d",i); 
    } 
    printf("String is now:%s\n",tubes); 
    printf ("Nombre de processus à engendrer : %d\n",n); 

    /* Création des n tubes anonymes.    */ 
    /* On considère que l'appel réussit toujours. */ 
    for (k=0;k<n;k++){ 
     for (int i = 0; i < 4; i++) 
      { 
       p[k][i] = mkfifo(tubes[i],0666); 

      } 
     } 

    /* Génération des n processus fils, qui vont communiquer */ 
    /* entre eux. Le processus père reste en superviseur. */ 
    for (i=0;i<n;i++) 
    { 
     pid = fork(); 

     if (pid>0) 
     { 
      printf ("Création du processus fils #%d : PID %d\n",i,pid); 
     } 
     else if (!pid) 
     { 


      int  in[2];  /* Descripteurs d'entrée  */ 
      int  out[2]; /* Descripteurs de sortie */ 
      int  data; /* Donnée à émettre   */ 

      int  j;  /* Autre indice de boucle (puisque i reste */ 
          /* de fait notre numéro de processus fils. */ 

      /* Temporisation pour que la boucle du processus père ait */ 
      /* le temps de se terminer avant de commencer le traitement */ 
      sleep(1); 

      for (int i = 0; i < n; i++) 
      { 
       if (i%2 == 0) 
       { 
        for (int i = 0; i < 2; ++i) 
        { 
         in[i]= open(tubes[i],O_RDONLY); 
         out[i]= open(tubes[i],O_WRONLY); 
        } 

       } 
       else { 
        for (int i = 0; i < 2; ++i) 
        { 
         in[i]= open(tubes[i],O_WRONLY); 
         out[i]= open(tubes[i],O_RDONLY); 
        } 
       } 

      } 

      /* Récupération des descripteurs adéquats */ 
      in[0] = p[ i  ][0]; 
      out[0] = p[(i+1) % n][1]; 
      in[1] = p[ i  ][2]; 
      out[1] = p[(i+1) % n][3]; 

      /* Fermeture des descripteurs inutiles */ 
      for (j=0;j<n;++j) 
      { 
       if (p[j][0] != in[0]) close(p[j][0]); 
       if (p[j][1] != out[0]) close(p[j][1]); 
       if (p[j][2] != in[1]) close(p[j][2]); 
       if (p[j][3] != out[2]) close(p[j][3]); 
      } 

      /* Récupération et émission de notre PID */ 
      data = (int)getpid(); 
      printf ("Processus #%d : émission de %d\n",i,data); 
      write (out[0],&data,sizeof data); 
      close (out[0]); 

      /* Réception de la donnée de l'autre processus */ 
      data = (int)getpid(); 
      read (in[0],&data,sizeof data); 
      printf ("Processus #%d : réception de %d\n",i,data); 
      close (in[0]); 

      data = (int)getpid(); 
      printf ("Processus #%d : émission de %d\n",i,data); 
      write (out[1],&data,sizeof data); 
      close (out[1]); 

      /* Réception de la donnée de l'autre processus */ 
      data = (int)getpid(); 
      read (in[1],&data,sizeof data); 
      printf ("Processus #%d : réception de %d\n",i,data); 
      close (in[1]); 

      /* En fin de traitement, un break pour quitter la boucle */ 
      /* démarrée par le processus père.      */ 
      break; 
     } 
     else perror ("Erreur à l'appel de fork()"); 
    } 

    /* Si PID est non nul à l'issue de la boucle, c'est qu'on est */ 
    /* toujours dans le processus père. On en profite pour faire n */ 
    /* wait() successifs pour attendre tous nos fils.    */ 
    if (pid>0) 
    for (i=0;i<2*n;i++) { 
     wait(NULL); 
     unlink(tubes[i]); 
    } 
} 

    return 0; 
} 
+2

为什么使用'mkfifo()'而不是'pipe()'?你的流程都是密切相关的,所以不需要清理的管道似乎是比需要清理的FIFO更好的选择。过程如何决定何时围绕环顺时针通信,何时逆时针通信?进程如何检测是否有输入等待顺时针输入或逆时针输入?如果任何一个流程做出错误的决定,事情将会锁定。 (请注意,您应该将ASCII图像作为代码包含在代码中,而不是作为外部图像。) –

+0

感谢编辑@ JonathanLeffler.i已经使用pipe做了它,但现在需要更好的了,我想要使用命名管道制作相同的程序 – Goolger

+0

您的'管道'名称数组是'char tubes [2 * n];' - 您实际上需要类似'char tubes [2 * n] [20];'有足够的空间来写出不同的名字。你的代码可能会崩溃,因为你正在写出那个数组的边界。当你编写'mkfifo(tubes [i],0666)时,你应该从你的编译器中听到愤怒的声音;'因为你将'char'传递给了一个需要'char *'的函数。注意你的编译器警告 - 并确保你已经得到了警告级别。你在Linux上;你可能使用GCC。我用'gcc -Wall -Wextra -Werror ...'编译代码等等。 –

回答

1

这是一个有效的实现。它需要一些注意力来建立连接,如评论中所述,而不是在这个答案中重复。写入顺序也必须与打开序列匹配:

  • P0将向前写入,向前读取,向后读取,向后写入。
  • PN将向后读取,向后写入,向前写入,向前读取。

请注意,子代码基本上是同步的 - 没有太多可能发生的重新排序。调用nanosleep()只是确保启动消息按顺序排列。如果您放弃它,只有报告输出可能会变得混乱。

我用我的标准错误报告包"stderr.h" and stderr.c来处理来自代码的大部分消息。 (目前,如果您使用stderr.c,则还需要来自同一目录的kludge.hkludge.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找到这段代码。

+0

令人惊叹的工作,非常感谢Jonathan.And如果我可能会问,你为什么说不使用O_NONBLOCK是正确的 – Goolger

+0

嗯,它不是'不必要的''不正确' - 在任何地方见证没有任何'O_NONBLOCK'引用的代码。当然有使用它的方法,但是你必须知道,当没有数据等待时,你可能会从read()调用中获得'错误'。使用O_NONBLOCK会让生活变得更加复杂。所以会使用O_RDWR;这也实现了'不会阻止FIFO对open()'的影响。 –

+0

非常感谢您的解释 – Goolger