[comp.unix.questions] fork

aviz@oahu.cs.ucla.edu (03/06/89)

What are the syntaxes for fork() and pipe()?

I am trying to write a C program that sets up a set of child
processes.. 

Thanks in advance...

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/06/89)

In article <21327@shemp.CS.UCLA.EDU> aviz@CS.UCLA.EDU (Dr Algirdas Avizienis) writes:
>What are the syntaxes for fork() and pipe()?
>I am trying to write a C program that sets up a set of child
>processes.. 

Excuse me for saying this, but if you have to ask the net for this
information because you don't have a reference manual, you'll have
SERIOUS problems getting your code right.  By far the preferable
way to answer such questions is to refer to the UNIX Programmer's
Reference Manual.

byrum@cs.odu.edu (Terry Franklin Byrum) (03/07/89)

I am borrowing this account; see signature below.

Although the obvious solution is to refer to the relevant man pages, I will
concede that there are a few commands (especially in SYSV) for which the
novice can stare at the man pages a long time and still say, "That's nice,
but HOW DO YOU USE IT??".  I would include fork() and pipe() among these.

Below, I present the nucleus of a program which uses fork() and pipe().
This program is intended to highlight fork() and pipe(), so error checking is
not rigorous, and signals and abnormal child exit()s are not considered.

For a pipe to work properly, each process should close() the end of the pipe
that it does not intend to use (the reader should close the writing end,
and vice-versa).  For example, if the reader does not close the writing end,
it will never perceive EOF from the pipe, since the operating system knows
that there is one potential writer (the reading process itself) still active.

An additional word to the wise: any buffering of I/O, especially by a
writing process, will drive you crazy in interprocess communication.
The reading process cannot perceive anything until it is flushed out to the
operating system.  If I/O is to a terminal device, output is line-buffered
by default, which is usually acceptable.  But pipes are not terminals, so
full buffering is the default, and nothing gets to the OS or the reader
until 512 (or whatever) characters have been written; rarely acceptable.
If only low level calls (read, write) are used, there is no problem.
However, if you want high level calls (fprintf, putc, puts), it is well
either to do an fflush(stream) after every batch of output, or do a
setbuf(stream, (char *)0) at the start to render the stream fully unbuffered.


                                    Lloyd Kremer
                                    Brooks Financial Systems
                                    ...!xanth!brooks!lloyd
                                    Have terminal ... will hack!

-------------------cut here----------------------------------------------
/*
 * A sample program to demonstrate the basic syntax of fork() and pipe()
 * commands.  The parent process simply writes a string through a pipe
 * to be received by a child process (octal dump)
 */

main()
{
    extern void perror();
    int pfd[2];  /* this will be an array of 2 pipe file descriptors */

    if(pipe(pfd) == -1){  /* pfd[0] is read end; pfd[1] is write end */
        perror("cannot create pipe");
        return(1);
    }
    if(!fork()){
        /* CHILD STARTS HERE (fork() returns 0) */
        close(pfd[1]);  /* child closes write end of pipe */
        close(0);  /* child closes its inherited stdin descriptor */
        dup(pfd[0]);  /* replace it with a dup() of read end of pipe */
        close(pfd[0]);  /* then close original read end of pipe */
        execl("/bin/od", "od", "-c", (char *)0);  /* child becomes "od -c" */
        perror("/bin/od");  /* if we still exist here, something's wrong */
        return(1);
    }
    /* PARENT CONTINUES HERE (fork() returns child's pid) */
    close(pfd[0]);  /* parent closes read end of pipe */
    write(pfd[1], "The quick brown fox jumps over the lazy dog.", 44);
        /* parent says what it has to say into write end of pipe */
    close(pfd[1]);  /* and closes it; child will read EOF on stdin and die */
    wait((int *)0);  /* parent awaits death of child; avoids zombie process */
    return(0);
}
-------------------cut here----------------------------------------------

