我有以下代码:
switch(fork())
{
case -1:
/*error case*/
error = errno;
printf("fork error(1): %s\n", strerror(error));
break;
case 0: //child case: execute remove_non_alpha and send result to pfd write end
remove_non_alpha(arg_str);
res = write(pfd[1], arg_str, arg_str_size);
if(res != arg_str_size)
{
return -1;
}
char *arg_str2 = NULL;
res = read(pfd[0], &arg_str2, 1); //hang happens right here
break;
default:
/*parent case-- fall through*/
break;
}
pfd是使用pipe()创建的。 arg_str是一个非空的char *字符串,arg_str_size是一个等于arg_str大小的int。我将read()调用添加为调试语句,以确保我的数据已成功写入管道。但是,当我调用它时,呼叫会无限期挂起。有人可以帮我解释一下我在这里做错了什么吗?
你需要为read()
分配一些内存。现在你正在尝试将read()
放入指针,而不是读入指针所指向的某些内存。
话虽这么说,虽然你正在做的事情不会起作用,但它不应该导致read()
阻止。你只是尝试read()
1个字节,并且指针是有效的内存,即使它像你正在做的那样对read()
没有意义。你没有展示很多代码,所以你的问题很可能就在其他地方。原则上,你正在做的事情应该是这样的:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char mystring1[] = "Some output";
char mystring2[100] = {0};
int pfd[2];
if ( pipe(pfd) == -1 ) {
perror("error calling pipe");
return EXIT_FAILURE;
}
if ( write(pfd[1], mystring1, sizeof mystring1) == -1 ) {
perror("error calling write()");
return EXIT_FAILURE;
}
if ( read(pfd[0], mystring2, 100) == -1 ) {
perror("error calling read()");
return EXIT_FAILURE;
}
printf("Read '%s' from pipe.\n", mystring2);
if ( close(pfd[0]) == -1 || close(pfd[1]) == -1 ) {
perror("error calling close");
return EXIT_FAILURE;
}
return 0;
}
哪个输出:
paul@thoth:~/src/sandbox$ ./sillypipe
Read 'Some output' from pipe.
paul@thoth:~/src/sandbox$
你最好的策略是简化你的程序,直到遇到出错的事情。例如,目前尚不清楚你是如何证明read()
确实是导致你的程序挂起的原因,而不是它后来做的事情,并且你没有显示你的父进程可能正在对该管道做什么。
只需稍加努力,就可以制作一个独立的示例,该示例适用于通常无用的情况,即只在其中一个进程中读取和写入管道。
我说没用,因为拥有管道通常是在不同进程之间进行通信。
除了微小的差异,它与上面的@Paul Griffiths类似。
working.c
#include <stdio.h>
#include <stdlib.h>
int go(){
int pfd[2];
const char *arg_str = "Can you read it now\n";
int arg_str_size = 20;
int res;
char arg_str2[30];
if (pipe (pfd))
{
fprintf (stderr, "Pipe failed.\n");
return -999;
}
switch(fork())
{
case -1:
fprintf(stderr, "fork error(1) \n");
break;
case 0: //child case: send result to pfd write end
res = write(pfd[1], arg_str, arg_str_size);
if(res != arg_str_size)
{
return -1;
}
res = read(pfd[0], &arg_str2, arg_str_size); //won't hang because unread by parent
fprintf(stderr, "read: %s \n", arg_str2);
break;
default:
/*parent case-- fall through*/
break;
}
return 0;
}
void main(){
int x = go();
}
请注意,为char arg_str2[30];
分配了一些空间。相反,如在OP代码中,代码使用char *arg_str2 = NULL;
这是未定义的行为,并且可能导致错误信号,如SIGSEGV(分段违例)等。
不要执行以下操作:如果父和子从同一管道读取,则会阻止。
如果您希望父和子都能够读写,那么您需要两个pipe()调用。如果您尝试在代码中进行测试以确保正确性,请仅测试读取和写入时的错误指示器,不要通过从管道读回刚刚写入的内容进行测试。管道是一种特殊的文件,而不是普通的磁盘文件。
写入管道的数据被写入先进先出(FIFO)队列,一旦读取就被删除。读取空管将阻塞(等待),这看起来像挂起。
我们可以在这里看到这种行为。两个进程读取管道(代码**可以但不应该这样做),一个进程将读取管道的内容,但另一个进程将挂起。
hang.c
#include <stdio.h>
#include <stdlib.h>
int go(){
int pfd[2];
const char *arg_str = "Can you read it now\n";
int arg_str_size = 20;
int res;
char arg_str2[30];
if (pipe (pfd))
{
fprintf (stderr, "Pipe failed.\n");
return -999;
}
switch(fork())
{
case -1:
fprintf(stderr, "fork error(1) \n");
break;
case 0: //child case: send result to pfd write end
res = write(pfd[1], arg_str, arg_str_size);
if(res != arg_str_size)
{
return -1;
}
res = read(pfd[0], &arg_str2, arg_str_size);
fprintf(stderr, "child read: %s \n", arg_str2);
break;
default:
/*parent case-- try to read here as well (not a good idea) */
res = read(pfd[0], &arg_str2, arg_str_size);
fprintf(stderr, "parent read: %s \n", arg_str2);
break;
}
return 0;
}
void main(){
int x = go();
}
要让孩子阻止而不是父母,请添加一些sleep()
调用,以便孩子读取第二个,以便父母在阅读后留下来。
最后注意:正如您可能想象的那样,从示例中填写其余的C代码需要花费时间和延迟来获得答案。通常情况下,如果您遇到构建良好测试用例的麻烦,或“最低完全可验证示例”MCVE,您将能够回答您自己的问题!
你正在把你正在做的事情搞砸到首先阅读然后写作。如果你将fprintf放在fscanf之前至少一个进程,它将继续
#include <stdio.h>
#include <unistd.h>
int main() {
int child_to_parent[2];
int parent_to_child[2];
pipe(child_to_parent);
pipe(parent_to_child);
pid_t id = fork();
if (id == 0) {
close(parent_to_child[1]);
close(child_to_parent[0]);
FILE* out = fdopen(child_to_parent[1], "w");
FILE* in = fdopen(parent_to_child[0], "r");
char msg[7];
fprintf(out, "hi\n");
fflush(out);
fscanf(in ,"%s", msg);
printf("Child got: %s\n", msg);
printf("Child sent: hi\n");
} else {
close(parent_to_child[0]);
close(child_to_parent[1]);
FILE* in = fdopen(child_to_parent[0], "r");
FILE* out = fdopen(parent_to_child[1], "w");
fprintf(out, "hello");
fflush(out);
printf("Parent sent: hello\n");
char msg[4];
fscanf(in, "%s", msg);
printf("Parent got: %s\n", msg);
}
另外不要忘记将\ n添加到fprintf,因为fscanf将等待换行符。正如你所看到的缓冲提到冲洗会有所帮助,