【问题标题】:Write numbers from 1 to 100 using fork() and pipe()使用 fork() 和 pipe() 写入从 1 到 100 的数字
【发布时间】:2018-06-19 13:09:43
【问题描述】:

我需要编写一个包含 2 个进程的程序,其中一个写入偶数,另一个写入奇数。结果,我必须有从 1 到 100 的数字。

我输入了这段代码,但是当涉及到进程部分时,它卡在了printProc() 函数中。我猜问题在于管道中的读写。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int fd[2];

int printProc(int startNumber, int procNumber);

int main()
{
    pid_t childpid;

    pipe(fd);
    int start = 0;
    write(fd[0], &start, sizeof(start));

    if ((childpid = fork()) == -1)
    {
        perror("fork");
    }

    if (childpid == 0)
    {
        printf("run child\n");

        printProc(1, 0);
    }
    else
    {
        printf("run parent\n");

        printProc(2, 1);
    }

    return 0;
}

int printProc(int startNumber, int procNumber)
{
    FILE *f;
    f = fopen("output.txt", "a+");

    int num = startNumber;
    int proc;

    while (num <= 100)
    {
        read(fd[1], &proc, sizeof(proc));

        if (proc == procNumber)
        {
            fprintf(f, "%d", num);
            num = num + 2;
            proc = (proc + 1) % 2;
            write(fd[0], &proc, sizeof(proc));
        }
    }

    return 0;
}

【问题讨论】:

  • 为什么是全球性的?为什么要打开文件?为什么忽略返回值? pipe() 的手册给出了一个示例代码,几乎可以满足您的需求……您在使用函数之前阅读了手册吗?
  • @Nicholas Goncharov 整个简介用英文写会好很多。:)

标签: c pipe fork ipc


【解决方案1】:

你需要2个管道,当一个进程写入数字时,它会向一个管道写入一些数据,以便另一个进程知道它可以写入自己的数字,等等。

实现示例:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>

int main(void) {
  int parent_to_child[2];
  int child_to_parent[2];
  if (pipe(parent_to_child) == -1 || pipe(child_to_parent) == -1) {
    perror("pipe");
    return EXIT_FAILURE;
  }
  pid_t cpid = fork();
  if (cpid == -1) {
    perror("fork");
    return EXIT_FAILURE;
  }
  if (cpid == 0) {
    close(parent_to_child[1]);
    close(child_to_parent[0]);

    int i = 1;
    bool buf;
    ssize_t ret;
    while ((ret = read(parent_to_child[0], &buf, sizeof buf)) > 0) {
      fprintf(stdout, "%d ", i);
      fflush(stdout);
      if (write(child_to_parent[1], &buf, sizeof buf) != sizeof buf) {
        break;
      }
      i += 2;
    }

    close(parent_to_child[0]);
    close(child_to_parent[1]);
  } else {
    close(parent_to_child[0]);
    close(child_to_parent[1]);

    int i = 2;
    bool buf = true;
    ssize_t ret;
    while (i <= 100 &&
           write(parent_to_child[1], &buf, sizeof buf) == sizeof buf &&
           (ret = read(child_to_parent[0], &buf, sizeof buf)) > 0) {
      fprintf(stdout, i != 100 ? "%d " : "%d\n", i);
      fflush(stdout);
      i += 2;
    }

    close(parent_to_child[1]);
    close(child_to_parent[0]);
  }
}

【讨论】:

  • 感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 它的长期价值通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
  • @TobySpeight 首先,关于 stackoverflow 的元数据会更有用......我不明白你为什么链接 meta.stackexchange.com 的问题。其次,我决定不解释自我解释代码。关于pipe()fork() 的工作方式还有很多其他问题,这不是 OP 所要求的,“使用 fork() 和 pipe() 从 1 到 100 写入数字”,我从字面上回答了这个问题,OP给我看一些工作,我认为这值得一些帮助。否决我我不认为仅代码答案不好,如果我真的需要解释这段代码,我会以解释 C 结束,那将花费太多时间。
  • 顺便说一句,我不认为我的解决方案是“完美的”,它只是一个可能的解决方案。顺便说一句,我没有故意让代码产生确切的解决方案;)。
  • @Stargateur:由于您没有对代码提供任何解释,乍一看并不明显它不符合一个 OP 的要求:2 个进程,其中一个写入偶数另一个写入奇数。不,没有单个注释的代码不是自我解释代码。
  • @SergeBallesta 我没有说自我记录,而是自我解释......无论如何,我已经厌倦了,你让我意识到 OP 在问题和标题中描述了完全不同的行为, 这个问题需要 IPC,我不想回答这个烂摊子的问题。