-- 

	Inventions have long since reached their limit --
	and I see no hope for further developments.
		- Julius Frontinus, world famous engineer, Rome, 10 AD

                                |  Terry Franklin (Frank) Byrum
                                |  BROOKS Financial Systems, Inc.
   ___                          |  INET:  byrum@cs.odu.edu
  /__  _.  __. _ _  /_          |  UUCP:  ...!sun!xanth!brooks!byrum 
 /   _/ \_(_/|_|\|_/ \          |  DISCLAIMER: No one listens anyway! 

ram@lcuxlm.ATT.COM (Miani Rich) (03/09/89)

In article <21327@shemp.CS.UCLA.EDU>, aviz@oahu.cs.ucla.edu writes:
> What are the syntaxes for fork() and pipe()?
> 

RTFM.

Pabbisetty.henr@xerox.com (Nagesh Pabbisetty) (03/13/89)

> sockets would replace the pipe, not fork and exec

Sorry about the wrong phrasing of the question. I want to know the pros and
cons of using pipes over sockets for interprocess communication.

Thanks to jvc!jonathan@uunet.UU.NET and wen-king@csvax.caltech.edu for
setting me straight.


Nagesh
716-427-1827 / 5458

andrewsr@acdc.rutgers.edu (Richard L Andrews) (02/16/90)

Why does the fork command start the child process at the point where
it occurs in the program normally, yet restart the child process when
the output is redireced to a file?  (I am using the tsch shell on UNIX).

Example:
--------
Code:
-----
#include <stdio.h>

main(

     printf("This is a test.\n");
     printf("Statement 2 in main.\n");
     if (fork() == 0) {
          printf("Child statement 1\n");
          printf("Child statement 2\n");
     } else {
          printf("Parent statement 3\n");
          printf("Parent statement 4\n");
     }
}

when output to terminal:
------------------------
This is a test.
Statement 2 in main.
Parent statement 3
Parent statement 4
Child statement 1
Child statement 2

When piped to a file, the file reads:
-------------------------------------
This is a test.
Statement 2 in main.
Parent statement 3
Parent statement 4
This is a test.
Statement 2 in main.
Child statement 1
Child statement 2


When displayed on the terminal, although the output can be
undetermined, it never prints "This is a test." twice.

Much thanks in advance.
-Rich
-- 
  //   Richard L Andrews II   |"Like any good philosophical discussion, the
\X/ andrewsr@topaz.rutgers.edu| conclusion is left unresolved." -McLaughlin

gwyn@smoke.BRL.MIL (Doug Gwyn) (02/16/90)

In article <Feb.15.14.14.27.1990.4568@acdc.rutgers.edu> andrewsr@acdc.rutgers.edu (Richard L Andrews) writes:
>Why does the fork command start the child process at the point where
>it occurs in the program normally, yet restart the child process when
>the output is redireced to a file?

First, it's not a fork command, it's a fork system call.  Commands are
things you feed to a shell.

Fork does not do what you say it does.  It causes the process to clone
itself and resume executing immediately after the system call in both
processes, the main discernable difference being that the system call
appears to have returned different values in the different processes.
This is unaffected by what the original process's standard output
happens to be.

>Example:
>#include <stdio.h>

Well, there is your problem.  Your printf()s are getting buffered in
your process data space (in effect inside the C library), and that
buffer is part of what is cloned and therefore inherited by both
processes after the fork.  The buffer is automatically flushed by the
stdio package when outputting a new-line to a terminal device, but is
flushed only when the buffer fills up when outputting to a regular
file.  We discussed all this on a couple of recent occasions.  The
solution is to explicitly unvoke fflush() to flush the buffer before
the fork.

brnstnd@stealth.acf.nyu.edu (02/16/90)

When you use printf, stdio buffers your output and sends it out in big
chunks. In your case you print into the buffer, then fork. Now both the
parent and the child have the same buffer, so the buffer ends up being
printed twice.

On the other hand, if stdout is a terminal, stdio sends the buffer out
as soon as you finish a line. So the lines are printed immediately. When
you fork, the buffer is empty, so nothing is printed twice.

In general, you should flush output buffers before forking, unless you
know the child is going to exec.

Read the setbuf() man page for more information on buffering.

---Dan

chris@mimsy.umd.edu (Chris Torek) (02/16/90)

In article <Feb.15.14.14.27.1990.4568@acdc.rutgers.edu>
andrewsr@acdc.rutgers.edu (Richard L Andrews) writes:
>Why does the fork command start the child process at the point where
>it occurs in the program normally, yet restart the child process when
>the output is redireced to a file?

It does not.

Fork() makes a copy of the program that calls fork().  In all but one
respect (the return value), the copy is identical to the original.

Now, what happens when the original includes a bunch of stuff marked
`write this to stdout sometime soon'?  Surprise, two copies of that
`stuff' get written.  Well, characters printed to stdout go in a buffer
and wait around until a `good' time to get written, so they get copied.
On a `tty' device, every newline is considered a `good' time to write,
so that a program that forks immediately after printing a newline has
no stuff marked `write this soon'; but on any other output file, newlines
are not considered `good' times to write, so that a program that forks
immediately after printing a newline typically prints more than one
newline.

(This is yet another example of how line-buffered output is a mistake.)

In other words, printf() does not mean `print this': it means `remember
to print this eventually'.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris