如何使用dup2将stdin和stdout重定向到管道文件描述符?

huangapple go评论62阅读模式
英文:

How to use dup2 to redirect stdin and stdout to pipe file descriptors?

问题

我试图分叉一个进程,并将父进程的标准输出重定向到管道的写入端,将子进程的标准输入重定向到管道的读取端。子进程应该读取整数,直到父进程打印零。父进程从1打印到3,然后打印0。父子进程都会在它们启动和完成时打印时间。由于父进程无法将内容打印到标准输出,因此它将其启动和完成时间发送给子进程,子进程打印其启动时间、完成时间以及父进程的启动时间和完成时间。我本可以使用dup并将标准输出重定向到另一个文件描述符,但我选择让它简单。这个程序非常简单,但我得到的输出看起来不合理。

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

int main() 
{ 
    int fd[2];
    int p = pipe(fd);
    int ch = fork();
 
    if (ch)
    {
        // 父进程 - 从1计数到3
        int dp = dup2(fd[1], 1);
        printf("Cnt_Started_at_%d\n", time(NULL));
        for (int i = 0; i <= 3; i++)
        {
            printf("Parent %d\n", i);
            sleep(1);
        }
        printf("0\n");
        printf("Cnt_Finished_at_%d\n", time(NULL));
    }
    else 
    {
        // 子进程 - 通过0终止
        int dp = dup2(fd[0], 0);
        printf("Trm_Started_at_%d\n", time(NULL));
        char buffer[100];
        scanf("%s", buffer);
        printf("%s\n", buffer);
 
        int i; 
        while (scanf("Parent %d", &i) && i)
            printf("Received: %d\n", i);
 
        scanf("%s", buffer);
        printf("%s\n", buffer);
        printf("Trm_Finished_at_%d\n", time(NULL));
    }
}

输出:

Trm_Started_at_1578295974
Cnt_Started_at_1578295974
Parent 0
Trm_Finished_at_1578295978
英文:

I was trying to fork a process and redirect stdout of the parent to the writing end of the pipe and stdin of the child to the reading end of the pipe. The child is supposed to read integers until the parent prints zero. the parent prints from 1 to 3 and then prints 0. Both the parent and the child prints the time when they start and when they finish. since the parent can't print to stdout it sends it's starting and finishing time to the child and the child prints both its starting time and finishing time and parents starting time and finishing time. I could've used dup and redirected stdout to another file descriptor but I chose to make it simple. The program is very simple but the output that I get doesn't make scene.

#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;fcntl.h&gt;
 
int main() 
{ 
	int fd[2];
	int p = pipe(fd);
	int ch = fork();
 
	if (ch)
	{
		// Parent - Counts from 1 to 3
		int dp = dup2(fd[1], 1);
		printf(&quot;Cnt_Started_at_%d\n&quot;, time(NULL));
		for (int i = 0; i &lt;= 3; i++)
		{
			printf(&quot;Parent %d\n&quot;, i);
			sleep(1);
		}
		printf(&quot;0\n&quot;);
		printf(&quot;Cnt_Finished_at_%d\n&quot;, time(NULL));
	}
	else 
	{
		// Child - Terminated by 0
		int dp = dup2(fd[0], 0);
		printf(&quot;Trm_Started_at_%d\n&quot;, time(NULL));
		char buffer[100];
		scanf(&quot;%s&quot;, buffer);
		printf(&quot;%s\n&quot;, buffer);
 
		int i; 
		while (scanf(&quot;Parent %d&quot;, &amp;i) &amp;&amp; i)
			printf(&quot;Recieved: %d\n&quot;, i);
 
		scanf(&quot;%s&quot;, buffer);
		printf(&quot;%s\n&quot;, buffer);
		printf(&quot;Trm_Finished_at_%d\n&quot;, time(NULL));
	}
}

Output:

Trm_Started_at_1578295974
Cnt_Started_at_1578295974
Parent
Trm_Finished_at_1578295978

答案1

得分: 0

根本问题是使用了 'scanf %s' 来读取消息。请注意,'%s' 在遇到空格时会停止读取,并将空格放回缓冲区。

父进程的初始消息是 'Cnt_Started_at_1234\n'。这个子进程将读取标记,但会将尾随的 '\n' 留在缓冲区中。

接下来父进程将发送 'Parent 0\n'。子进程将尝试解析它为 scanf("Parent %d", &i) && i。这里有两个问题:

  • 来自 'Parent' 的 'P' 将无法与初始消息中剩余的 '\n' 匹配。
  • 当格式更新以跳过前导空格时,'i' 的值将为零,这将导致读取 'Parent 0' 后退出 while 循环。

可能的解决方案:允许 scanf 跳过空格,并消除对 i 的条件

