Click here to Skip to main content
Click here to Skip to main content

Unleashing anonymous pipes – Part 1

By , 19 Aug 2003
Rate this:
Please Sign up or sign in to vote.

Introduction

Inter Process Communication (IPC) is a mechanism through which different processes interact and share data between themselves. The applications, which use IPC for interaction can be divided into two categories. They are the server application and the client application. The server application provides the services to the client application, which has requested the set of services from the server application. There are the various IPC mechanism which are supported by the WIN32 SDK. Pipes is one of those mechanisms. Pipes allow the transfer of data between processes in a FIFO (first-in-first-out) manner. The application, which creates pipe, is called the pipe server and the application, which connects, to the pipe is called a pipe client. The FIFO manner means that the processes read the data in a same order in which it was written i.e. the data which was written first will be read first by the processes. Once the data is read from the pipe, the data is removed from the pipe and hence makes a space available for the process, which is writing on the pipe.

The WIN32 SDK provides two flavours of Pipes: Anonymous pipe and Named pipes.

An anonymous pipe is a one-way communication conduit, which transfers the data between the related processes i.e. between parent and child process. The anonymous pipe doesn’t support communications over the network. Anonymous pipes are always byte-stream oriented.

A named pipe provides one way or two-way communication between the pipe server and pipe client. It can be used to interact between the processes, which may be unrelated, or on different machines on a network. We will cover the implementation of named pipe in the second article of this series. This article will be covering the practical implementation of anonymous pipe along with a sample code for each implementation.

There are the following cases in which the implementation of anonymous pipes can be explored in providing the communication conduit:

  • To explore how it can act as a channel between the writer thread and the reader thread where both threads belong to the same process.
  • To explore how it can act as a channel between the parent and a child process. It uses the concept of handle inheritance.
  • To explore how it can act as a channel between related and unrelated processes by using the concept of file mapping objects. This will be covered in PART-2 of the article.
  • To explore the use of overlapped I/O operation in anonymous pipe. Windows 95/98 doesn’t supports the overlapped I/O operations on pipes, mailslots and files. This too will be covered in PART-2 of the article.

Intra-process Communication

This mode of communication is within a process; in which one thread acts as a writer thread and another thread act as a reader thread. Every process has at least a one thread and that main thread is a writer thread in our code sample. The main thread spawns a new thread and passes a handle to the read end of a pipe and a handle to the writer thread to the reader thread.The main thread creates the unnamed pipe by calling CreatePipe and gets the handle to the read and the write ends of the pipe. The last parameter to CreatePipe specifies the buffer size, which has been allocated for a pipe. There is a need to maintain the synchronization between the two threads i.e. when the reader thread is reading the data, the writer thread should sleep and as soon as the reader threads reads all the data from the pipe, it awakes the writer thread so that it can write to the empty pipe. The duration for which the writer thread will write on a pipe, the reader thread will be in sleep state. The same is for the writer thread. Once the pipe gets full, the writer thread will awakes the reader thread to read the pipe and when the reader pipe will be reading the pipe, the writer thread will go for a sleep. To maintain the toggle between the two threads, the both threads should have each other’s handle so that they resume the other thread before going into a suspended state. This has been achieved by structure ThreadData.
// Code Snippet.

struct {
HANDLE hWrThread;
HANDLE hRdPipe;
};

// hWrThread: This is a handle to the writer thread which will 
// be passed to the reader thread at the time of its spawning.
// hRdPipe : This is a handle to the read end of a unnamed pipe.

ThreadData thdData;
// Creates an unnamed pipe.
bReturn = CreatePipe(&hReadPipe,&hWritePipe,NULL,PIPE_BUFFERLENGTH);

thdData.hRdPipe = hReadPipe;
hWrThread = GetCurrentThread();

bReturn = DuplicateHandle(GetCurrentProcess(),hWrThread,
GetCurrentProcess(),&hWrDupl,NULL,FALSE,DUPLICATE_SAME_ACCESS);
thdData.hWrThread = hWrDupl;

// Creates a reader thread, which act as a pipe client.
hRdThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,
(LPVOID)&thdData,CREATE_SUSPENDED,&dwThreadID);

In this code snippet, the main thread creates the unnamed pipe by calling CreatePipe. This function returns the handle to the read and the write end of the pipe. The read end of a pipe is stored in the data member (hRdPipe) of a ThreadData structure. The writer thread gets it own handle by calling GetCurrentThread and to get a compatible handle, which could be used by another threads, it calls DuplicateHandle with the pseudo handle as an argument to it. Once we get the duplicate handle to the writer thread, it is stored in the data member of a ThreadData structure. The ThreadData structure is passed to the reader thread as one of the arguments to the CreateThread function. Each thread maintains the toggle between the two threads by calling the ResumeThread and SuspendThread function on an appropriate thread’s handle. The reader thread reads the data from the pipe by calling the ReadFile function. The ReadFile call will read data from the pipe and once the data is read from the pipe, the data is removed from the pipe. If the pipe is empty, then the ReadFile function will go into a sleep state and wait until data is written into the pipe by the writer thread. The operating system awakes the reader thread as soon as the writer thread writes data on the pipe.The writer thread writes the data on the pipe by calling WriteFile function. The WriteFile function will write the data into a pipe till the entire buffer size allocated for the pipe gets consumed. Once that happens, the writer thread will go to sleep and waits for the reader thread to remove the data from the pipe, so that it can write again on the pipe. The main thread (i.e. writer thread) can wait for the reader thread by calling a wait function called WaitForSingleObject function.

