C管没有沟通

时间:2012-09-24 13:49:52

标签: c process ipc fork pipe

我有一个编码分配,其中我要使用fork()设置一个进程环,然后通过环传递一个消息。现在,显而易见的问题是我无法将消息从初始进程传递给其直接连接的子进程。 (只是先做一个消息传递作为测试)然而,我意识到它也可能是环无法正常工作的情况。如果是这种情况,我会不会感到惊讶,因为我正在使用的消息传递测试基本上是示例代码逐字,但是再次,形成环的代码也是如此...

所以,我的问题是:任何人都可以帮我解决我的代码中出错的问题吗?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int msg[2];
const int MAX = 100;

int main(int argc, char *argv[]) {

    int master, i, child, num, pid, ppid, counter, loops;
    char buffer[MAX];
    num = atoi(argv[1]);
    loops = atoi(argv[2]);
    counter = 0;

    master = (int)getpid();

    //check num arguments
    if (argc != 4) {
        fprintf(stderr, "%s\n", "incorrect # arguments");
    }
    pipe(msg);                      //create pipe
    dup2(msg[0], STDIN_FILENO);     //duplicate pipes
    dup2(msg[1], STDOUT_FILENO);
    close(msg[0]);                  //close ends of pipe
    close(msg[1]);

    //create other processes
    for(i=1; i<num; i++) {
        pipe(msg);                  //create new pipe

        //create new process
        child = fork();             //parent has child id
        pid = (int)getpid();        //has own pid
        ppid = (int)getppid();      //has parent pid

        //if parent, fix output
        if(child > 0){
            dup2(msg[1], STDOUT_FILENO);
        } else {
            dup2(msg[0], STDIN_FILENO);
        }
        close(msg[0]);
        close(msg[1]);
        if(child){
            break;
        }
    }

    //simple output
    fprintf(stderr, "process %d with id %d and parent id %d\n", i, pid, ppid);

    //message passing
    //if master, establish trasnfer
    if (pid == master) {
        //parent
        close(msg[0]);  //closes its read end
        char buffer[MAX];
        fprintf(stderr, "Parent: Waiting for input\n");
        while(1) {
            scanf("%s", buffer);
            if (strcmp(buffer, "exit")==0) {
               break;
            }
            write(msg[1], buffer, MAX);
        }
        close(msg[1]);  //closes it write end
    } else {
        //child
        close(msg[1]);  //closes its write end
        char buffer[MAX];
        fprintf(stderr, "Child: Waiting for pipe\n");
        while(read(msg[0], buffer, MAX) > 0) {
            fprintf(stderr, "Received: %s\n", buffer);
            buffer[0] = '\0';
        }
        close(msg[0]);  //closes its read end        
    }

    //special stuff for master node
    if(master == pid){
        fprintf(stderr, "%s\n", "i am the master");
        //special stuff
    } else {
        fprintf(stderr, "%s\n", "i am a child");
        //nothing really?
    }

    wait(2); //let all processes finish.
    exit(0);
}

我会问我的导师或助教,但他们都决定离开城镇,远离电子邮件,直到作业到期。如果我不能让这个工作,它不是世界末日,但我想避免以不完整的编码任务开始课程。

4 个答案:

答案 0 :(得分:2)

