Unix Programming - multi-process programming: reading/writing a pipe

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > March 2006 > multi-process programming: reading/writing a pipe





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author multi-process programming: reading/writing a pipe
hierarchical_polymorphist

2006-03-19, 12:02 pm

Hi all,

The parent program creates two pipes, and forks a child. Then it sends
the child a string "GO", which triggers the child to send back some
string. The parent program receives the string, and again send
something to the child. It terminates the child by sending an EOF.

Here's the code:
<code>
/***** parent.c *****/
#define IN 1
#define OUT 0

int main()
{
int p1_pipes[2][2];

pipe(p1_pipes[0]);
pipe(p1_pipes[1]);

int pid1;
pid1 = fork();
switch (pid1) {
case -1:
perror("fork"); exit(EXIT_FAILURE);
case 0:
close(p1_pipes[0][OUT]);
close(p1_pipes[1][IN]);
// p1_pipes[0] is the pipe through which the parent writes to the
child
// p1_pipes[1] is the pipe through which the parent reads from
the child
dup2(p1_pipes[0][IN], fileno(stdin));
dup2(p1_pipes[0][OUT], fileno(stdout));
execl("child", "child", "", NULL);
break;
default:
close(p1_pipes[0][IN]);
close(p1_pipes[1][OUT]);
}
char in[32];
char out[32];

memset(in, 0, 32);
memset(out, 0, 32);

write(p1_pipes[0][OUT], "GO\n", strlen(in)+1);
read(p1_pipes[1][IN], out, sizeof(out));
printf("%s", out);
fflush(stdout);

write(p1_pipes[0][OUT], "1\n", 3);
write(p1_pipes[0][OUT], (char*)EOF, 1);
}
/****** end of parent.c *****/


/***** child.c *****/
int main()
{
char buf[32];
char *ptr_line = (char*) malloc(sizeof(buf));

do {
ptr_line = fgets(buf, sizeof(buf), stdin);
if (ptr_line == NULL) return 1;

ptr_line[strlen(ptr_line)-1] = '\0';
if (strncmp(ptr_line, "GO", strlen("GO")))
{
fprintf(stdout, "1", sizeof("1")); // procedure simplified to
make the point
ptr_line = fgets(buf, sizeof(buf), stdin);
// some other processings
...
}
} while (1);
}

/***** end of child.c *****/
</code>

Well...here the problem is like this:
The parent sends "GO" to the child, and the child responses perfectly
right. (in my original program I have outputted the variable values,
and they turned out correct.) The parent also gets the response from
the child.

but the second time the parent tries to send something to the child, it
turns out it is never received by the child, and the weird thing is
when I did a call to int c = write(pipe, stuff, size), and c is not 0,
which means the bytes were sent but not received by anybody?

Also, it turns out that from that point on, the parent continues
sending stuff, and closes itself before the child receives anything
further. The child actually stuck, because it's waiting for an EOF,
which the parent has sent but not received by the child..........

I've been working on this for many hours, and tried various methods,
but not a clue has come to me. I admit I'm not experienced in multi
proc programming, nor am I a C guru. Can anybody help me figure this
out? Many thanks!

Robert Harris

2006-03-19, 12:02 pm