Inter-Process Communication (Master-Slave Model)

This mode of communication represents a parent-child relation. A process that creates or spawns a new process is called the parent and the spawned processes are called its children. The child process, once created, doesn’t depend on the parent process and runs independently. The child process has its own virtual address space, which is independent of the parent’s virtual address space. A child process can inherit the open handles from its parent process. An inherited handle in the child process refers to the same object as an original handle in the parent process. When a child process inherits the handle, the operating system provides only access to the child process. As child process has a new virtual address space, therefore the parent process should communicate the handle value to the child process. The handle value can be passed in various ways:
  • By command line argument
  • By file mapping objects
  • By pipes or by standard I/O channels
The handle inheritance causes the child process to have the entries for all the inheritable handles (which are in the open state) in its own object table. The inheritance gives the access on the handle, which is in the parent process, and once the handle value is passed to the child process via some IPC mechanism, the child process can also use that handle. Inheritance works fine for a related processes i.e. which has a parent-child relation. DuplicateHandle can be used to create a handle in a related as well as unrelated process.

An anonymous pipe provides a one-way conduit between the processes. The parent process and child process needs two pipes to exchange their respective data with each other. The parent process creates the two anonymous pipes by calling CreatePipe and get the inheritable handle to the read end and write end to the pipes.

To have a two-way communication with the child process, the parent process provides access to the handle to the read end of pipe2 and handle to the write end of the pipe1 to the child process via handle inheritance. The parent process calls DuplicateHandle on hWrMaster and hRdMaster to create non-inheritable handles and then it closes the inheritable handles. After closing these inheritable handle, the parent process spawns the child process by calling CreateProcess and passes the value of inheritable handle to the read end to the pipe2 and value of handle to the write end to the pipe1 as a command line argument.

// Code Snippet.

// creation of pipe1.
bReturn = CreatePipe(&hRdMaster,&hWrSlave,&secAttr,PIPE_BUFFERLENGTH);
DuplicateHandle(GetCurrentProcess(),
                hRdMaster,
                GetCurrentProcess(),
                &hDuplRdMaster,
                DUPLICATE_SAME_ACCESS,
                FALSE,
                DUPLICATE_SAME_ACCESS
      );

// Closing inheritable handle to the read end to pipe1.
CloseHandle(hRdMaster);

// creation of pipe2.
bReturn = CreatePipe(&hRdSlave,&hWrMaster,&secAttr,PIPE_BUFFERLENGTH);
DuplicateHandle(GetCurrentProcess(),
                hWrMaster,
                GetCurrentProcess(),
                &hDuplWrMaster,
                DUPLICATE_SAME_ACCESS,
                FALSE,
                DUPLICATE_SAME_ACCESS
      );

// Closing inheritable handle to the write end to pipe2.
CloseHandle(hWrMaster);

The Child process retrieves the value to these handles from its command line argument.

// Code Snippet.
// Child process retrieving the values to the inheritable handles.
HANDLE hWrSlave = NULL;      // handle to write end to pipe1.
HANDLE hRdSlave = NULL;      // handle to read end to pipe2.

int laddWrSlave = 0;
int laddRdSlave = 0;
laddWrSlave = atoi(argv[2]);
laddRdSlave = atoi(argv[3]);

cout << "Value of write handle to pipe1: " << laddWrSlave << endl;
cout << "Value of read handle to pipe2 : " << laddRdSlave << endl;
hWrSlave = (void *) laddWrSlave; 
hRdSlave = (void *) laddRdSlave;

After retrieving the handle values, the child process can communicate with the parent process by using the ReadFile and WriteFile functions. The parent process can wait for the child process to terminate by calling WaitForSingleObject function.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Dinesh Ahuja

India India
No Biography provided

Comments and Discussions

 
QuestionExample1 code can NOT finish gracefully PinmemberElecmike2k2-Nov-12 5:54 
GeneralOvercoming Win32 restrictions PinmemberJohnnyfartpants4-Mar-10 18:17 
Questionexample2 code.... PinmemberJohnMcclain7-Aug-07 10:01 
QuestionERROR_INVALID_HANDLE PinmemberZaaN10001-Feb-07 1:14 
QuestionPerformance? Pinmemberstudio_ukc28-Sep-06 14:00 
GeneralSorry Pinmembercaesten4-Apr-06 7:00 
GeneralRe: Sorry PinmemberAyoko Shin8-Aug-06 8:14 
JokeRe: Sorry PinmemberJohn Lomen20-Aug-10 5:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140421.2 | Last Updated 20 Aug 2003
Article Copyright 2003 by Dinesh Ahuja
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid