Difference between revisions of "CPGuide - Pipes"

From EDM2
Jump to: navigation, search
(Created page with " Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Pipes are one of three forms of interprocess c...")
 
(One intermediate revision by one user not shown)
Line 1: Line 1:
 +
{{IBM-Reprint}}
 +
{{CPGuide}}
  
 
Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Pipes are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and queues.  
 
Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Pipes are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and queues.  
Line 13: Line 15:
  
 
==About Pipes==
 
==About Pipes==
 +
A pipe is a named or unnamed buffer used to pass data between processes. A process writes to or reads from a pipe as if the pipe were standard input or standard output. A parent process can use pipes to control the input that a child process receives and to receive the output that the child process produces.
 +
 +
There are two types of pipes-named and unnamed.
 
===Unnamed Pipes===
 
===Unnamed Pipes===
 +
An unnamed pipe is a circular buffer in memory. The buffer has in and out pointers that are maintained by the system.
 +
 +
An unnamed pipe can transfer information only between related processes. A child process started by a parent process with DosExecPgm inherits the handles to any unnamed pipes created by its parent. This inheritance enables the parent process and the child process to use the unnamed pipe to communicate with one another. This type of pipe is typically used to redirect the standard input and standard output of a child process.
 +
 +
To do this, a process opens a pipe and duplicates the read and write handles of the pipe as the standard input and standard output files for the child process. Once the handles are duplicated, the parent process can use DosExecPgm to start the child process. When the child process reads and writes to its standard input and standard output handles, it is reading and writing to the pipe. The parent process can also communicate with the child process through the pipe.
 +
 +
Using an unnamed pipe, a text editor could run another program, such as a compiler or assembler, and display the output of the compiler or assembler within the editor.
 +
 +
DosCreatePipe creates an unnamed pipe. This function returns two file handles for the pipe, one for writing to the pipe and another for reading from the pipe. A process can then write to the pipe by using DosWrite and read from the pipe by using DosRead.
 +
 +
A pipe exists until both handles are closed. The order in which the handles are closed is sometimes important. For example, DosWrite might wait for data to be read from the pipe before completing its operation. In this case, the read handle is closed before the write handle is closed, writing to the pipe generates an error.
 +
 +
No control or permission mechanisms or checks are performed on operations to unnamed pipes.
 
===Named Pipes===
 
===Named Pipes===
 +
Named pipes enable related or unrelated processes on either the same computer system or different systems to communicate with each other. Any process that knows the name of a pipe can open and use a named pipe. In addition, named pipe data can be transparently redirected across a network, such as a local area network (LAN). (Unnamed pipes, by contrast, can be used only by related processes that are on the same computer system.)
 +
 +
One process (the server process) creates the pipe and connects to one end of it. Other processes that access the named pipe are called client processes; they connect to the other end of the pipe. The server and client processes can then pass data back and forth by reading from and writing to the pipe. The server process controls access to the named pipe.
 +
 +
The client process can be either local or remote. A local client process is one that runs on the same computer system as the server process. A remote client process runs on a different system and communicates with the server process across a local area network (LAN).
 +
 +
When the server process creates a named pipe with DosCreateNPipe, it must specify the direction that data will flow through the pipe. The process specifies an inbound pipe if it intends to read data from the client process, an outbound pipe if it intends to write data to the client process, or a duplex pipe if it intends to read from and write to the client process.
 +
 +
The server process also specifies whether data passes through the pipe as bytes or messages. A message is a block of data, with a system-supplied header, that is read or written as a single unit. The server and client processes define the size and format of a message.
 +
 +
The server process also specifies whether child processes will inherit the named pipe and how information will be read from and written to the pipe. If the server specifies wait mode, DosRead will be blocked (it will not return to the process) until data is available in the pipe, and DosWrite will be blocked until there is enough room in the pipe to contain the entire data buffer. If the server specifies no-wait mode, reading from an empty pipe or writing to a full pipe immediately returns an error value.
 +
 +
A named pipe consists of two pipe buffers, one for each direction of communication. However, each end of the pipe has only one handle associated with it. The server receives the handle for its end when it creates the pipe with DosCreateNPipe. The client receives the handle for its end when it opens the pipe with DosOpen.
 +
 +
The server and the client use their respective handles both to read from the pipe and to write to it. (This is in contrast to unnamed pipes, for which both the server and the client read from one handle and write to another.) In other words, data that is written by the process at one end of the pipe is read by the process at the other end.
 +
 +
A named pipe can have multiple instances, up to the number specified when the pipe is first created. Pipe instances are actually separate pipes-that is, unique sets of pipe buffers with unique handles-that share the same name. The ability to create multiple pipe instances enables the server to communicate with multiple client processes at the same time.
 
====Server-Client Communications Using Named Pipes====
 
====Server-Client Communications Using Named Pipes====
 +
A server process initiates a connection to a client process by using DosConnectNPipe. Once the pipe has been connected by the server process, the client process must open the pipe by using DosOpen to complete the connection. If no client process has opened the pipe when the server process calls DosConnectNPipe, the function either waits until a client opens the pipe or returns ERROR_PIPE_NOT_CONNECTED, depending on whether the server process created the pipe to wait for data.
 +
 +
Each client process requires a separate instance of a pipe. For example, if a server process creates a named pipe and specifies that four instances of the pipe can be created, the process can then create three more instances of the named pipe (for a total of four pipes, including the original). Each instance has the same name, but each has a unique pipe handle. The process can then connect each pipe to the server. (Each instance must be connected by an explicit use of DosConnectNPipe.) In this example, the server must use each function four times to create and connect four separate instances of the named pipe. Each pipe is then available to a separate client process.
 +
 +
If a client process receives the ERROR_PIPE_BUSY return value from DosOpen, no instances of the given pipe are available. The process can use DosWaitNPipe to wait for an instance to become available. The function waits until an instance is free or until the specified time interval elapses. When an instance becomes free, the process can open the pipe. If several processes are waiting for an instance to become available, the system gives the named pipe to the process that has been waiting the longest.
 +
 +
The server process can disconnect a client process from a pipe by using DosDisConnectNPipe. Ideally, the client process closes the pipe by using DosClose before the server process disconnects the pipe. If the client process has not closed the pipe when the server process disconnects it, the server process forces the pipe closed and the client process subsequently receives errors if it attempts to access the pipe. Note that forcing the closure of the pipe might discard data in the pipe before the client process has had an opportunity to read it.
 +
 +
A process can read and write bytes to a named pipe by using DosRead and DosWrite. A process can read or write messages by using DosTransactNPipe. Depending on the access mode, DosTransactNPipe writes a message to the pipe, reads a message from the pipe, or both. If a named pipe contains unread data or is not a message pipe, DosTransactNPipe fails.
 +
 +
Named pipes created with the NP_ACCESS_INBOUND or NP_ACCESS_OUTBOUND access mode cannot use the DosTransactNPipe function. If the named pipe's client uses the DosTransactNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED).
 +
 +
If it is reading from the pipe, DosTransactNPipe does not return until a complete message is read, even if the server process specified no-wait mode when the pipe was created.
 +
 +
A process can also read data from a named pipe without removing the data from the pipe by using DosPeekNPipe. This function copies the specified number of bytes from the pipe and returns the number of bytes of data left in the pipe and the number of bytes left in the current message, if any.
 +
 +
DosPeekNPipe also returns the state of the pipe. A named pipe can be in one of the following states:
 +
 +
;Named Pipe States
 +
<pre>
 +
┌───────────────┬─────────────────────────────────────────────┐
 +
│State          │Description                                  │
 +
