现在的位置: 首页 > 综合 > 正文

Windows & Unix IPC Introduction (Part.1)

2013年07月11日 ⁄ 综合 ⁄ 共 8942字 ⁄ 字号 评论关闭

1. Introduction

Interprocess communication (IPC) refers to the coordination of activities among cooperating processes. A common example of this need is managing access to a
given system resource. To carry out IPC, some form of active or passive communication is required.

 

The increasingly important role that distributed systems play in modern computing environments exacerbates the need for IPC. Systems for managing communication
and synchronization between cooperating processes are essential to many modern software systems. IPC has always played a prominent role in UNIX-variant operating systems, but it has been largely overlooked for systems running the Windows NT operating system.
This paper will discuss some of the IPC options that are available to programmers using UNIX and describe the corresponding techniques available to programmers writing for Windows NT. Features and mechanisms specific to Windows NT will also be discussed. The
conclusion will offer a summary of the available techniques as applicable to Windows NT.

 

2. Pipes

Pipes are a simple synchronized way of passing information between two processes. A pipe can be viewed as a special file that can store only a limited amount
of data and uses a FIFO access scheme to retrieve data. In a logical view of a pipe, data is written to one end and read from the other. The processes on the ends of a pipe have no easy way to identify what process is on the other end of the pipe.

 

The system provides synchronization between the reading and writing process. It also solves the producer/consumer problem: writing to a full pipe automatically
blocks, as does reading from an empty pipe. The system also assures that there are processes on both ends of the pipe at all time. The programmer is still responsible, however, for preventing deadlock between processes. Pipes come in two varieties:

 

ü 
Unnamed.
Unnamed pipes can only be used by related processes (i.e. a process and one of its child processes, or two of its children). Unnamed pipes cease
to exist after the processes are done using them.

ü 
Named.
Named pipes exist as directory entries, complete with permissions. This means that they are persistent and that unrelated processes can use them.

 

2.1 UNIX

Most UNIX systems limit pipes to 5120K (typically ten 512K chunks). The unbuffered system call write() is used to add data to a pipe. Write() takes a file descriptor
(which can refer to the pipe), a buffer containing the data to be written, and the size of the buffer as parameters. The system assures that no interleaving will occur between writes, even if the pipeline fills temporarily. To get data from a pipe, the read()
system call is used. Read() functions on pipes much the same as it functions on files. However, seeking is not supported and it will block until there is data to be read.

 

The pipe() system call is used to create unnamed pipes in UNIX. This call returns two pipes. Both support bidirectional communication (two pipes are returned
for historical reasons: at one time pipes were unidirectional so two pipes were needed for bidirectional communication). In a fullduplex environment (i.e. one that supports bidirectional pipes) each process reads from one pipe and writes to the other; in a
half-duplex (i.e. unidirectional) setting, the first file descriptor is always used for reading and the second for writing.

 

Pipes are commonly used on the UNIX command line to send the output of one process to another process as input. When a pipe is used both processes run concurrently
and there is no guarantee as to the sequence in which each process will be allowed to run. However, since the system manages the producer/consumer issue, both proceed per usual, and the system provides automatic blocking as required.

 

Using unnamed pipes in a UNIX environment normally involves several steps:

ü 
Create the pipe(s) needed

ü 
Generate the child process(es)

ü 
Close/duplicate the file descriptors to associate the ends of the pipe

ü 
Close the unused end(s) of the pipe(s)

ü 
Perform the communication

ü 
Close the remaining file descriptors

ü 
Wait for the child process to terminate

 

To simplify this process, UNIX provides two system calls that handle this procedure. The call popen() returns a pointer to a file after accepting a shell command
to be executed as input. Also given as input is a type flag that determines how the returned file descriptor will be used. The popen() call automatically generates a child process, which exec()s a shell and runs the indicated command. Depending on the flag
passed in, this command could have either read or write access to the file. The pclose() call is used to close the data stream opened with popen(). It takes the file descriptor returned by popen() as its only parameter.

 

Named pipes can be created on the UNIX command line using mknod, but it is more interesting to look at how they can be used programatically. The mknod() system
call, usable only by the superuser, takes a path, access permissions, and a device (typically unused) as parameters and creates a pipe referred to by the user-specified path. Often, mkfifo() will be provided as an additional call that can be used by all users
but is only capable of making FIFO pipes.

 

2.2 Windows NT

