Multiprocess Programming in C++

Multiprocess Programming in C++

In modern computing, efficiency is at the core of everything. Multiprocess programming is a way to achieve efficient concurrency. This article will introduce multiprocess programming in C++, including the basics of creating, managing, and inter-process communication (IPC).

What is Multiprocessing?

Multiprocessing refers to the simultaneous execution of multiple independent programs in an operating system. These programs can share system resources, but each has its own memory space. Compared to threads, processes are more independent, thus providing better fault isolation. However, since each process requires its own memory space, multiprocessing can sometimes lead to higher overhead.

Creating a New Process

In C++, we can use the <span>fork()</span> function to create a new process. The <span>fork()</span> function duplicates the currently running process, generating a child process. Here is a simple example:

#include <iostream>
#include <unistd.h>
int main() {    pid_t pid = fork(); // Create a new child process
    if (pid < 0) {        std::cerr << "Fork failed!" << std::endl;        return 1; // Return if failed    }
    if (pid == 0) {        // Child process        std::cout << "This is the child process. PID: " << getpid() << std::endl;    } else {        // Parent process        std::cout << "This is the parent process. Child PID: " << pid << std::endl;    }
    return 0;}

Program Analysis

  1. Include Header Files: We included <span><iostream></span> and <span><unistd.h></span>. The former is used for input and output streams, while the latter provides access to POSIX operating system APIs.
  2. Call <span>fork()</span>: This function is used to create a new child process.
  3. Error Handling: If <span>fork()</span> returns a value less than zero, it indicates an error.
  4. Distinguish Between Parent and Child Processes:
  • In the child process (<span>pid == 0</span>), you can execute logic specific to that process.
  • In the parent process, you will receive the PID of the child.

Using wait() to Prevent Zombie Processes

When a child process ends, if the parent process does not call <span>wait()</span> to retrieve its exit status, the child process will become a “zombie” and occupy system resources. This usually occurs when the parent fails to manage the related IDs and information without reusing/recycling them. To prevent these zombies from existing, you should use the <span>wait()</span> function:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {    pid_t pid = fork();
    if (pid > 0) {       int status;       wait(&status); // Wait for the child process to finish and get its exit status
      if(WIFEXITED(status)) {           std::cout<< "Child exited with status code: "<< WEXITSTATUS(status)<<std::endl;        }
      std::cout<<"Parent Process, PID : "<<getpid()<<std::endl ;  
   } else if (pid == 0) {        sleep(1); // Sleep for a while to simulate workload       exit(42); // Return code 42 when the child terminates
   } 
return 0; }

Program Analysis

  1. Add Header Files <span><sys/types.h></span> and <span><sys/wait.h></span> to support wait operations.
  2. In the parent process, we call <span>wait(&status)</span> to block itself until a child process terminates and to retrieve the termination status of that child;
  3. Use the macro <span>WIFEXITED(status)</span> to check if the child terminated normally, and use <span>WEXITSTATUS(status)</span> to get the value returned by the child.

Inter-Process Communication (IPC)

In practical applications, processes often need to exchange data. IPC provides several mechanisms, such as pipes, queues, and shared memory. This example will demonstrate how to achieve effective communication through pipes:

#include <iostream>  
#include <unistd.h>  
#define BUFFER_SIZE 20  
int main(){          int pipefd[2];             char buf[BUFFER_SIZE];  
     pipe(pipefd); // Create a pipe
     pid_t pid = fork(); 
     if (pid > 0){         close(pipefd[0]); 
         const char* msg = "Hello from parent!";         write(pipefd[1], msg, strlen(msg)); 
         close(pipefd[1]); 
	} else{    		close(pipefd[1]);  
           read(pipefd[0], buf, sizeof(buf));          		printf("Received in child: %s\n", buf);  		close(pipefd[0]);	}
	return 0; }   

Program Analysis

  • We defined a constant for buffer size and an array to receive messages;
  • Called <span>pipe()</span> to create a pair of file descriptors for IPC;
  • Then, based on the derived PID, we decide to close the unused end, which normalizes the opening of read or write permissions;
  • Finally, read and display the message content from the parent, and if feasible, further explore more IPC methods such as semaphores and message queues, etc.;

Conclusion

This article briefly introduced how to perform multiprocess programming in C++ using functions like fork and wait, as well as how to implement IPC communication through pipes. I encourage you to explore more features and techniques to fully utilize your CPU and adopt better strategies for complex tasks.

Leave a Comment