├───────────────┼─────────────────────────────────────────────┤
 +
│Connected      │The pipe has been created and connected by  │
 +
│              │the server process and has been opened by a  │
 +
│              │client process.  Only connected pipes can be │
 +
│              │written to or read from.                    │
 +
├───────────────┼─────────────────────────────────────────────┤
 +
│Closing        │The pipe has been closed by the client      │
 +
│              │process but has not yet been disconnected by │
 +
│              │the server process.                          │
 +
├───────────────┼─────────────────────────────────────────────┤
 +
│Disconnected  │The pipe has been created by the server      │
 +
│              │process but not connected, or has been      │
 +
│              │explicitly disconnected and not yet          │
 +
│              │reconnected. A disconnected pipe cannot      │
 +
│              │accept a DosOpen request.                    │
 +
├───────────────┼─────────────────────────────────────────────┤
 +
│Listening      │The pipe has been created and connected by  │
 +
│              │the server process but has not yet been      │
 +
│              │opened by a client process. A listening pipe │
 +
│              │is ready to accept a request to open. If the │
 +
│              │pipe is not in a listening state, DosOpen    │
 +
│              │returns ERROR_PIPE_BUSY.                    │
 +
└───────────────┴─────────────────────────────────────────────┘
 +
</pre>
 +
DosCallNPipe is used to open, write to, read from, and close a named message-format pipe.
 +
 +
Named pipes created with the NP_ACCESS_INBOUND or NP_ACCESS_OUTBOUND access mode cannot use the DosCallNPipe function. If the named pipe's client uses the DosCallNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED). This function is equivalent to calling DosOpen, DosTransactNPipe, and DosClose If no instances of the pipe are available, DosCallNPipe waits for an instance or returns without opening the pipe if the specified interval of time elapses.
 +
 +
A process can retrieve information about the handle state of a named pipe by using DosQueryNPHState. The handle state is a combination of the instance count, the access mode, and the pipe type (byte or message). DosQueryNPHState also specifies whether the process owning the handle is a server or client and whether the pipe waits if reading and writing cannot proceed.
 +
 +
A process can modify the handle state of a named pipe by using DosSetNPHState. For example, the process can change the reading mode for the pipe, enabling a process to read bytes from the pipe instead of messages.
 
====Steps in Managing Server-Client Transactions====
 
====Steps in Managing Server-Client Transactions====
 +
The following sequence summarizes the typical steps in the management of a named pipe:
 +
 +
1.The server process creates the pipe by calling DosCreateNPipe. DosCreateNPipe returns a handle for the server end of the pipe. (Note that the server uses the same handle to both read from and write to the pipe.) The pipe is now in the disconnected state and cannot be opened by a client process. The server process calls DosConnectNPipe to put the pipe into a listening state.
 +
 +
2.The pipe can now be opened by a client process.
 +
 +
3.A client process supplies the name of the pipe in a call to DosOpen and receives a pipe handle in return. (The client uses the same handle to both read from and write to the pipe.) The pipe is now in the connected state and can be read from or written to by the client.
 +
 +
4.The server and client processes communicate by calling DosRead and DosWrite. DosResetBuffer can be used to synchronize read and write dialogs. A server process that supports a large number of clients for a local named pipe can use DosSetNPipeSem and DosQueryNPipeSemState to coordinate access to the pipe. Server and client processes can also use DosTransactNPipe and DosCallNPipe to facilitate their communication.
 +
 +
5.After completing its transactions, the client process calls DosClose to close its end of the pipe. The pipe is now in the closing state and cannot be accessed by another client.
 +
 +
6.The server process calls DosDisConnectNPipe to acknowledge that the client has closed its end of the pipe. The pipe is now in the disconnected state again.
 +
 +
7.To enable another client process to open the pipe, the server must call DosConnectNPipe again. This puts the pipe back into the listening state. To end its access to the pipe, the server calls DosClose. When all of the handles for both ends of the pipe have been closed, the pipe is deallocated by the system.
 +
 
====Preparing a Named Pipe for a Client====
 
====Preparing a Named Pipe for a Client====
 +
A server process uses DosConnectNPipe to put a newly created named pipe into the listening state. The pipe must be in the listening state in order for a client process to gain access to the pipe by calling DosOpen.
 +
 +
After successfully opening the pipe and finishing its transactions, the client process calls DosClose to end its access to the pipe. The server process must acknowledge the close by calling DosDisConnectNPipe. It can then call DosConnectNPipe again to put the pipe into the listening state for the next client.
 +
 +
Together, DosConnectNPipe and DosDisConnectNPipe enable a server to create a named pipe and to reuse it for communication with different clients. Without these functions, the server would have to delete and re-create the pipe for each client.
 +
 +
;Note: If multiple instances of a named pipe have been created, then each instance of the pipe must be put into the listening state before it can be opened by a client.
 +
 +
 
====Facilitating Transaction Processing====
 
====Facilitating Transaction Processing====
 +
DosTransactNPipe and DosCallNPipe facilitate the use of named pipes by combining other named pipe functions. Compared to calling the other functions separately, DosTransactNPipe and DosCallNPipe provide significant performance gains for applications that operate in a networked environment. They can also be used by local processes. However, both of these functions can be used only with duplex message pipes.
 +
*DosTransactNPipe performs a transaction (a DosWrite followed by DosRead) on a duplex message pipe.
 +
*DosCallNPipe has the combined effect of DosOpen, DosTransactNPipe, and DosClose, and is referred to as a procedure call. It provides an efficient means of implementing local and remote procedure call interfaces between processes.
 +
 
====Coordinating Access to a Local Named Pipe with Semaphores ====
 
====Coordinating Access to a Local Named Pipe with Semaphores ====
 +
When a process writes to a named pipe, the process at the other end (or handle) of the pipe might require notification that data is available to be read. Similarly, when a process reads from a named pipe, the process at the other end might require notification that write space has become available. As long as the communicating processes are on the same computer system, shared event semaphores and muxwait semaphores can be used to provide this notification. Using shared semaphores for this purpose is more efficient than dedicating a thread to periodically poll each pipe, particularly when a server process is communicating with a large number of client processes. Whenever data is available in the pipe, the system posts a semaphore to the server or client (whichever is reading from the pipe). This means that the reading process can use DosWaitEventSem or DosWaitMuxWaitSem to wait for data to arrive, rather than devote a thread to periodically polling the pipe.
 +
 +
A process associates a semaphore with a named pipe by using DosSetNPipeSem. First, create an event semaphore with DosCreateEventSem, specifying the initial state of the semaphore as reset. Then call DosSetNPipeSem to attach the event semaphore to a particular named-pipe handle. Up to two event semaphores can be attached to each named pipe, one for the server process and one for the client process. If there is already a semaphore associated with one end of the pipe, that semaphore is replaced. A process can check the state of the semaphores by using DosQueryNPipeSemState.
 +
 +
The server or client process must then call DosWaitEventSem. The particular thread that calls this function will block until data is either read from or written to the pipe. At that time, the system posts the event semaphore, enabling the blocked thread to resume its execution.
 +
 +
If a process requires notification whenever any one of multiple named pipes has been written to or read from, it can either attach the same event semaphore to multiple pipes, or it can create a muxwait semaphore:
 +
 +
*If the same event semaphore is attached to multiple pipes, then the KeyHandle parameter of DosSetNPipeSem is used to assign a unique value to each pipe. After the event semaphore has been posted, the process calls DosQueryNPipeSemState. This function returns information about each of the named pipes that are attached to the semaphore, including key-handle values. The calling process can use this information to determine which one of the named pipes has either data or write space available.
 +
 +
