有vs没有C pipe()函数-what';是什么导致了这种行为?
我编写了一个简单的脚本(取自教程),它将数据写入子进程中管道的一端,并从父进程中管道的另一端读取数据:有vs没有C pipe()函数-what';是什么导致了这种行为?,c,pipe,C,Pipe,我编写了一个简单的脚本(取自教程),它将数据写入子进程中管道的一端,并从父进程中管道的另一端读取数据: #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> int main() { pid_t pid; int mypipefd[2]; int ret; char buf[20]; ret = pipe(mypipefd
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid;
int mypipefd[2];
int ret;
char buf[20];
ret = pipe(mypipefd);
if (ret == -1) {
printf("Pipe failed.\n");
exit(1);
}
if ((pid = fork()) == -1) {
printf("Fork failed.\n");
exit(1);
} else if (pid == 0) {
printf("Child process.\n");
char msg[] = "Hello there!";
write(mypipefd[1], msg, strlen(msg) + 1);
} else {
printf("Parent process.\n");
read(mypipefd[0], buf, 15);
printf("Buf: %s\n", buf);
}
return 0;
}
当我对代码越来越熟悉时,我想知道为什么我们需要使用mypipefd[2]
和pipe()
来实现这个目标,或者mypipefd[1]
本身是否有效。因此,我使用以下代码进行了尝试:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
pid_t pid;
int my_array[1];
char buf[20];
if ((pid = fork()) == -1) {
printf("Fork failed.\n");
exit(1);
} else if (pid == 0) {
printf("Child process.\n");
char msg[] = "Hello there!\n";
write(my_array[0], msg, strlen(msg) + 1);
} else {
// wait(NULL);
printf("Parent process.\n");
read(my_array[0], buf, 15);
printf("Buf: %s\n", buf);
}
return 0;
}
这次没有提示。我甚至尝试取消对wait(NULL)
调用的注释,因为根本原因很可能是父进程和子进程之间的冲突。没有这样的运气
这是怎么回事?为什么在没有程序挂起的情况下,我无法以这种方式读写一个数组的长度?编译器到底卡在什么地方?计算机和现实生活中的管道都有两端。就像现实生活中的管道一样,数据从管道的一端(写端)流向另一端(读端) 该函数通过将这两个端点写入两个文件描述符的数组来提供这两个端点。该对的第一个元素是只读的,第二个元素是只读的。pipe()函数接受一个2整数数组作为输入参数
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
#包括
内部管道(内部管道[2]);
#定义源代码/*参见功能测试宏(7)*/
#包括/*获取O_*常数定义*/
#包括
int-pipe2(int-pipefd[2],int标志);
然后,它生成一个新的管道对象,并使用文件描述符初始化pipefd数组以进行读写操作
您尝试使用一些任意的、未初始化的int(或文件描述符)调用read()和write()。这意味着操作系统没有分配管道对象,也没有为您提供与read()和write()一起使用的文件描述符(管道的API)
这(使用未初始化的文件描述符调用read()或write())将导致“未定义的行为”
“我发现“未定义行为”的一个很好的工作定义是“为我工作,为你工作,在开发和QA期间工作,但在你最重要的客户面前爆炸”——Scott Meyers
当然可以,但用一个类似的比喻,为什么不能将一个数组的长度用作“bucket”,用子进程填充它(向它写入),然后用父进程清空它(从中读取)?数组不仅仅包含一个简单的无意义整数。这个int是一个文件描述符。操作系统维护一个文件描述符的llist,并将它们与对象关联(本例中为管道)。您使用错误的fd进行读取或写入,但行为未定义。@RichieThomas可能是,但它不会是管道…;)@某个程序员,我想我过分强调了我对
pipe()
的兴趣。我编辑了问题标题以澄清我的意图。我的目标是找出为什么第二个示例(没有pipe()
)挂起,我想我已经完成了。此外,我不确定管道的实际用例是什么,假设第二个示例可以工作(可能使用不同的系统调用或数据结构)。管道为我们提供了第二个示例的工作版本所没有的东西?或者这样的版本不可能?换句话说,如果(我知道这是一个很大的如果)我可以用一个“bucket”的长度和两个“pipe”的长度获得相同的结果,那么这个管道有什么好处呢?用其他参数类型调用pipe()(在你的例子中是int-array[1])会导致未定义的行为,…
但是在第二个代码示例中,我没有使用数组[1]调用pipe()。事实上,在那个示例中,我根本没有调用pipe()。这就是示例2的要点-我试图避免使用该函数调用。很抱歉,我错过了。因此,您根本没有创建管道!对未初始化的文件描述符使用写和读函数。这仍然是“未定义的行为”。您能否更具体地说明未定义的行为是如何导致挂起过程的?根据脚本的输出,我们知道写/读操作正在成功,因为buf
(源自mypipefd
)的内容被记录到示例2中的屏幕上。因此,我认为该流程唯一要做的就是终止。该功能只有在偶然的情况下才能正常工作;可能会发生一些其他的副作用,比如进程TCB被破坏,导致操作系统无法终止进程。我喜欢这句话。我通常用“你的老板、你老板的老板和你老板的老板的老板”作为听众。错误消息应该打印到stderr
(标准错误),而不是stdout
。使用fprintf(stderr,
代替printf(
)。您确实应该检查像read()
和write()这样的函数的返回值
…@Shawn如果通过检查返回值,您的意思是确保由于SIGINT或某些错误而未返回-1
,则根据StackOverflow指南[1],上述代码片段是演示我看到的错误的最小可验证示例。添加错误处理无助于说明我的观点。[1]错误检查(希望;对于未定义的行为(如使用未初始化的值)总是很难确定)表明问题——使用EBADF
,除非你运气不好。在我的测试系统上,my_array[0]就是一个不走运的例子
最终被初始化为0,这是用于标准输入的描述符。显然,写入标准输入在我的系统中起作用(尽管我从您那里得到不同的输出)但是,对于具有不同操作系统的用户来说可能不是这样。在这种情况下,父进程正在阻止等待用户键入某个内容并单击enter以便可以读取该内容(如果您这样做,则会出现更多未定义的行为,因为您尝试打印的字符数组并不总是以0结尾,更不用说在正确的位置了)
Parent process.
Child process.
Buf: Hello there!
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);