Windows NT supports both named and unnamed pipes, although it refers to the latter as anonymous pipes.

 

The CreatePipe() function creates an anonymous pipe and returns two handles. One handle is a read handle to the pipe and the other is a write handle to the pipe;
neither pipe can perform the opposite operation. When the pipe is created the server requests that it be implemented using a programmer-defined buffer size. To communicate using the pipe, the server must pass one of the handles to another process. Usually,
this is done through inheritance; that is, the process allows the handle to be inherited by a child process. The process can also send the handle to an unrelated process using another form of interprocess communication, such as shared memory.

 

A server can send either the read handle or the write handle to the pipe client, depending on whether the client should use the anonymous pipe to send information
or receive information. To read from the pipe, the pipe's read handle is used as a parameter to the call ReadFile(). The ReadFile() call returns when another process has written to the pipe. ReadFile() call can also return if all write handles to the pipe
have been closed or if an error occurs before the read operation has been completed.

 

To write to the pipe, the pipe's write handle is passed as a parameter to the WriteFile() function. The WriteFile() call does not return until it has written
the specified number of bytes to the pipe or an error occurs. If the pipe buffer is full and there are more bytes to be written, WriteFile() does not return until another process reads from the pipe, which frees up buffer space.

 

Asynchronous read and write operations are not supported by anonymous pipes. An anonymous pipe exists until all pipe handles, both read and write, have been
closed. A process can close its pipe handles by using the CloseHandle() function. All pipe handles are also closed when the process terminates.

 

Named pipes in Windows NT have a unique name that distinguishes them from other named pipes in the system's list of named objects. A server specifies a name
for a pipe when it creates it by calling CreateNamedPipe(). Clients specify the pipe name when they call CreateFile() to connect to an instance of the named pipe.

 

Like filenames in Windows NT, the form //ServerName/pipe/PipeName should be used when specifying the name of a pipe to any of the pipe functions. Here, the server
could be a remote computer or the local host, which can be specified using a period. Pipes cannot be created on remote machines, but they can be used on remote machines once created.

 

The first time a pipe server calls CreateNamedPipe(), it specifies the maximum number of instances of the pipe that can exist simultaneously. The server can
call CreateNamedPipe() repeatedly to create additional instances of the pipe, as long as it does not exceed the maximum number of instances. If the function succeeds, each call returns a handle to the server end of a named pipe instance.

 

As soon as the pipe server creates a pipe instance, a pipe client can connect to it by calling CreateFile() or CallNamedPipe(). If a pipe instance is available,
CreateFile() returns a handle to the client end of the pipe instance. If no instances of the pipe are available, a pipe client can use the WaitNamedPipe() function to wait until a pipe becomes available. A server can determine when a client is connected to
a pipe instance by calling ConnectNamedPipe(). If the pipe handle is in blocking-wait mode, ConnectNamedPipe() does not return until a client is connected.

 

When a client and server finish using a pipe instance, the server should first call

FlushFileBuffers(), to ensure that all bytes or messages written to the pipe are read by the client. FlushFileBuffers() does not return until the client has
read all data from the pipe. The server then calls DisconnectNamedPipe() to close the connection to the pipe client. This function makes the client's handle invalid, if it has not already been closed. Any unread data in the pipe is discarded. After the client
is disconnected, the server calls the CloseHandle() function to close its handle to the pipe instance. Alternatively, the server can use ConnectNamedPipe() to enable a new client to connect to this instance of the pipe.

 

Several functions unique to Windows NT are available for working with pipes. The call

PeekNamedPipe() can be used to read from a pipe without removing data from it. The

TransactNamedPipe() function writes a request message and reads a reply message in a single operation, enhancing network performance. It can be used with duplex
pipes if the pipe handle of the calling process is set to read mode. A process can retrieve information about a named pipe by calling GetNamedPipeInfo(), which returns the type of the pipe, the size of the input and output buffers, and the maximum number of
pipe instances that can be created.

 

GetNamedPipeHandleState() reports on the read and wait modes of a pipe handle, the current number of pipe instances, and additional information for pipes that
communicate over a network. SetNamedPipeHandleState() sets the read mode and wait modes of a pipe handle. For pipe clients communicating with a remote server, the function also controls the maximum number of bytes to collect or the maximum time to wait before
transmitting a message (assuming the client's handle was not opened with write-through mode enabled).

抱歉!评论已关闭.