*To use a muxwait semaphore, a process first creates an event semaphore for each of the pipes that it wants to monitor. Each semaphore must then be attached to a pipe by calling DosSetNPipeSem. Again, a unique key-handle value must be assigned to each pipe.
 +
 +
Next, the process calls DosCreateMuxWaitSem to create the muxwait semaphore, specifying DCMW_WAIT_ANY as one of the flAttr flags. The muxwait semaphore will consist of a linked list of the previously created event semaphores.
 +
 +
The process calls DosWaitMuxWaitSem so that it will be notified the next time data is read from or written to any of the pipes. However, it must call DosQueryNPipeSemState to determine which one of the pipes is ready to be read from or written to.
 +
 +
 
==Using Unnamed Pipes==
 
==Using Unnamed Pipes==
 +
Unnamed pipes are useful in applications that transfer data between related processes. They are commonly used to control the input and output of child processes by redirecting the standard input and output of the child process to a pipe controlled by the parent process.
 +
 +
;Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.
 +
 
===Creating Unnamed Pipes===
 
===Creating Unnamed Pipes===
 +
DosCreatePipe creates an unnamed pipe. Two handles are returned: one for read access to the pipe and one for write access. The pipe size specified is advisory; its actual size is dependent on the amount of available memory. If the size parameter is 0, the pipe is created with the default size, which is 512 bytes.
 +
 +
This example creates an unnamed pipe. The current process can use the unnamed pipe for communication between itself and a child process.
 +
 +
<pre>
 +
    #define INCL_BASE    /* Queue values */
 +
    #include <os2.h>
 +
    #include <stdio.h>
 +
 +
    HFILE    hfReadHandle;    /* Pointer to the read handle      */
 +
    HFILE    hfWriteHandle;  /* Pointer to the write handle    */
 +
    ULONG    ulPipeSize;      /* Pipe size                      */
 +
    APIRET  ulrc;            /* Return code                    */
 +
 +
    ulPipeSize = 4096;        /* Ask for 4KB of internal storage */
 +
                              /* for the pipe                    */
 +
 +
    ulrc = DosCreatePipe(&hfReadHandle,
 +
                        &hfWriteHandle,
 +
                        ulPipeSize);
 +
 +
    if (ulrc != 0) {
 +
        printf("DosCreatePipe error: return code = %ld",
 +
              ulrc);
 +
        return;
 +
    }
 +
</pre>
 +
 +
On successful return, the ReadHandle variable contains the read handle for the pipe, and the WriteHandle variable contains the write handle for the pipe.
 +
 +
After a process creates a pipe, any child process started with DosExecPgm inherits the pipe handles. Using shared memory, the parent process can pass one of the pipe handles to the child process; thus, one process can store data in the pipe and the other can retrieve it.
 
===Reading from and Writing to Unnamed Pipes===
 
===Reading from and Writing to Unnamed Pipes===
 +
Applications use the OS/2 file system functions to read from and write to unnamed pipes.
 +
 +
The handles returned by DosCreatePipe are used as file handles to DosRead and DosWrite.
 +
 +
To write (or add data) to an unnamed pipe, call DosWrite, specifying the write handle of the pipe in DosWrite's file handle parameter. DosWrite requests to a pipe are processed in the order in which they are made. Multiple calls to DosWrite can be made before data is read (or removed) from the pipe. When the pipe becomes full, write requests are blocked until space is freed by read requests.
 +
 +
When the pipe is too full to hold the entire contents of the write request, the portion that will fit is written to the pipe before the writer is blocked.
 +
 +
To read from a pipe, call DosRead, specifying the read handle of the pipe in DosRead's file handle parameter. Subsequent calls to DosRead can empty the pipe if no further calls to DosWrite are made in the meantime.
 +
 +
Different threads writing to and reading from a pipe are not synchronized. It is possible for the pipe reader to obtain partial contents of a write request if the pipe becomes full during the write request.
 +
 +
If the process reading the pipe ends, the next DosWrite request for that pipe returns ERROR_BROKEN_PIPE.
 +
 +
Calling DosClose terminates access to an unnamed pipe. However, the pipe is not deleted from memory until all handles to the pipe have been closed, including any handles that were defined with DosDupHandle.
 
===Redirecting Standard I/O for Child Processes===
 
===Redirecting Standard I/O for Child Processes===
 +
An application can use unnamed pipes to redirect the standard input and the standard output for a child process.
 +
 +
A typical use of an unnamed pipe is to read the output of a child process. An application creates a pipe and then duplicates the standard output handle. When the child process is started, its standard output will be written into the pipe, where the application can read and display it. The following code fragment shows how to do this:
 +
<pre>
 +
    #define INCL_DOSPROCESS
 +
    #define INCL_DOSNMPIPES
 +
 +
    #include <os2.h>
 +
 +
    #define PIPESIZE 256
 +
    #define HF_STDOUT 1      /* Standard output handle */
 +
 +
    HPIPE hpR, hpW;
 +
    RESULTCODES resc;
 +
    ULONG ulRead, ulWritten;
 +
    CHAR achBuf[PIPESIZE],
 +
        szFailName[CCHMAXPATH];
 +
 +
    HFILE hfSave = -1,
 +
          hfNew = HF_STDOUT;
 +
 +
    DosDupHandle(HF_STDOUT,
 +
                &hfSave); /* Saves standard output handle      */
 +
 +
    DosCreatePipe(&hpR,
 +
                  &hpW,
 +
                  PIPESIZE); /* Creates pipe                      */
 +
 +
    DosDupHandle(hpW,
 +
                &hfNew); /* Duplicates standard output handle */
 +
 +
 +
    DosExecPgm(szFailName,
 +
              sizeof(szFailName), /* Starts child process      */
 +
              EXEC_ASYNC,
 +
              (PSZ) NULL,
 +
              (PSZ) NULL,
 +
              &resc,
 +
              "DUMMY.EXE");
 +
 +
    DosClose(hpW);                /* Closes write handle to ensure    */
 +
                                  /* Notification at child termination */
 +
 +
    DosDupHandle(hfSave,
 +
                &hfNew);    /* Brings stdout back                */
 +
 +
    /*
 +
    * Read from the pipe and write to the screen
 +
    * as long as there are bytes to read.
 +
    *
 +
    */
 +
 +
    do {
 +
        DosRead(hpR,
 +
                achBuf,
 +
                sizeof(achBuf),
 +
                &ulRead);
 +
 +
        DosWrite(HF_STDOUT,
 +
                achBuf,
 +
                ulRead,
 +
                &ulWritten);
 +
 +
    } while(ulRead);
 +
 +
</pre>
 +
 +
A parent process can also use unnamed pipes to communicate with a child process by redirecting both the standard input and the standard output for the child process. To do this, the parent process:
 +
 +
1.Uses DosDupHandle to redefine the read handle of one pipe as standard input (0000), and the write handle of the other pipe as standard output (0001).
 +
2.Starts the child process with DosExecPgm.
 +
3.Uses the remaining pipe handles to read and write to the pipes.
 +
 +
The parent process controls the meanings for standard I/O for the child process. Thus, when the child process uses standard I/O handles with DosRead and DosWrite, it reads from and writes to the pipes of its parent instead of reading from the keyboard and writing to the display.
 +
 
==Using Named Pipes==
 
==Using Named Pipes==
 +
Named pipes are useful in applications that transfer data between processes. The processes using named pipes can be related, unrelated, or even on different computers.
 
===Creating Named Pipes===
 
===Creating Named Pipes===
 +
The server process creates a named pipe by using DosCreateNPipe.
 +
 +