恕我直言,你最好在引用它们之前验证命令行参数(即代码应该像

if (argc != 4) {
    ...
}

int num = atol(argv[1]);

我不清楚你的代码,我很抱歉我的不耐烦。 以下是我的解决方案,其中包含要验证的代码段。 要建立环,首先将注释过程分为两种类型:主进程,初始化IPC和从属进程。 最初,我们需要两个管道,一个用于主机读取,另一个用于主机写入。 在初始化之后,两个进程通过这样的管道连接(P代表父代,C代表子代)。

C:+----------+-----------+
  |          |           |
  | Read End | Write End |
  |          |           |
  +----------+-----------+
         |\            \
           \            \
            \            \
             \            \
              \            \
P:+----------+-----------+  \
  |          |           |   \
  | Read End | Write End |    \
  |          |           |     \
  +----------+-----------+      |
       /|                       |
        |                       |
        +-----------------------+

递归地,当新进程分叉时,我们希望连接演变为:

   C2:+----------+-----------+
      |          |           |
      | Read End | Write End |
      |          |           |
      +----------+-----------+
             |\                \
               \                \
                \                \
                 \                \
                  \                \
   C1:+----------+-----------+      |
      |          |           |      |
      | Read End | Write End |      |
      |          |           |      |
      +----------+-----------+      |
             |\                     |
               \                    |
                \                   |
                 \                  |
                  \                 |
    P:+----------+-----------+      |
      |          |           |      |
      | Read End | Write End |      |
      |          |           |      |
      +----------+-----------+      |
           /|                       |
            |                       |
            +-----------------------+

因此我们需要C1和C2之间的另一个管道,然后重定向I / O. 以下ASCII艺术说明了这一点。

                                  C2:+----------+-----------+
                                     |          |           |
                                     | Read End | WriteEnd3 |
                                     |          |           |
                                     +----------+-----------+
                                             |\           \
                                               \           \
                                                \           \
                                                 \           \
   C1:+----------+-----------+       +----------+-----------+ |
      |          |           |       |          |           | |
      | Read End | WriteEnd1 |       | Read End | WriteEnd2 | |
      |          |           |       |          |           | |
      +----------+-----------+       +----------+-----------+ |
             |\            \              /|                  |
               \            \              |                  |
                \            \             +------------------+
                 \            \
                  \            \
    P:+----------+-----------+  \
      |          |           |   \
      | Read End | Write End |    \
      |          |           |     \
      +----------+-----------+      |
           /|                       |
            |                       |
            +-----------------------+

要形成铃声, WriteEnd2 应该重复到 WriteEnd1 ,因此child2可以从child1读取。 WriteEnd1 也应该取代 WriteEnd3 来写入master。

我包含示例代码,但是,您最好了解完成作业所发生的事情,而不仅仅是复制和粘贴:)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BUF_LEN 32

int  input_pipe[2];
int output_pipe[2];

char buf[BUF_LEN];
const char msg[] = "hello world.\n";
int str_len = sizeof(msg);

int status;

int main(int argc, char **argv) {
    /* master input channel */
    int master_input;
    int ring_pid = 0;

    if (argc != 2) {
        fprintf(stdout, "Argument error!\n");
        fflush(stdout);

        exit(-1);
    }

    int total_processes = atoi(argv[1]);

    if (pipe(input_pipe) || pipe(output_pipe)) {
        fprintf(stdout, "Cannot create pipes at master. Program exits.\n");
        fflush(stdout);

        exit(-1);
    } else {
        /* fork child process */
        pid_t pid = fork();
        if (pid) {
            /* dup read end of input pipe and write end of output pipe. */
            int input_fd  = dup(input_pipe[0]);
            int output_fd = dup(output_pipe[1]);

            close(input_pipe[0]);
            close(input_pipe[1]);
            close(output_pipe[0]);
            close(output_pipe[1]);

            /* parent process */
            fprintf(stdout, "Send message to downstream: %s", msg);
            fflush(stdout);
            write(output_fd, msg, str_len + 1);

            read(input_fd, buf, BUF_LEN);
            fprintf(stdout, "Recieve message from upstream: %s", buf);
            fflush(stdout);

            waitpid(pid, &status, 0);
        } else {
            /* child process */
            /* write end of master's input pipe */
            master_input  = dup(input_pipe[1]);

            int input_fd  = dup(output_pipe[0]);
            int output_fd = -1;

            /* increase ring number */
            while (++ring_pid < total_processes) {
                int tmp_pipe[2];

                if (pipe(tmp_pipe)) {
                    fprintf(stdout, "Cannot create pipes at master. Program exits.\n");
                    fflush(stdout);

                    exit(-1);
                } else {
                    /* pid of sub-process's child */
                    pid_t spid = fork();
                    if (spid) {
                        /* drop read end of the pipe, we read from parent process */
                        close(tmp_pipe[0]);
                        /* output of last process directs to master's input */
                        if (ring_pid == total_processes - 1) {
                            output_fd = dup(master_input);
                        } else {
                            output_fd = dup(tmp_pipe[1]);
                        }
                        close(tmp_pipe[1]);
                        /* receive and send message */
                        read(input_fd, buf, BUF_LEN);
                        fprintf(stdout, "Read from upstream: %s", buf);

                        fprintf(stdout, "Write to downstream: %s", buf);
                        write(output_fd, buf, BUF_LEN);

                        waitpid(spid, &status, 0);
                        break;
                    } else {
                        /* for child process, the input is read end of the new pipe. */
                        input_fd = dup(tmp_pipe[0]);
                        close(tmp_pipe[0]);
                    }
                }
            }
        }
    }
}

答案 1 :(得分:0)

我可以建议的一件事是在分叉过程时使用这种风格:

child_pid = fork();
if (child_pid==0) {
  childProcess(); // isolate the child process logic
  _exit(0); // make sure the execution doesn't escape the if statement
}