hierarchical_polymorphist wrote:
> Hi all,
>
> The parent program creates two pipes, and forks a child. Then it sends
> the child a string "GO", which triggers the child to send back some
> string. The parent program receives the string, and again send
> something to the child. It terminates the child by sending an EOF.
>
> Here's the code:
> <code>
> /***** parent.c *****/
> #define IN 1
> #define OUT 0
>
> int main()
> {
> int p1_pipes[2][2];
>
> pipe(p1_pipes[0]);
> pipe(p1_pipes[1]);
>
> int pid1;
> pid1 = fork();
> switch (pid1) {
> case -1:
> perror("fork"); exit(EXIT_FAILURE);
> case 0:
> close(p1_pipes[0][OUT]);
> close(p1_pipes[1][IN]);
> // p1_pipes[0] is the pipe through which the parent writes to the
> child
> // p1_pipes[1] is the pipe through which the parent reads from
> the child
> dup2(p1_pipes[0][IN], fileno(stdin));
> dup2(p1_pipes[0][OUT], fileno(stdout));
> execl("child", "child", "", NULL);
> break;
> default:
> close(p1_pipes[0][IN]);
> close(p1_pipes[1][OUT]);
> }
> char in[32];
> char out[32];
>
> memset(in, 0, 32);
> memset(out, 0, 32);
>
> write(p1_pipes[0][OUT], "GO\n", strlen(in)+1);


strlen(in) is zero because in[] is filled with zeroes.

> read(p1_pipes[1][IN], out, sizeof(out));
> printf("%s", out);
> fflush(stdout);
>
> write(p1_pipes[0][OUT], "1\n", 3);
> write(p1_pipes[0][OUT], (char*)EOF, 1);


EOF isn't a character. If you want to stop writing to a pipe (or file),
call close().

> }
> /****** end of parent.c *****/
>
>
> /***** child.c *****/
> int main()
> {
> char buf[32];
> char *ptr_line = (char*) malloc(sizeof(buf));
>
> do {
> ptr_line = fgets(buf, sizeof(buf), stdin);


fgets() is buffered. Call read() instead if you want to retrieve
whatever has been written to the pipe.

Robert

> if (ptr_line == NULL) return 1;
>
> ptr_line[strlen(ptr_line)-1] = '\0';
> if (strncmp(ptr_line, "GO", strlen("GO")))
> {
> fprintf(stdout, "1", sizeof("1")); // procedure simplified to
> make the point
> ptr_line = fgets(buf, sizeof(buf), stdin);
> // some other processings
> ...
> }
> } while (1);
> }
>
> /***** end of child.c *****/
> </code>
>
> Well...here the problem is like this:
> The parent sends "GO" to the child, and the child responses perfectly
> right. (in my original program I have outputted the variable values,
> and they turned out correct.) The parent also gets the response from
> the child.
>
> but the second time the parent tries to send something to the child, it
> turns out it is never received by the child, and the weird thing is
> when I did a call to int c = write(pipe, stuff, size), and c is not 0,
> which means the bytes were sent but not received by anybody?
>
> Also, it turns out that from that point on, the parent continues
> sending stuff, and closes itself before the child receives anything
> further. The child actually stuck, because it's waiting for an EOF,
> which the parent has sent but not received by the child..........
>
> I've been working on this for many hours, and tried various methods,
> but not a clue has come to me. I admit I'm not experienced in multi
> proc programming, nor am I a C guru. Can anybody help me figure this
> out? Many thanks!
>

Alex Fraser

2006-03-19, 12:02 pm

"hierarchical_polymorphist" <hierarchicalpolymorphist@gmail.com> wrote in
message news:1142689394.901022.88010@p10g2000cwp.googlegroups.com...
> The parent program creates two pipes, and forks a child. Then it sends
> the child a string "GO", which triggers the child to send back some
> string. The parent program receives the string, and again send
> something to the child. It terminates the child by sending an EOF.
>
> Here's the code:
> <code>
> /***** parent.c *****/
> #define IN 1
> #define OUT 0
>
> int main()


Style point: prefer being explicit, write int main(void).