To create a named pipe, specify on the DosCreateNPipe function:
 +
*The name of the pipe
 +
*The access modes
 +
*The type of pipe (byte or message)
 +
*The sizes of the input and output buffers
 +
 +
DosCreateNPipe returns a pipe handle that can be used in subsequent pipe operations.
 +
 +
Each named pipe must have a unique name of the following form:
 +
 +
    \PIPE\PipeName
 +
 +
 +
The "\PIPE\" in the name above is required, but need not be uppercase. It is not the name of a subdirectory.
 +
 +
To open a pipe on a remote computer, the client process must specify the name of the server process that opened the pipe as part of the pipe name, as follows:
 +
 +
    \\Server\PIPE\PipeName
 +
 +
 +
"\\Server" in the name above is the name of the remote computer; again, "\PIPE\" is required.
 +
 +
The name parameter must conform to the rules for OS/2 file names, but no actual file is created for the pipe.
 +
 +
Named pipes created with certain access modes prevent the named pipes' clients from using certain functions. If the named pipe is created with access mode NP_ACCESS_INBOUND, the named pipe's client cannot use these functions:
 +
*DosCallNPipe
 +
*DosTransactNPipe
 +
*DosPeekNPipe
 +
If the named pipe is created with access mode NP_ACCESS_OUTBOUND, the named pipe's client cannot use these functions:
 +
*DosCallNPipe
 +
*DosTransactNPipe
 +
*DosWrite
 +
 +
If the named pipe's client uses an incorrect function, the function returns error code 5 (ERROR_ACCESS_DENIED).
 +
 +
In the following code fragment, DosCreateNPipe creates a pipe named \pipe\pipe1 and supplies a unique handle identifying the pipe. OpenMode is set to NP_ACCESS_DUPLEX. This activates full duplex access to the named pipe. There will be no inheritance to child process, and no write-through (write-through only affects remote pipes). PipeMode is set to "NP_WMESG | NP_RMESG | 0x01". This specifies that the pipe should be read as a message stream for both reading and writing and an instance count of 1 (only one instance of the named pipe can be created at a time). The pipe will block on Read/Write if no data is available.
 +
<pre>
 +
    #define INCL_DOSNMPIPES  /* Named-pipe values */
 +
    #include <os2.h>
 +
    #include <stdio.h>
 +
 +
    UCHAR  ucFileName[40];  /* Pipe name              */
 +
    HPIPE  hpPipeHandle;    /* Pipe handle (returned) */
 +
    ULONG  ulOpenMode;      /* Open-mode parameters  */
 +
    ULONG  ulPipeMode;      /* Pipe-mode parameters  */
 +
    ULONG  ulOutBufSize;    /* Size of the out-buffer */
 +
    ULONG  ulInBufSize;    /* Size of the in-buffer  */
 +
    ULONG  ulTimeOut;      /* Default value for      */
 +
                            /* DosWaitNPipe time-out  */
 +
                            /* parameter              */
 +
    APIRET  ulrc;            /* Return code            */
 +
 +
    strcpy(ucFileName,
 +
          "\\PIPE\\PIPE1");
 +
 +
    ulOpenMode = NP_ACCESS_DUPLEX;            /* Full duplex, no inheritance,    */
 +
                                              /* no write-through                */
 +
 +
    ulPipeMode = NP_WMESG |
 +
                NP_RMESG |
 +
                0x01;    /* Block on read and write, message                  */
 +
                          /* stream, instance count of 1                        */
 +
 +
    ulOutBufSize = 4096;  /* The outgoing buffer must be 4KB in size            */
 +
 +
    ulInBufSize = 2048;    /* The incoming buffer must be 2KB in size            */
 +
 +
    ulTimeOut = 10000;    /* Time-out is 10 seconds (units are in milliseconds) */
 +
 +
    ulrc = DosCreateNPipe(ucFileName,
 +
                          &hpPipeHandle,
 +
                          ulOpenMode,
 +
                          ulPipeMode,
 +
                          ulOutBufSize,
 +
                          ulInBufSize,
 +
                          ulTimeOut);
 +
 +
    if (ulrc != 0) {
 +
        printf("DosCreateNPipe error: return code = %ld",
 +
              ulrc);
 +
        return;
 +
    }
 +
</pre>
 +
 +
Once the named pipe is created, the application can call DosConnectNPipe to connect a client process to the pipe.
 +
 +
Once a client process connects to the pipe, the process can read from and write to the pipe. The preceding example creates a byte pipe, so the process can use DosRead and DosWrite to read from and write to the pipe.
 +
 +
After the client process finishes using the pipe, the server process can disconnect the pipe by using DosDisConnectNPipe. The server process can either connect again or close the named pipe by using DosClose.
 +
 +
When a server process creates a named pipe, it defines the pipe to the system by specifying the file write-through mode, the inheritance mode, the access and blocking modes, the pipe type, the read mode, the size of the in and out buffers, and the instance count. The following list describes these modes, types, and buffers.
 +
 +
*The file write-through mode has significance only for communication with remote client processes. When the file write-through bit is set, data is sent across the network as soon as it is written; otherwise, OS/2 will in some cases hold data briefly in a local buffer before sending it across the network.
 +
*The inheritance mode specifies whether or not the pipe handle will be inherited by a child process.
 +
*The access mode specifies the direction in which data will flow through the pipe. The server creates an inbound pipe (a pipe with inbound access mode) if it intends to read data from the client process, an outbound pipe if it intends to write data to the client process, or a duplex pipe if it intends to both read from and write to the client process.
 +
*The blocking mode specifies whether or not DosRead and DosWrite will block when no data is available to read, or there is no room to write data.
 +
*The pipe type is the form in which a stream of data is written to the pipe. If the pipe is a byte pipe, the server and client processes write data as an undifferentiated stream of bytes. If the pipe is a message pipe, the processes write data as a stream of messages; messages are blocks of data, each with a system-supplied header, that are written as single units. The server and client processes define the size and format of a message.
 +
*The read mode is the form in which data is read from the pipe. The data in a pipe that was created as a byte pipe can only be read as bytes; therefore, a byte pipe will always be in byte-read mode. The data in a message pipe, however, can be read either as messages or as bytes. (If it is to be read as bytes, DosRead skips over the message headers). Therefore, message pipes can be in either message-read mode or byte-read mode.
 +
 +
;Note: The terms "byte pipe" and "message pipe" always refer to the pipe type-the form in which data is written to the pipe. When the read mode of a pipe is being referred to, it is always explicitly identified as either message-read mode or byte-read mode.
 +
 +
*The in and out buffers can be up to 64KB in size. If the pipe will be read in message-read mode, and if the message size is known, the server can control how many messages the buffer will hold at one time by specifying the appropriate buffer size.
 +
*The instance count is the maximum number of instances of the named pipe that can be created. A pipe instance is actually a separate pipe-that is, a unique set of pipe buffers with unique handles. However, the term "pipe instance" is used to distinguish pipes that share the same name from pipes with different names. Because a client process uses only the name of the pipe when opening it, the existence of multiple pipe instances is transparent to a client process.
 +
 +
;Creating Multiple Instances of a Named Pipe
 +
Although each named pipe must have a unique name, a server process can create multiple instances of a pipe, all of which have the same name. A pipe instance is actually a separate pipe-that is, a unique set of pipe buffers with unique handles.
 +
 +
The ability to create multiple pipe instances enables the server to communicate with multiple client processes at the same time. Because a client process uses only the name of the pipe when opening it, the existence of multiple pipe instances is transparent to a client process.
 +
 +