while (scanf(" Parent %d", &i) == 1 )
        printf("接收到:%d\n", i);
英文:

The root issue is the usage of 'scanf %s' to read messages. Recall that '%s' will stop reading when it encounter white space, and will put the white space back into the buffer.

The initial message from the parent is 'Cnt_Started_at_1234\n'. This child process will read the token, but will leave the trailing '\n' in the buffer.

Next the parent will send 'Parent 0\n'. The Child will attempt to parse this is scanf(&quot;Parent %d&quot;, &amp;i) &amp;&amp; i). Two issues here:

  • The 'P' from 'Parent' will not match the left over '\n' from the initial message
  • When the format is updated to skip of leading spaces, the value of 'i' will be zero, which will cause the while to exit after reading 'Parent 0'.

Possible solution: Allow the scanf to skip of spaces, and eliminate the condition on i

    while (scanf(&quot; Parent %d&quot;, &amp;i) == 1 )
            printf(&quot;Recieved: %d\n&quot;, i);

答案2

得分: 0

以下是翻译的内容:

问题出在你的 scanf 语句上。如 @dash-o 所建议的,将其修改以处理空格。

另一个问题是,首先 i = 0。你需要修改你的 while 循环以适应 0。

由于你只在你的 while 循环 中评估 i,所以你不会进入 i=0 的情况。

下面是修改后的程序和输出,此外,请根据你认为合适的情况添加各种函数/缓冲区溢出的返回值检查 --

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

int main()
{
    int fd[2];
    int p = pipe(fd);
    int ch = fork();

    if (ch)
    {
        // 父进程 - 从1计数到3
        int dp = dup2(fd[1], 1);
        printf("Cnt_Started_at_%d\n", time(NULL));
        for (int i = 0; i <= 3; i++)
        {
            printf("Parent %d\n", i);
            sleep(1);
        }
        printf("0\n");
        printf("Cnt_Finished_at_%d\n", time(NULL));
    }
    else
    {
        // 子进程 - 由0终止
        int dp = dup2(fd[0], 0);
        printf("Trm_Started_at_%d\n", time(NULL));
        char buffer[100];
        scanf("%s", buffer);
        printf("%s\n", buffer);

        int i;
        while (scanf(" Parent %d", &i) && i >= 0) // 注意这里的修改...
            printf("Received: %d\n", i);

        scanf("%s", buffer);
        printf("%s\n", buffer);
        printf("Trm_Finished_at_%d\n", time(NULL));
    }
}

输出 --
$ ./main.out
Trm_Started_at_1578303662
Cnt_Started_at_1578303662
Received: 0
Received: 1
Received: 2
Received: 3
0
Trm_Finished_at_1578303666
英文:

The problem here is with your scanf statement. As suggested by @dash-o, change it to treat spaces.

Another problem is that, first i = 0. You need to modify your while to accomodate 0.

Since you are only evaluating i in your while loop, you won't be entering for ``i=0``` case.

Below is the modified program and the output, also, please add various checks for return values of the functions / buffer overflows as you deem right --

#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;fcntl.h&gt;

int main()
{
        int fd[2];
        int p = pipe(fd);
        int ch = fork();

        if (ch)
        {
                // Parent - Counts from 1 to 3
                int dp = dup2(fd[1], 1);
                printf(&quot;Cnt_Started_at_%d\n&quot;, time(NULL));
                for (int i = 0; i &lt;= 3; i++)
                {
                        printf(&quot;Parent %d\n&quot;, i);
                        sleep(1);
                }
                printf(&quot;0\n&quot;);
                printf(&quot;Cnt_Finished_at_%d\n&quot;, time(NULL));
        }
        else
        {
                // Child - Terminated by 0
                int dp = dup2(fd[0], 0);
                printf(&quot;Trm_Started_at_%d\n&quot;, time(NULL));
                char buffer[100];
                scanf(&quot;%s&quot;, buffer);
                printf(&quot;%s\n&quot;, buffer);

                int i;
                while (scanf(&quot; Parent %d&quot;, &amp;i) &amp;&amp; i &gt;= 0) // notice change here ...
                        printf(&quot;Recieved: %d\n&quot;, i);

                scanf(&quot;%s&quot;, buffer);
                printf(&quot;%s\n&quot;, buffer);
                printf(&quot;Trm_Finished_at_%d\n&quot;, time(NULL));
        }
}

OUTPUT --
$ ./main.out
Trm_Started_at_1578303662
Cnt_Started_at_1578303662
Recieved: 0
Recieved: 1
Recieved: 2
Recieved: 3
0
Trm_Finished_at_1578303666

huangapple
  • 本文由 发表于 2020年1月6日 15:47:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/59608412.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定