> {
> int p1_pipes[2][2];
> pipe(p1_pipes[0]);
> pipe(p1_pipes[1]);
>
> int pid1;
> pid1 = fork();
> switch (pid1) {
> case -1:
> perror("fork"); exit(EXIT_FAILURE);


You check that fork() succeeds, but nothing else as far as I can see.

> case 0:
> close(p1_pipes[0][OUT]);
> close(p1_pipes[1][IN]);
> // p1_pipes[0] is the pipe through which the parent writes to the
> child
> // p1_pipes[1] is the pipe through which the parent reads from
> the child


Style point: avoid '//' comments in code posted to Usenet, or ensure they
don't get wrapped.

> dup2(p1_pipes[0][IN], fileno(stdin));
> dup2(p1_pipes[0][OUT], fileno(stdout));


You closed p1_pipes[0][OUT] above; you probably meant p1_pipes[1][OUT]. In
this case it makes no difference, but in general you need to close the old
descriptors.

> execl("child", "child", "", NULL);
> break;
> default:
> close(p1_pipes[0][IN]);
> close(p1_pipes[1][OUT]);
> }
> char in[32];
> char out[32];
>
> memset(in, 0, 32);
> memset(out, 0, 32);


Zeroing memory that you are going to write to is generally just a waste of
time.

> write(p1_pipes[0][OUT], "GO\n", strlen(in)+1);


Since in[0] == 0 due to the memset(), strlen(in)+1 is 1. So this write
requests 1 byte.

> read(p1_pipes[1][IN], out, sizeof(out));


The return value from read() is valuable information.

> printf("%s", out);


Could cause printf() to access beyond the end of the array, since you do not
guarantee that it is null-terminated.

> fflush(stdout);
>
> write(p1_pipes[0][OUT], "1\n", 3);


This writes a 0 byte. Did you really want to do that?

> write(p1_pipes[0][OUT], (char*)EOF, 1);


"(char*)EOF" is just total nonsense. You can't write EOF; it is not a
character, it is an event.

You may want to wait() for the child.

Non-void functions should have a return statement.

> }
> /****** end of parent.c *****/
>
> /***** child.c *****/
> int main()
> {
> char buf[32];
> char *ptr_line = (char*) malloc(sizeof(buf));


Do not cast the return value of malloc(). If you had not cast it, you might
have realised your failure to #include <stdlib.h>, which is a serious error.

In this case, the malloc() call itself is entirely unnecessary, and in
fact...

> do {
> ptr_line = fgets(buf, sizeof(buf), stdin);


....causes a memory leak: the only reference to bytes allocated by the
malloc() above is overwritten.

> if (ptr_line == NULL) return 1;
>
> ptr_line[strlen(ptr_line)-1] = '\0';


Use strchr() to look for the '\n', and overwrite it if found. If no '\n' is
found, you know the line is longer than the buffer.

> if (strncmp(ptr_line, "GO", strlen("GO")))
> {
> fprintf(stdout, "1", sizeof("1")); // procedure simplified to
> make the point


Spurious (but harmless) third argument to fprintf(). Use fflush() to ensure
the stdio buffer is written to the pipe.

> ptr_line = fgets(buf, sizeof(buf), stdin);
> // some other processings
> ...
> }
> } while (1);
> }
>
> /***** end of child.c *****/
> </code>


Fix the issues above, then post complete, compilable code that demonstrates
the problem.

Alex


Lew Pitcher

2006-03-19, 12:02 pm

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Alex Fraser wrote:
> "hierarchical_polymorphist" <hierarchicalpolymorphist@gmail.com> wrote in
> message news:1142689394.901022.88010@p10g2000cwp.googlegroups.com...
[snip][vbcol=seagreen]
>
> "(char*)EOF" is just total nonsense. You can't write EOF; it is not a
> character, it is an event.


To the OP:

More to the point, the (char *) cast forces the results to be interpreted as
an address. write() will attempt to use this address as the source of the data
that it will write out.

Since EOF, interpreted as an address, is likely to point to someplace outside
of the address space of this process, this write() is likely to result in a
SIGSEGV as it tries to access the (char *)EOF buffer.

Additionally, if the code survives the SIGSEGV, the written data will not be
an EOF. It will be whatever was in memory in the buffer pointed to by
(char *)EOF.

Finally, as Alex points out, EOF is an input event, not an output datum. To
terminate the data in the file, you simply close() the file descriptor. The
reader of this file will run out of data at this point (because you closed the
file), and receive the /event notification/ of EOF ("End Of File").

Think of it this way: You have a very large ball of string, and you unwind a
length of string for me. How do I (as the "reader" of the string) know when I
have reached the end of my piece of string? I know that I have reached the end
when I find the physical end of the string, and there is no more. How do you
(as the "writer" of the string) signal that there is no more string for the
"reader"? You cut the string off. That's what close() does; it cuts off the
file that you are writing so that there is no more file after that point.

- --
Lew Pitcher

Master Codewright & JOAT-in-training | GPG public key available on request
Registered Linux User #112576 (http://counter.li.org/)
Slackware - Because I know what I'm doing.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)

iD8DBQFEHCYhagVFX4UWr64RAvZvAJ9glsRg+vJ7
X+YkuVVRokMfsqYkegCaAyod
5klnyCROdK36NT9A2hBSl4Q=
=UaPf
-----END PGP SIGNATURE-----
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com