The ICount parameter of DosCreateNPipe specifies the maximum number of named pipe instances that can be created. (An unlimited number can also be specified.) This parameter is specified only when the first instance of a named pipe is created; any subsequent attempt to redefine the instance count will be ignored.
 +
 +
If the instance count is greater than 1, the server process can create additional pipe instances by specifying the same pipe name in subsequent calls to DosCreateNPipe. Generally, the attributes of the subsequent pipe instances are defined to be the same as those of the original pipe instance, because a client process that requests the pipe has no way of controlling which pipe instance will be assigned to it.
 +
 +
After an additional pipe instance has been created, it is used in the same manner as the original pipe instance. That is, the same sequence of named pipe functions is used in the control or management of all named pipe instances. (See Steps in Managing Server-Client Transactions for more information.)
 +
 +
;Note: If all of the instances of a named pipe are in use when a client calls DosOpen, ERROR_PIPE_BUSY is returned. However, the client can wait for an instance of that pipe to become available by calling DosWaitNPipe.
 +
 +
Multiple instances of a named pipe can be created by different processes. That is, multiple server processes can create and use instances of the same named pipe to communicate with their clients.
 +
 +
 
===Opening Named Pipes===
 
===Opening Named Pipes===
 
===Reading from Named Pipes===
 
===Reading from Named Pipes===
Line 33: Line 415:
 
===Examining the Contents of Named Pipes===
 
===Examining the Contents of Named Pipes===
 
===Closing Named Pipes===
 
===Closing Named Pipes===
 +
 +
 +
[[Category:CPGuide]]

Revision as of 03:06, 27 March 2020

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Control Program Programming Guide and Reference
  1. Introduction to the Control Program
  2. Control Program Functions
  3. Keyboard Functions
  4. Mouse Functions
  5. Video Functions
  6. Data Types
  7. Errors
  8. Debugging
  9. Kernel Debugger Communications Protocol
  10. Device I/O
  11. Dynamic Linking
  12. Error Management
  13. Exception Management
  14. Extended Attributes
  15. File Management
  16. File Names
  17. File Systems
  18. Generic IOCtl Commands
  19. Memory Management
  20. Message Management
  21. National Language Support
  22. Pipes
  23. Program Execution Control
  24. Queues
  25. Semaphores
  26. Timers

Communication between processes is valuable in a multitasking operating system to enable concurrent processes to work together. Pipes are one of three forms of interprocess communication (IPC), the other forms of IPC being semaphores and queues.

This chapter describes how to create, manage, and use pipes. Pipes enable two or more processes to communicate as if they were reading from and writing to a file.

The following topics are related to the information in this chapter:

  • Memory (shared memory)
  • Program execution and control
  • Semaphores
  • Queues


About Pipes

A pipe is a named or unnamed buffer used to pass data between processes. A process writes to or reads from a pipe as if the pipe were standard input or standard output. A parent process can use pipes to control the input that a child process receives and to receive the output that the child process produces.

There are two types of pipes-named and unnamed.

Unnamed Pipes

An unnamed pipe is a circular buffer in memory. The buffer has in and out pointers that are maintained by the system.

An unnamed pipe can transfer information only between related processes. A child process started by a parent process with DosExecPgm inherits the handles to any unnamed pipes created by its parent. This inheritance enables the parent process and the child process to use the unnamed pipe to communicate with one another. This type of pipe is typically used to redirect the standard input and standard output of a child process.

To do this, a process opens a pipe and duplicates the read and write handles of the pipe as the standard input and standard output files for the child process. Once the handles are duplicated, the parent process can use DosExecPgm to start the child process. When the child process reads and writes to its standard input and standard output handles, it is reading and writing to the pipe. The parent process can also communicate with the child process through the pipe.

Using an unnamed pipe, a text editor could run another program, such as a compiler or assembler, and display the output of the compiler or assembler within the editor.

DosCreatePipe creates an unnamed pipe. This function returns two file handles for the pipe, one for writing to the pipe and another for reading from the pipe. A process can then write to the pipe by using DosWrite and read from the pipe by using DosRead.

A pipe exists until both handles are closed. The order in which the handles are closed is sometimes important. For example, DosWrite might wait for data to be read from the pipe before completing its operation. In this case, the read handle is closed before the write handle is closed, writing to the pipe generates an error.

No control or permission mechanisms or checks are performed on operations to unnamed pipes.

Named Pipes

Named pipes enable related or unrelated processes on either the same computer system or different systems to communicate with each other. Any process that knows the name of a pipe can open and use a named pipe. In addition, named pipe data can be transparently redirected across a network, such as a local area network (LAN). (Unnamed pipes, by contrast, can be used only by related processes that are on the same computer system.)

One process (the server process) creates the pipe and connects to one end of it. Other processes that access the named pipe are called client processes; they connect to the other end of the pipe. The server and client processes can then pass data back and forth by reading from and writing to the pipe. The server process controls access to the named pipe.

The client process can be either local or remote. A local client process is one that runs on the same computer system as the server process. A remote client process runs on a different system and communicates with the server process across a local area network (LAN).

When the server process creates a named pipe with DosCreateNPipe, it must specify the direction that data will flow through the pipe. The process specifies an inbound pipe if it intends to read data from the client process, an outbound pipe if it intends to write data to the client process, or a duplex pipe if it intends to read from and write to the client process.

The server process also specifies whether data passes through the pipe as bytes or messages. A message is a block of data, with a system-supplied header, that is read or written as a single unit. The server and client processes define the size and format of a message.

The server process also specifies whether child processes will inherit the named pipe and how information will be read from and written to the pipe. If the server specifies wait mode, DosRead will be blocked (it will not return to the process) until data is available in the pipe, and DosWrite will be blocked until there is enough room in the pipe to contain the entire data buffer. If the server specifies no-wait mode, reading from an empty pipe or writing to a full pipe immediately returns an error value.

A named pipe consists of two pipe buffers, one for each direction of communication. However, each end of the pipe has only one handle associated with it. The server receives the handle for its end when it creates the pipe with DosCreateNPipe. The client receives the handle for its end when it opens the pipe with DosOpen.

The server and the client use their respective handles both to read from the pipe and to write to it. (This is in contrast to unnamed pipes, for which both the server and the client read from one handle and write to another.) In other words, data that is written by the process at one end of the pipe is read by the process at the other end.

A named pipe can have multiple instances, up to the number specified when the pipe is first created. Pipe instances are actually separate pipes-that is, unique sets of pipe buffers with unique handles-that share the same name. The ability to create multiple pipe instances enables the server to communicate with multiple client processes at the same time.

Server-Client Communications Using Named Pipes

A server process initiates a connection to a client process by using DosConnectNPipe. Once the pipe has been connected by the server process, the client process must open the pipe by using DosOpen to complete the connection. If no client process has opened the pipe when the server process calls DosConnectNPipe, the function either waits until a client opens the pipe or returns ERROR_PIPE_NOT_CONNECTED, depending on whether the server process created the pipe to wait for data.

Each client process requires a separate instance of a pipe. For example, if a server process creates a named pipe and specifies that four instances of the pipe can be created, the process can then create three more instances of the named pipe (for a total of four pipes, including the original). Each instance has the same name, but each has a unique pipe handle. The process can then connect each pipe to the server. (Each instance must be connected by an explicit use of DosConnectNPipe.) In this example, the server must use each function four times to create and connect four separate instances of the named pipe. Each pipe is then available to a separate client process.

If a client process receives the ERROR_PIPE_BUSY return value from DosOpen, no instances of the given pipe are available. The process can use DosWaitNPipe to wait for an instance to become available. The function waits until an instance is free or until the specified time interval elapses. When an instance becomes free, the process can open the pipe. If several processes are waiting for an instance to become available, the system gives the named pipe to the process that has been waiting the longest.