【解决方案2】:

如果你想用管道来同步2个进程,你真的应该使用一对管道,前者由proc1读取,由proc2写入,后者由proc2读取,由proc1写入。从那里您将读取和写入句柄传递给printProc,仅此而已。

但是您的代码中还有另一个问题:您在两个进程中都以缓冲模式打开了一个输出文件(使用fopen)。所以每个进程都会有自己的缓冲区。两个缓冲区将分别提供,并且仅在关闭时写入文件,这不是您想要的:您必须在 fork 之前打开输出文件,并在非缓冲模式下使用它。

所以你的代码可以变成:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>


int printProc(int startNumber, FILE *f, int fdin, int fdout);

int main()
{
    pid_t childpid;
    int fd[4];
    FILE *f;

    pipe(fd);
    pipe(fd + 2);
    int start = 0;
    write(fd[0], &start, sizeof(start));
    f = fopen("output.txt", "w");
    if (f == NULL) {
        perror("Error opening file");
        return 1;
    }
    setbuf(f, NULL);             // use unbuffered mode for output.txt

    if ((childpid = fork()) == -1)
    {
        perror("fork");
    }

    if (childpid == 0)
    {
        printf("run child\n");

        printProc(1, f, fd[1], fd[2]);
    }
    else
    {
        printf("run parent\n");

        printProc(2, f, fd[3], fd[0]);
    }
    fclose(f);
    return 0;
}

int printProc(int num, FILE *f, int fdin, int fdout)
{
    int proc;

    while (num <= 100)
    {
        read(fdin, &proc, sizeof(proc));

        fprintf(f, "%d\n", num);
        num = num + 2;
        write(fdout, &proc, sizeof(proc));
    }
    return 0;
}

【讨论】:

    【解决方案3】:

    我建议在这里使用互斥锁。您的关键部分将包含三个操作:

    • 检查是否轮到你写作
    • 如果以上为真,则写入输出
    • 如果您写入输出,请向其他进程发出信号,表明轮到他了

    以下是实现这一目标的代码:

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <pthread.h>
    #include <sys/mman.h>
    
    int main() {
        pthread_mutex_t *pmutex;
        short * even;
        pthread_mutexattr_t attrmutex;
    
        pthread_mutexattr_init(&attrmutex);
        pthread_mutexattr_setpshared(&attrmutex, PTHREAD_PROCESS_SHARED);
    
        pmutex = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
        even = mmap(NULL, sizeof(short), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
        pthread_mutex_init(pmutex, &attrmutex);
    
    
        pthread_mutex_lock(pmutex);
    
        *even = 0;
    
        if(fork() == 0){
            int a = 2;
    
            while(a <= 100) {
                pthread_mutex_lock(pmutex);
                if(*even) {
                    printf("%d\n", a);
                    *even = 0;
                    a = a + 2;
                }
                pthread_mutex_unlock(pmutex);
            }
    
            return 0;
        }else {
            int a = 1;
    
            printf("%d\n", a);
            *even = 1;
            pthread_mutex_unlock(pmutex);
            a = a + 2;
            while(a <= 99) {
                pthread_mutex_lock(pmutex);
                if(!*even) {
                    printf("%d\n", a);
                    *even = 1;
                    a = a + 2;
                }
                pthread_mutex_unlock(pmutex);
            }
    
        }
    
        pthread_mutex_destroy(pmutex);
        pthread_mutexattr_destroy(&attrmutex);
    }
    

    两个进程共享两个变量:pmutex(用于同步)和even(用于检查允许打印哪个进程)。

    【讨论】:

    • 看起来这些功能并非随处启用,rextester.com/MBCTYE81393 不起作用,但您的代码在我的机器上运行。也许你的代码会通过添加错误检查来改进。
    • 感谢@Stargateur 指出,确保这段代码可以在很多方面进行改进,但我想分享对我有用的最小示例。
    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    相关资源
    最近更新 更多