我认为这可以帮助您查看代码中的一些逻辑问题。

答案 2 :(得分:0)

您正在尝试使用scanf读取输入,msg从标准输入读取。但是,您所做的第一件事就是通过复制第一个管道的读取端来关闭原始标准输入。因此,您正在尝试从管道读取输入。要解决此问题,您应该将第一个管道的读取结束保存在变量中,以便主控制器从以后读取,但不能通过标准输入复制它。

此外,当您尝试读取或写入管道时,您正在使用 input(stdin) v +> A -+ | v D B ^ | +- C <+ 中存储的描述符,但在创建链时关闭了这些描述符。您需要读取/写入复制管道的标准输入/输出描述符。在读/写之前,您也(尝试)关闭管道链的未使用端,但是当您开始使用将不再使用的整个链时。

例如,4个进程的循环可能如下所示:

{{1}}

创建所有进程后,每个连接都是标准输入或输出,但链返回进程A(主进程)除外。过程A从标准输入读取并写入标准输出,标准输出连接到过程B的标准输入。然后,进程B读取其标准输入并写入其标准输出,将消息传递给进程C.进程C和D执行相同的操作,将消息一直传递到循环并返回到读取端口上的进程A.原管道。由于每个过程都负责读取和写入,因此在完成任何管道之前,它们都不应关闭。

答案 3 :(得分:0)

您的算法需要更改。逻辑,这里是如果你有n个孩子,那么你需要有n + 1个管道。

现在您需要将管道的末端存储在合适的数据结构中。我使用了一个二维数组,其中每一行代表一个管道,并说第0列是读取结束,第1列是特定管道的写入结束。(pl考虑使这个数组动态化)

现在对于每个孩子,我们需要两个结束一个读端,另一个写结束。现在这里的这些目的将来自两个不同的管道。这就是逻辑。

这意味着我们要考虑child1,它从pipe1读取结束并从pipe2写入结尾。现在,child0应该从pipe1写入结束,而child2应该从pipe2读取结束。希望这个想法很清楚。

(现在另一个标准是进程需要表现为过滤器,即从标准输入读取并写入标准输出。这是使用dup2函数完成的,它将复制相应的用于读写的管道描述符,标准输入和输出)

我在下面提供了一些示例代码,其中包含一些注释,可以帮助您理解这个概念。

(有硬编码值和非常少的错误处理。你应该添加。)

#define MAXCHILD 50
#define NO_OF_CHILDREN 20
#define MAXBUF 100

void child(int readend, int writeend);
int main()
{
int pipefd[MAXCHILD + 1][2];
int noofchildren = 0;
int nchild = NO_OF_CHILDREN;
int ret;
int i ;
char buf[MAXBUF];
for(i = 0; i <= nchild; i++)
{
    ret = pipe(pipefd[i]);
    if (0 > ret)
    {
        printf("Pipe Creation failed\n");
        exit(1);
    }
}
printf("Pipes created\n");

for(i = 0; i < nchild; i++)
{

    ret = fork();
    if (0 == ret)
    {
        child(pipefd[i][0], pipefd[i + 1][1]);
    }
    //These ends are no longer needed in parent.
    close (pipefd[i][0]);
    close(pipefd[i + 1][1]);
}

printf("%d Children  Created\n", nchild);

while(1) 
{
    printf("Enter Some data\n");
    //Now the parent/master reads from the standard input
    fgets(buf, MAXBUF, stdin);
    //Write to the pipe which is connected to the first child.
    write(pipefd[0][1],buf, strlen(buf) + 1);
    //Read from the pipe, which is attached to the last child created
    read(pipefd[nchild][0], buf, MAXBUF);
    printf("Master Read from the pipe - %s\n", buf);
    if (strcmp(buf, "exit\n") == 0)
    {
        break;
    }
} 
//By this time all other children, would have exited
//Let's wait to see this 
while(1)
{
    ret = wait(NULL);
    if (0 > ret) //Returns -ve when no more child is left.
    {
        break;
    }
    noofchildren++;
}
printf("My %d children terminated! Now I am exiting", noofchildren);
exit(0);
}

void child(int readend, int writeend)
{
char buf[MAXBUF];
int ret;
//set up environment
dup2(readend, 0);
dup2(writeend,1);   
while(1)
{
    ret = read(0, buf, MAXBUF );
    if (strcmp(buf, "exit\n") == 0)
    {
        write(1, buf, ret);
        exit(0);
    }
    write(1, buf, ret);
}
}