The server process can disconnect a client process from a pipe by using DosDisConnectNPipe. Ideally, the client process closes the pipe by using DosClose before the server process disconnects the pipe. If the client process has not closed the pipe when the server process disconnects it, the server process forces the pipe closed and the client process subsequently receives errors if it attempts to access the pipe. Note that forcing the closure of the pipe might discard data in the pipe before the client process has had an opportunity to read it.

A process can read and write bytes to a named pipe by using DosRead and DosWrite. A process can read or write messages by using DosTransactNPipe. Depending on the access mode, DosTransactNPipe writes a message to the pipe, reads a message from the pipe, or both. If a named pipe contains unread data or is not a message pipe, DosTransactNPipe fails.

Named pipes created with the NP_ACCESS_INBOUND or NP_ACCESS_OUTBOUND access mode cannot use the DosTransactNPipe function. If the named pipe's client uses the DosTransactNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED).

If it is reading from the pipe, DosTransactNPipe does not return until a complete message is read, even if the server process specified no-wait mode when the pipe was created.

A process can also read data from a named pipe without removing the data from the pipe by using DosPeekNPipe. This function copies the specified number of bytes from the pipe and returns the number of bytes of data left in the pipe and the number of bytes left in the current message, if any.

DosPeekNPipe also returns the state of the pipe. A named pipe can be in one of the following states:

Named Pipe States
┌───────────────┬─────────────────────────────────────────────┐
│State          │Description                                  │
├───────────────┼─────────────────────────────────────────────┤
│Connected      │The pipe has been created and connected by   │
│               │the server process and has been opened by a  │
│               │client process.  Only connected pipes can be │
│               │written to or read from.                     │
├───────────────┼─────────────────────────────────────────────┤
│Closing        │The pipe has been closed by the client       │
│               │process but has not yet been disconnected by │
│               │the server process.                          │
├───────────────┼─────────────────────────────────────────────┤
│Disconnected   │The pipe has been created by the server      │
│               │process but not connected, or has been       │
│               │explicitly disconnected and not yet          │
│               │reconnected. A disconnected pipe cannot      │
│               │accept a DosOpen request.                    │
├───────────────┼─────────────────────────────────────────────┤
│Listening      │The pipe has been created and connected by   │
│               │the server process but has not yet been      │
│               │opened by a client process. A listening pipe │
│               │is ready to accept a request to open. If the │
│               │pipe is not in a listening state, DosOpen    │
│               │returns ERROR_PIPE_BUSY.                     │
└───────────────┴─────────────────────────────────────────────┘

DosCallNPipe is used to open, write to, read from, and close a named message-format pipe.

Named pipes created with the NP_ACCESS_INBOUND or NP_ACCESS_OUTBOUND access mode cannot use the DosCallNPipe function. If the named pipe's client uses the DosCallNPipe function, the function returns error code 5 (ERROR_ACCESS_DENIED). This function is equivalent to calling DosOpen, DosTransactNPipe, and DosClose If no instances of the pipe are available, DosCallNPipe waits for an instance or returns without opening the pipe if the specified interval of time elapses.

A process can retrieve information about the handle state of a named pipe by using DosQueryNPHState. The handle state is a combination of the instance count, the access mode, and the pipe type (byte or message). DosQueryNPHState also specifies whether the process owning the handle is a server or client and whether the pipe waits if reading and writing cannot proceed.

A process can modify the handle state of a named pipe by using DosSetNPHState. For example, the process can change the reading mode for the pipe, enabling a process to read bytes from the pipe instead of messages.

Steps in Managing Server-Client Transactions

The following sequence summarizes the typical steps in the management of a named pipe:

1.The server process creates the pipe by calling DosCreateNPipe. DosCreateNPipe returns a handle for the server end of the pipe. (Note that the server uses the same handle to both read from and write to the pipe.) The pipe is now in the disconnected state and cannot be opened by a client process. The server process calls DosConnectNPipe to put the pipe into a listening state.

2.The pipe can now be opened by a client process.

3.A client process supplies the name of the pipe in a call to DosOpen and receives a pipe handle in return. (The client uses the same handle to both read from and write to the pipe.) The pipe is now in the connected state and can be read from or written to by the client.

4.The server and client processes communicate by calling DosRead and DosWrite. DosResetBuffer can be used to synchronize read and write dialogs. A server process that supports a large number of clients for a local named pipe can use DosSetNPipeSem and DosQueryNPipeSemState to coordinate access to the pipe. Server and client processes can also use DosTransactNPipe and DosCallNPipe to facilitate their communication.

5.After completing its transactions, the client process calls DosClose to close its end of the pipe. The pipe is now in the closing state and cannot be accessed by another client.

6.The server process calls DosDisConnectNPipe to acknowledge that the client has closed its end of the pipe. The pipe is now in the disconnected state again.

7.To enable another client process to open the pipe, the server must call DosConnectNPipe again. This puts the pipe back into the listening state. To end its access to the pipe, the server calls DosClose. When all of the handles for both ends of the pipe have been closed, the pipe is deallocated by the system.

Preparing a Named Pipe for a Client

A server process uses DosConnectNPipe to put a newly created named pipe into the listening state. The pipe must be in the listening state in order for a client process to gain access to the pipe by calling DosOpen.

After successfully opening the pipe and finishing its transactions, the client process calls DosClose to end its access to the pipe. The server process must acknowledge the close by calling DosDisConnectNPipe. It can then call DosConnectNPipe again to put the pipe into the listening state for the next client.

Together, DosConnectNPipe and DosDisConnectNPipe enable a server to create a named pipe and to reuse it for communication with different clients. Without these functions, the server would have to delete and re-create the pipe for each client.

Note
If multiple instances of a named pipe have been created, then each instance of the pipe must be put into the listening state before it can be opened by a client.


Facilitating Transaction Processing

DosTransactNPipe and DosCallNPipe facilitate the use of named pipes by combining other named pipe functions. Compared to calling the other functions separately, DosTransactNPipe and DosCallNPipe provide significant performance gains for applications that operate in a networked environment. They can also be used by local processes. However, both of these functions can be used only with duplex message pipes.

  • DosTransactNPipe performs a transaction (a DosWrite followed by DosRead) on a duplex message pipe.
  • DosCallNPipe has the combined effect of DosOpen, DosTransactNPipe, and DosClose, and is referred to as a procedure call. It provides an efficient means of implementing local and remote procedure call interfaces between processes.

Coordinating Access to a Local Named Pipe with Semaphores

When a process writes to a named pipe, the process at the other end (or handle) of the pipe might require notification that data is available to be read. Similarly, when a process reads from a named pipe, the process at the other end might require notification that write space has become available. As long as the communicating processes are on the same computer system, shared event semaphores and muxwait semaphores can be used to provide this notification. Using shared semaphores for this purpose is more efficient than dedicating a thread to periodically poll each pipe, particularly when a server process is communicating with a large number of client processes. Whenever data is available in the pipe, the system posts a semaphore to the server or client (whichever is reading from the pipe). This means that the reading process can use DosWaitEventSem or DosWaitMuxWaitSem to wait for data to arrive, rather than devote a thread to periodically polling the pipe.

A process associates a semaphore with a named pipe by using DosSetNPipeSem. First, create an event semaphore with DosCreateEventSem, specifying the initial state of the semaphore as reset. Then call DosSetNPipeSem to attach the event semaphore to a particular named-pipe handle. Up to two event semaphores can be attached to each named pipe, one for the server process and one for the client process. If there is already a semaphore associated with one end of the pipe, that semaphore is replaced. A process can check the state of the semaphores by using DosQueryNPipeSemState.

