Inter-process communication (IPC) is a crucial part of any Linux system, allowing different processes to communicate and share data with each other.
IPC is a method that allows processes to communicate and exchange data.
IPCS is a command-line tool that allows you to view information about the IPC facilities that the calling process has read access to.
It provides details about all three primary IPC resources: shared memory segments, message queues, and semaphore arrays.
The IPCS command offers various options to control the information displayed.
-a : Use all print options.
Listing All IPC Facilities: You can list all IPC facilities that the current process has read access to using the -a option.
Listing All Semaphores: To list all the currently accessible semaphore arrays, use the -s flag.
Listing Shared Memory: You can view the shared memory on your system using the -m flag.
Viewing IPC Facility Limit: Each IPC facility has a limit.
You can check this using the -l option combined with the flag for the desired facility.
Viewing Owner Details and Usage Status: Using the -c and -uoptions respectively, you can view the owner details and current usage status of any IPC facility.
You can specify a specific facility by adding its flag.
Displaying Time Information: To view the last accessed time for a specific facility, add its flag to the -t option.
The IPCS command is a powerful tool for interacting with IPC facilities on your Linux system.
With it, you can view a wealth of information about active message queues, shared memory segments, semaphore sets, and more.
By understanding and effectively using IPCS, you can gain a deeper understanding of how processes on your system communicate and share data.
Inter-process communication (IPC) is a fundamental concept in UNIX-like operating systems like Linux that enables processes to exchange data.
Here's an overview of the key IPC mechanisms.
These IPC mechanisms are sometimes combined to enable efficient process communication.
Have you seen examples of these being used together in an application?
The socket example, with request messages echoed back to the client, hints at the possibilities of arbitrarily rich conversations between the server and the client.
Perhaps this is the chief appeal of sockets.
It is common on modern systems for client applications (e.g., a database client) to communicate with a server through a socket.
As noted earlier, local IPC sockets and network sockets differ only in a few implementation details; in general, IPC sockets have lower overhead and better performance.
IPC sockets (aka Unix domain sockets) enable channel-based communication for processes on the same physical device (host), whereas network sockets enable this kind of IPC for processes that can run on different hosts, thereby bringing networking into play.
By contrast, IPC sockets rely upon the local system kernel to support communication; in particular, IPC sockets communicate using a local file as a socket address.
Despite these implementation differences, the IPC socket and network socket APIs are the same in the essentials.
Sockets configured as streams (discussed below) are bidirectional, and control follows a client/server pattern: the client initiates the conversation by trying to connect to a server, which tries to accept the connection.
An iterative server, which is suited for development only, handles connected clients one at a time to completion: the first client is handled from start to finish, then the second, and so on.
The downside is that the handling of a particular client may hang, which then starves all the clients waiting behind.
A production-grade server would be concurrent, typically using some mix of multi-processing and multi-threading.
Example 1. }The server program above performs the classic four-step to ready itself for client requests and then to accept individual requests.
0); /* system picks protocol (TCP) */The first argument specifies a network socket as opposed to an IPC socket.
There are several options for the second argument, but SOCK_STREAM and SOCK_DGRAM (datagram) are likely the most used.
A stream-based socket supports a reliable channel in which lost or altered messages are reported; the channel is bidirectional, and the payloads from one side to the other can be arbitrary in size.
By contrast, a datagram-based socket is unreliable (best try), unidirectional, and requires fixed-sized payloads.
The third argument to socket specifies the protocol.
For the stream-based socket in play here, there is a single choice, which the zero represents: TCP.
The bind call is the most complicated, as it reflects various refinements in the socket API.
The point of interest is that this call binds the socket to a memory address on the server machine.
if (listen(fd, MaxConnects) < 0)
The first argument is the socket's file descriptor and the second specifies how many client connections can be accommodated before the server issues a connection refused error on an attempted connection.
The accept call defaults to a blocking wait: the server does nothing until a client attempts to connect and then proceeds.
The accept function returns -1 to indicate an error.
If the call succeeds, it returns another file descriptor-for a read/write socket in contrast to the accepting socket referenced by the first argument in the accept call.
The server uses the read/write socket to read requests from the client and to write responses back.
By design, a server runs indefinitely.
Example 2.
if (!hptr) report("gethostbyname", 1); /* is hptr NULL? /* Write some stuff and read the echoes.
The client program's setup code is similar to the server's.
if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)
The connect call might fail for several reasons; for example, the client has the wrong server address or too many clients are already connected to the server.
If the connect operation succeeds, the client writes requests and then reads the echoed responses in a for loop.
After the conversation, both the server and the client close the read/write socket, although a close operation on either side is sufficient to close the connection.
A signal interrupts an executing program and, in this sense, communicates with it.
Most signals can be either ignored (blocked) or handled (through designated code), with SIGSTOP (pause) and SIGKILL (terminate immediately) as the two notable exceptions.
Signals can arise in user interaction.
For example, a user hits Ctrl+C from the command line to terminate a program started from the command-line; Ctrl+C generates a SIGTERM signal.
SIGTERM for terminate, unlike SIGKILL, can be either blocked or handled.
Consider how a multi-processing application such as the Nginx web server might be shut down gracefully from another process.
int kill(pid_t pid, int signum); /* declaration */can be used by one process to terminate another process or group of processes.
The second argument to kill is either a standard signal number (e.g., SIGTERM or SIGKILL) or 0, which makes the call to signal a query about whether the pid in the first argument is indeed valid.
The graceful shutdown of a multi-processing application thus could be accomplished by sending a terminate signal-a call to the kill function with SIGTERM as the second argument-to the group of processes that make up the application.
Example 3. /* Try to terminate child. }
The shutdown program above simulates the graceful shutdown of a multi-processing system, in this case, a simple one consisting of a parent process and a single child process.
The parent process tries to fork a child.
The child process goes into a potentially infinite loop in which the child sleeps for a second, prints a message, goes back to sleep, and so on.
It is precisely a SIGTERM signal from the parent that causes the child to execute the signal-handling callback function graceful.
The signal thus breaks the child process out of its loop and sets up the graceful termination of both the child and the parent.
The parent process, after forking the child, sleeps for five seconds so that the child can execute for a while; of course, the child mostly sleeps in this simulation.
My child terminated, about to exit myself...
For the signal handling, the example uses the sigaction library function (POSIX recommended) rather than the legacy signal function, which has portability issues.
If the call to fork succeeds, the parent executes the parent_code function and the child executes the child_code function.
The child_code function first calls set_handler and then goes into its potentially infinite sleeping loop.
} The first three lines are preparation.
The fourth statement sets the handler to the function graceful, which prints some messages before calling _exit to terminate.
The fifth and last statement then registers the handler with the system through the call to sigaction.
Using signals for IPC is indeed a minimalist approach, but a tried-and-true one at that.
Even today, when thread-centric languages such as Java, C#, and Go have become so popular, IPC remains appealing because concurrency through multi-processing has an obvious advantage over multi-threading: every process, by default, has its own address space, which rules out memory-based race conditions in multi-processing unless the IPC mechanism of shared memory is brought into play.
(Shared memory must be locked in both multi-processing and multi-threading for safe concurrency.)
Anyone who has written even an elementary multi-threading program with communication via shared variables knows how challenging it can be to write thread-safe yet clear, efficient code.
There is no simple answer, of course, to the question of which among the IPC mechanisms is the best.
Each involves a trade-off typical in programming: simplicity versus functionality.
Signals, for example, are a relatively simple IPC mechanism but do not support rich conversations among processes.
If such a conversion is needed, then one of the other choices is more appropriate.
Shared files with locking is reasonably straightforward, but shared files may not perform well enough if processes need to share massive data streams; pipes or even sockets, with more complicated APIs, might be a better choice.

IPCS is a powerful tool for interacting with IPC facilities on your Linux system.
With it, you can view a wealth of information about active message queues, shared memory segments, semaphore sets, and more.
Listing All IPC Facilities: You can list all IPC facilities that the current process has read access to using the -a option.
Listing All Semaphores: To list all the currently accessible semaphore arrays, use the -s flag.
Listing Shared Memory: You can view the shared memory on your system using the -m flag.
Viewing IPC Facility Limit: Each IPC facility has a limit.
You can check this using the -l option combined with the flag for the desired facility.
Viewing Owner Details and Usage Status: Using the -c and -uoptions respectively, you can view the owner details and current usage status of any IPC facility.
You can specify a specific facility by adding its flag.
Displaying Time Information: To view the last accessed time for a specific facility, add its flag to the -t option.
| Opcja IPCS | Co pokazuje |
|---|---|
| -a | Use all print options; lista wszystkich IPC facilities z prawem odczytu bieżącego procesu |
| -s | Listing All Semaphores: wszystkie dostępne tablice semaforów |
| -m | Listing Shared Memory: segmenty pamięci współdzielonej w systemie |
| -l + flaga | Viewing IPC Facility Limit: limit dla wybranej klasy obiektów IPC |
| -c + flaga | Viewing Owner Details: szczegóły właściciela danej instancji IPC |
| -u + flaga | Viewing Usage Status: bieżący status użycia dla danej instancji IPC |
| -t + flaga | Display czasu: last accessed time dla wybranej instancji |
IPC remains appealing because concurrency through multi-processing has an obvious advantage over multi-threading: every process, by default, has its own address space, which rules out memory-based race conditions in multi-processing unless the IPC mechanism of shared memory is brought into play.
(Shared memory must be locked in both multi-processing and multi-threading for safe concurrency.)
There is no simple answer, of course, to the question of which among the IPC mechanisms is the best.
Each involves a trade-off typical in programming: simplicity versus functionality.
Perhaps this is the chief appeal of sockets.
Signals, for example, are a relatively simple IPC mechanism but do not support rich conversations among processes.
If such a conversion is needed, then one of the other choices is more appropriate.
Shared files with locking is reasonably straightforward, but shared files may not perform well enough if processes need to share massive data streams; pipes or even sockets, with more complicated APIs, might be a better choice.
These IPC mechanisms are sometimes combined to enable efficient process communication.
About the author