[comp.unix.questions] Need help with interprocess communications

lanzo@mercutio.steinmetz (Mark Lanzo) (12/07/87)

    [This was originally posted to comp.unix, where it seems to have
    fallen into a black hole, so I'll try my luck here...]

I'm working on an application where I have a process fork off a 2nd process
and maintain communications with the subprocess via a pipe (or more accurately,
via 2 pipes--one for parent-to-child writes, the other for child-to-parent).

The problem that I am running into is that I need to be able to detect when 
the child process writes to the pipe; and I need the I/O to be as unbuffered
as possible.  I need the ability to detect writes asynchronously as well as
synchronously.

I have tried using "fcntl(fd,F_SETFL,FASYNC)" as well as setting up an
interrupt handler to handle SIGIO signals (via "sigvec(2)"), and this works
fine when I'm reading from the terminal, but does not seem to work at all
when I try it from a pipe.

My intent was for an interrupt routine to be triggered whenever the child
process wrote into the pipe; but I never get a SIGIO signal when data is
written into the pipe.

I can manually examine the pipe to see if any data is waiting to be read (by
the master process) using "ioctl(FIONREAD)", but even this does not work
reliably since I don't detect that anything was written until the child 
process flushes its end of the pipe.  I need to know when it writes something
to the pipe, not when the buffer is full (4K bytes?).

One major constraint is that I don't necessarily have access to the source
code of the child process--the code I'm working on (the master process) has
to work with any existing program out there.

For reference, my basic setup looks like:

     call "pipe" to open a pipe which is for writing from parent-to-child
     call "pipe" to open a pipe for writing from child-to-parent
     use fcntl to set FASYNC mode on all descriptors.
     fork process:

	child				 
        -------                          
	disconnect unused ends of pipes:
	    close parent-to-child write end
	    close child-to-parent read	end
	use close/dup etc to make pipe ends be stdin & stdout.
	set unbuffered I/O mode on stdin, stdout, stderr
	exec subprocess...

    
        parent                            
	----------			   
	disconnect unused ends of pipes:
	    close child-to-parent write end 
	    close parent-to-child read end
	set up signal handler for SIGIO interrupts.
	sit in loop waiting for signals (sigpause(2))--
	    SIGIO handler does ioctl(FIONREAD) to check pipe, then reads data.

Well, the SIGIO handler works fine to detect input from places like stdin, but
never sees anything coming down the pipe.  When it gets invoked (generally
by me banging on the <RETURN> key causing an interrupt from stdin), it 
does find that there is data available in the pipe (as well as stdin) and
has no problem reading it.


Does anyone out there know how I can fix this problem?

Also:  Is there a way that I can determine WHICH file descriptor caused
a SIGIO interrupt to be invoked, or by which I can set up a different
interrupt handler for each descriptor?


Oh yeah, almost forgot:
    I'm using Ultrix 1.2 (going to 2.0 soon), but I'm interesting in solutions
which are as portable as possible between various flavors of Unix.

	    Thanks in advance,

		    Mark Lanzo
                    mercutio!lanzo



P.s.  I'm also interested in solutions using the pseudoterminal
mechanism or sockets.  I tried using pseudoterminals, but had no more
luck than I have had with pipes (in fact, I got almost the same results).
Pseudoterminals would seem to suffer from the problem that I must have
a 'device' (special file) set up ahead of time, ie., I need to create
the files in /dev/.  Since my application may eventually be running MANY
subprocesses simultaneously, I don't want to have to tie up all the
pseudoterminals, especially since on systems around here the ptys exist
primarily for the sake of remote logins (as best as I can figure it).

Sockets unfortunately I don't know enough about to do anything useful,
and what docs I could find didn't help much.

chris@mimsy.UUCP (Chris Torek) (12/08/87)

In article <8117@steinmetz.steinmetz.UUCP> lanzo@mercutio.steinmetz
(Mark Lanzo) writes:
>     call "pipe" to open a pipe which is for writing from parent-to-child
>     call "pipe" to open a pipe for writing from child-to-parent

(Using a Unix domain stream socket is more efficient, since one socket
can be used bidirectionally.  Not that this matters...)

>     use fcntl to set FASYNC mode on all descriptors.

(Remember to F_SETOWN it to get the signals sent to the right process
or pgroup.)

>     fork process:
>
>	child				 
>        -------                          
>	disconnect unused ends of pipes:
>	    close parent-to-child write end
>	    close child-to-parent read	end
>	use close/dup etc to make pipe ends be stdin & stdout.

Good so far.

>	set unbuffered I/O mode on stdin, stdout, stderr

This does nothing, because...

>	exec subprocess...

this wipes out the stdio structures you just crafted.  Stdio is
part of the user process, folks, not the kernel; it is not retained
across exec!  There is no way, short of changing the code for the
child, to force child programs to run unbuffered.

>Also:  Is there a way that I can determine WHICH file descriptor caused
>a SIGIO interrupt to be invoked, or by which I can set up a different
>interrupt handler for each descriptor?

No, but rather than using ioctl(FIONREAD), you can use select, which
will tell you everything at once.  In fact, you can then stop using
SIGIO, as long as you are always waiting for input.

>P.s.  I'm also interested in solutions using the pseudoterminal
>mechanism or sockets.

pty's will make child programs think they are talking to terminals
(because they *are*), which makes them behave `interactively'.  Sockets
are the same as pipes, since pipes are in fact just sockets that have
been shut down (see `man 2 shutdown').
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

erikn@sics.se (Erik Nordmark) (12/10/87)

[[ I tried sending this as mail using different addresses, but failed! ]]

In article <8117@steinmetz.steinmetz.UUCP> you write:
>
>
>I have tried using "fcntl(fd,F_SETFL,FASYNC)" as well as setting up an
>interrupt handler to handle SIGIO signals (via "sigvec(2)"), and this works
>fine when I'm reading from the terminal, but does not seem to work at all
>when I try it from a pipe.
>
>
>Well, the SIGIO handler works fine to detect input from places like stdin, but
>never sees anything coming down the pipe.  When it gets invoked (generally
>by me banging on the <RETURN> key causing an interrupt from stdin), it 
>does find that there is data available in the pipe (as well as stdin) and
>has no problem reading it.
>
>
>Does anyone out there know how I can fix this problem?
>

>From looking at the BSD4.3 sources I found out the following:
When a tty is opened the associated process group is set to
that of the creator. The signals that the tty driver generate (e.g. caused
by ^C) are sent to this process group.

However, for sockets (a pipe is implemented as a pair of sockets in BSD4.3
and maybe elsewhere!) the associated process group is not set automatically.

So what you have to do is to set it before you can get ant SIGIO's! Use
   int pgrp = getpid();
   if (fcntl(fd, F_SETOWN, pgrp) == -1) {
	perror("fnctl");
	exit(1);
   }
or
       ioctl(fd, SIOCSPGRP, &pgrp)	/* note: & */

I think this should work even if pipes aren't implemented as a pair of
sockets, but I haven't tried any of it.

>Also:  Is there a way that I can determine WHICH file descriptor caused
>a SIGIO interrupt to be invoked, or by which I can set up a different
>interrupt handler for each descriptor?
>

See select(2). (Just a detail: select will tell you that there is data
to read if there actually is data to read or if the other end(s) have
closed the pipe. In the latter case read() will return an EOF - this
stuff caused me some trouble before I read the *real* documentation -
the OS source code!!)

-------------------------------------------------------------------------
Erik Nordmark
Swedish Institute of Computer Science, Box 1263, S-163 13  SPANGA, Sweden
Phone: +46 8 750 79 70	Ttx: 812 61 54 SICS S	Fax: +46 8 751 72 30

uucp:	erikn@sics.UUCP or {seismo,mcvax}!enea!sics!erikn
Domain: erikn@sics.se
-------------------------------------------------------------------------