The server or client process must then call DosWaitEventSem. The particular thread that calls this function will block until data is either read from or written to the pipe. At that time, the system posts the event semaphore, enabling the blocked thread to resume its execution.

If a process requires notification whenever any one of multiple named pipes has been written to or read from, it can either attach the same event semaphore to multiple pipes, or it can create a muxwait semaphore:

  • If the same event semaphore is attached to multiple pipes, then the KeyHandle parameter of DosSetNPipeSem is used to assign a unique value to each pipe. After the event semaphore has been posted, the process calls DosQueryNPipeSemState. This function returns information about each of the named pipes that are attached to the semaphore, including key-handle values. The calling process can use this information to determine which one of the named pipes has either data or write space available.
  • To use a muxwait semaphore, a process first creates an event semaphore for each of the pipes that it wants to monitor. Each semaphore must then be attached to a pipe by calling DosSetNPipeSem. Again, a unique key-handle value must be assigned to each pipe.

Next, the process calls DosCreateMuxWaitSem to create the muxwait semaphore, specifying DCMW_WAIT_ANY as one of the flAttr flags. The muxwait semaphore will consist of a linked list of the previously created event semaphores.

The process calls DosWaitMuxWaitSem so that it will be notified the next time data is read from or written to any of the pipes. However, it must call DosQueryNPipeSemState to determine which one of the pipes is ready to be read from or written to.


Using Unnamed Pipes

Unnamed pipes are useful in applications that transfer data between related processes. They are commonly used to control the input and output of child processes by redirecting the standard input and output of the child process to a pipe controlled by the parent process.

Note
In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred.

Creating Unnamed Pipes

DosCreatePipe creates an unnamed pipe. Two handles are returned: one for read access to the pipe and one for write access. The pipe size specified is advisory; its actual size is dependent on the amount of available memory. If the size parameter is 0, the pipe is created with the default size, which is 512 bytes.

This example creates an unnamed pipe. The current process can use the unnamed pipe for communication between itself and a child process.

    #define INCL_BASE    /* Queue values */
    #include <os2.h>
    #include <stdio.h>

    HFILE    hfReadHandle;    /* Pointer to the read handle      */
    HFILE    hfWriteHandle;   /* Pointer to the write handle     */
    ULONG    ulPipeSize;      /* Pipe size                       */
    APIRET   ulrc;            /* Return code                     */

    ulPipeSize = 4096;        /* Ask for 4KB of internal storage */
                              /* for the pipe                    */

    ulrc = DosCreatePipe(&hfReadHandle,
                         &hfWriteHandle,
                         ulPipeSize);

    if (ulrc != 0) {
        printf("DosCreatePipe error: return code = %ld",
               ulrc);
        return;
    }

On successful return, the ReadHandle variable contains the read handle for the pipe, and the WriteHandle variable contains the write handle for the pipe.

After a process creates a pipe, any child process started with DosExecPgm inherits the pipe handles. Using shared memory, the parent process can pass one of the pipe handles to the child process; thus, one process can store data in the pipe and the other can retrieve it.

Reading from and Writing to Unnamed Pipes

Applications use the OS/2 file system functions to read from and write to unnamed pipes.

The handles returned by DosCreatePipe are used as file handles to DosRead and DosWrite.

To write (or add data) to an unnamed pipe, call DosWrite, specifying the write handle of the pipe in DosWrite's file handle parameter. DosWrite requests to a pipe are processed in the order in which they are made. Multiple calls to DosWrite can be made before data is read (or removed) from the pipe. When the pipe becomes full, write requests are blocked until space is freed by read requests.

When the pipe is too full to hold the entire contents of the write request, the portion that will fit is written to the pipe before the writer is blocked.

To read from a pipe, call DosRead, specifying the read handle of the pipe in DosRead's file handle parameter. Subsequent calls to DosRead can empty the pipe if no further calls to DosWrite are made in the meantime.

Different threads writing to and reading from a pipe are not synchronized. It is possible for the pipe reader to obtain partial contents of a write request if the pipe becomes full during the write request.

If the process reading the pipe ends, the next DosWrite request for that pipe returns ERROR_BROKEN_PIPE.

Calling DosClose terminates access to an unnamed pipe. However, the pipe is not deleted from memory until all handles to the pipe have been closed, including any handles that were defined with DosDupHandle.

Redirecting Standard I/O for Child Processes

An application can use unnamed pipes to redirect the standard input and the standard output for a child process.

A typical use of an unnamed pipe is to read the output of a child process. An application creates a pipe and then duplicates the standard output handle. When the child process is started, its standard output will be written into the pipe, where the application can read and display it. The following code fragment shows how to do this:

    #define INCL_DOSPROCESS
    #define INCL_DOSNMPIPES

    #include <os2.h>

    #define PIPESIZE 256
    #define HF_STDOUT 1      /* Standard output handle */

    HPIPE hpR, hpW;
    RESULTCODES resc;
    ULONG ulRead, ulWritten;
    CHAR achBuf[PIPESIZE],
         szFailName[CCHMAXPATH];

    HFILE hfSave = -1,
          hfNew = HF_STDOUT;

    DosDupHandle(HF_STDOUT,
                 &hfSave); /* Saves standard output handle      */

    DosCreatePipe(&hpR,
                  &hpW,
                  PIPESIZE); /* Creates pipe                      */

    DosDupHandle(hpW,
                 &hfNew); /* Duplicates standard output handle */


    DosExecPgm(szFailName,
               sizeof(szFailName), /* Starts child process      */
               EXEC_ASYNC,
               (PSZ) NULL,
               (PSZ) NULL,
               &resc,
               "DUMMY.EXE");

    DosClose(hpW);                /* Closes write handle to ensure     */
                                  /* Notification at child termination */

    DosDupHandle(hfSave,
                 &hfNew);     /* Brings stdout back                */

    /*
     * Read from the pipe and write to the screen
     * as long as there are bytes to read.
     *
     */

    do {
        DosRead(hpR,
                achBuf,
                sizeof(achBuf),
                &ulRead);

        DosWrite(HF_STDOUT,
                 achBuf,
                 ulRead,
                 &ulWritten);

    } while(ulRead);

A parent process can also use unnamed pipes to communicate with a child process by redirecting both the standard input and the standard output for the child process. To do this, the parent process:

1.Uses DosDupHandle to redefine the read handle of one pipe as standard input (0000), and the write handle of the other pipe as standard output (0001). 2.Starts the child process with DosExecPgm. 3.Uses the remaining pipe handles to read and write to the pipes.

The parent process controls the meanings for standard I/O for the child process. Thus, when the child process uses standard I/O handles with DosRead and DosWrite, it reads from and writes to the pipes of its parent instead of reading from the keyboard and writing to the display.

Using Named Pipes

Named pipes are useful in applications that transfer data between processes. The processes using named pipes can be related, unrelated, or even on different computers.

Creating Named Pipes

The server process creates a named pipe by using DosCreateNPipe.

To create a named pipe, specify on the DosCreateNPipe function:

  • The name of the pipe
  • The access modes
  • The type of pipe (byte or message)
  • The sizes of the input and output buffers

DosCreateNPipe returns a pipe handle that can be used in subsequent pipe operations.

Each named pipe must have a unique name of the following form:

   \PIPE\PipeName


The "\PIPE\" in the name above is required, but need not be uppercase. It is not the name of a subdirectory.

To open a pipe on a remote computer, the client process must specify the name of the server process that opened the pipe as part of the pipe name, as follows:

   \\Server\PIPE\PipeName


"\\Server" in the name above is the name of the remote computer; again, "\PIPE\" is required.

The name parameter must conform to the rules for OS/2 file names, but no actual file is created for the pipe.

Named pipes created with certain access modes prevent the named pipes' clients from using certain functions. If the named pipe is created with access mode NP_ACCESS_INBOUND, the named pipe's client cannot use these functions:

  • DosCallNPipe
  • DosTransactNPipe
  • DosPeekNPipe

If the named pipe is created with access mode NP_ACCESS_OUTBOUND, the named pipe's client cannot use these functions:

  • DosCallNPipe
  • DosTransactNPipe
  • DosWrite

If the named pipe's client uses an incorrect function, the function returns error code 5 (ERROR_ACCESS_DENIED).

In the following code fragment, DosCreateNPipe creates a pipe named \pipe\pipe1 and supplies a unique handle identifying the pipe. OpenMode is set to NP_ACCESS_DUPLEX. This activates full duplex access to the named pipe. There will be no inheritance to child process, and no write-through (write-through only affects remote pipes). PipeMode is set to "NP_WMESG | NP_RMESG | 0x01". This specifies that the pipe should be read as a message stream for both reading and writing and an instance count of 1 (only one instance of the named pipe can be created at a time). The pipe will block on Read/Write if no data is available.

    #define INCL_DOSNMPIPES   /* Named-pipe values */
    #include <os2.h>
    #include <stdio.h>

    UCHAR   ucFileName[40];  /* Pipe name              */
    HPIPE   hpPipeHandle;    /* Pipe handle (returned) */
    ULONG   ulOpenMode;      /* Open-mode parameters   */
    ULONG   ulPipeMode;      /* Pipe-mode parameters   */
    ULONG   ulOutBufSize;    /* Size of the out-buffer */
    ULONG   ulInBufSize;     /* Size of the in-buffer  */
    ULONG   ulTimeOut;       /* Default value for      */
                             /* DosWaitNPipe time-out  */
                             /* parameter              */
    APIRET  ulrc;            /* Return code            */

    strcpy(ucFileName,
           "\\PIPE\\PIPE1");

    ulOpenMode = NP_ACCESS_DUPLEX;            /* Full duplex, no inheritance,     */
                                              /* no write-through                 */

    ulPipeMode = NP_WMESG |
                 NP_RMESG |
                 0x01;     /* Block on read and write, message                   */
                           /* stream, instance count of 1                        */

    ulOutBufSize = 4096;   /* The outgoing buffer must be 4KB in size            */

    ulInBufSize = 2048;    /* The incoming buffer must be 2KB in size            */

    ulTimeOut = 10000;     /* Time-out is 10 seconds (units are in milliseconds) */

    ulrc = DosCreateNPipe(ucFileName,
                          &hpPipeHandle,
                          ulOpenMode,
                          ulPipeMode,
                          ulOutBufSize,
                          ulInBufSize,
                          ulTimeOut);

    if (ulrc != 0) {
        printf("DosCreateNPipe error: return code = %ld",
               ulrc);
        return;
    }

Once the named pipe is created, the application can call DosConnectNPipe to connect a client process to the pipe.

Once a client process connects to the pipe, the process can read from and write to the pipe. The preceding example creates a byte pipe, so the process can use DosRead and DosWrite to read from and write to the pipe.

After the client process finishes using the pipe, the server process can disconnect the pipe by using DosDisConnectNPipe. The server process can either connect again or close the named pipe by using DosClose.

When a server process creates a named pipe, it defines the pipe to the system by specifying the file write-through mode, the inheritance mode, the access and blocking modes, the pipe type, the read mode, the size of the in and out buffers, and the instance count. The following list describes these modes, types, and buffers.

  • The file write-through mode has significance only for communication with remote client processes. When the file write-through bit is set, data is sent across the network as soon as it is written; otherwise, OS/2 will in some cases hold data briefly in a local buffer before sending it across the network.
  • The inheritance mode specifies whether or not the pipe handle will be inherited by a child process.
  • The access mode specifies the direction in which data will flow through the pipe. The server creates an inbound pipe (a pipe with inbound access mode) if it intends to read data from the client process, an outbound pipe if it intends to write data to the client process, or a duplex pipe if it intends to both read from and write to the client process.
  • The blocking mode specifies whether or not DosRead and DosWrite will block when no data is available to read, or there is no room to write data.
  • The pipe type is the form in which a stream of data is written to the pipe. If the pipe is a byte pipe, the server and client processes write data as an undifferentiated stream of bytes. If the pipe is a message pipe, the processes write data as a stream of messages; messages are blocks of data, each with a system-supplied header, that are written as single units. The server and client processes define the size and format of a message.
  • The read mode is the form in which data is read from the pipe. The data in a pipe that was created as a byte pipe can only be read as bytes; therefore, a byte pipe will always be in byte-read mode. The data in a message pipe, however, can be read either as messages or as bytes. (If it is to be read as bytes, DosRead skips over the message headers). Therefore, message pipes can be in either message-read mode or byte-read mode.
Note
The terms "byte pipe" and "message pipe" always refer to the pipe type-the form in which data is written to the pipe. When the read mode of a pipe is being referred to, it is always explicitly identified as either message-read mode or byte-read mode.
  • The in and out buffers can be up to 64KB in size. If the pipe will be read in message-read mode, and if the message size is known, the server can control how many messages the buffer will hold at one time by specifying the appropriate buffer size.
  • The instance count is the maximum number of instances of the named pipe that can be created. A pipe instance is actually a separate pipe-that is, a unique set of pipe buffers with unique handles. However, the term "pipe instance" is used to distinguish pipes that share the same name from pipes with different names. Because a client process uses only the name of the pipe when opening it, the existence of multiple pipe instances is transparent to a client process.
Creating Multiple Instances of a Named Pipe

Although each named pipe must have a unique name, a server process can create multiple instances of a pipe, all of which have the same name. A pipe instance is actually a separate pipe-that is, a unique set of pipe buffers with unique handles.

The ability to create multiple pipe instances enables the server to communicate with multiple client processes at the same time. Because a client process uses only the name of the pipe when opening it, the existence of multiple pipe instances is transparent to a client process.

The ICount parameter of DosCreateNPipe specifies the maximum number of named pipe instances that can be created. (An unlimited number can also be specified.) This parameter is specified only when the first instance of a named pipe is created; any subsequent attempt to redefine the instance count will be ignored.

If the instance count is greater than 1, the server process can create additional pipe instances by specifying the same pipe name in subsequent calls to DosCreateNPipe. Generally, the attributes of the subsequent pipe instances are defined to be the same as those of the original pipe instance, because a client process that requests the pipe has no way of controlling which pipe instance will be assigned to it.

After an additional pipe instance has been created, it is used in the same manner as the original pipe instance. That is, the same sequence of named pipe functions is used in the control or management of all named pipe instances. (See Steps in Managing Server-Client Transactions for more information.)

Note
If all of the instances of a named pipe are in use when a client calls DosOpen, ERROR_PIPE_BUSY is returned. However, the client can wait for an instance of that pipe to become available by calling DosWaitNPipe.

Multiple instances of a named pipe can be created by different processes. That is, multiple server processes can create and use instances of the same named pipe to communicate with their clients.


Opening Named Pipes

Reading from Named Pipes

Writing to Named Pipes

Synchronizing Named Pipe Dialogs

Determining Pipe Status

Examining the Contents of Named Pipes

Closing Named Pipes