Concurrent programming using Boost's IPC and MPI libraries

Source: Internet
Author: User
Tags constructor error handling message queue

Concurrent programming with a very popular Boost library is interesting. Boost has several libraries for concurrent programming: the Interprocess (IPC) library is used to implement shared memory, memory mapped I/O, and Message Queuing; The thread library is used to implement portable multithreading; Message passing Interface (MPI) Libraries are used for message passing in distributed computing; The Asio Library is used to implement portable networking with sockets and other low-level features. This article describes the IPC and MPI libraries and some of the features they provide.

This article will learn how to use the Boost IPC Library to implement shared memory objects, Message Queuing, and synchronous file locks. Learn about environment and communicator classes, and how to implement distributed communication by using the Boost MPI library.

Note: The code in this article has been tested with the gcc-4.3.4 and boost-1.45 packages. Common Abbreviations API: Application Programming Interface I/o: input/Output POSIX: Portable Operating System interface for UNIX ® SDK: Software Development Kit

Using the Boost IPC Library

Boost Interprocess is a library of only headers, so all you need to do is include the appropriate header file in your source code and let the compiler know the include path. This is a very good feature; you only need to download the Boost source code (see Resources for a link) and then you can start using it. For example, to use shared memory in your own code, use the include shown in Listing 1.
Listing 1. Boost IPC Library consists of only headers

				
#include <boost/interprocess/shared_memory_object.hpp>
using namespace boost::interprocess; 
... your sources follow ... 

When you pass information to the compiler, you ask the process to modify the include path accordingly, depending on the installation. Then, compile the code:

bash-4.1$  g++ ipc1.cpp–i. /boost_1_45_0

Create a shared Memory object

Let's start with the traditional "Hello world!" program. There are two processes: the first process writes the string "Hello world!" to memory, and the other process reads and displays this string. Create a shared memory object like Listing 2.
Listing 2. Creating a shared Memory object

				
#include <boost/interprocess/shared_memory_object.hpp>

int main (int argc, char* argv[]) 
{
    using namespace using Boost::interprocess; 
    try { 
    //creating our first shared memory object.
    Shared_memory_object sharedmem1 (create_only, "Hello", read_write);

    Setting the size of the shared memory
    sharedmem1.truncate (n);

    ... more code follows
    } catch (interprocess_exception& e) { 
    //...  Clean up 
    } 
}

The type of the Sharedmem1 object is Shared_memory_object (declared and defined in the Boost header file), and its constructor has three parameters: the first parameter-create_only-represents the shared memory object to be created and has not yet been created. If a shared object with the same name already exists, an exception is thrown. For a process that wants to access the shared memory that has already been created, the first parameter should be open_only. The second parameter,-hello-, is the name of the shared memory region. Another process will use this name to access this shared memory. The third parameter,-read_write-, is the access indicator for the shared memory object. Because this process modifies the contents of the shared memory object, the Read_write is used. The process that reads data only from shared memory uses the READ_ONLY indicator.

The Truncate method sets the size of the shared memory in bytes. It is best to put the code in the Try-catch block. For example, if a shared memory object cannot be created, an exception of type boost::interprocess_exception is thrown.

Write data using shared memory objects

Processes that use shared memory objects must map objects in their own address space. The mapping is performed using the Mapped_region class declared and defined in the header file MAPPED_REGION.HPP. Another benefit of using mapped_region is the ability to have full and partial access to shared memory objects. Listing 3 shows how to use Mapped_region.
Listing 3: Accessing a Shared Memory object using Mapped_region

				
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_ region.hpp>

int main (int argc, char* argv[]) 
{
    using namespace boost::interprocess; 
    try { 
    //creating our first shared memory object.
    Shared_memory_object sharedmem1 (create_only, "Hello", read_write);

    Setting the size of the shared memory
    sharedmem1.truncate (n);

   Map the shared memory to current process 
   mapped_region mmap (SHAREDMEM1, n); 

    Access the mapped region using get_address
    std::strcpy (static_cast<char* > (region.get_address ()), "Hello World!\n ");
    
    } catch (interprocess_exception& e) { 
    //...  Clean up 
    } 
}

It's so simple. You have now created your own Mapped_region object and accessed it using the Get_address method. Static_cast was executed because Get_address returned a void*.

What happens when the main process exits with shared memory.

When the main process exits, the shared memory is not deleted. To remove shared memory, you need to call Shared_memory_object::remove. The access mechanism for the second process is also simple: Listing 4 demonstrates this.
Listing 4. Accessing a shared memory object from the second process

 #include <boost/interprocess/shared_memory_object.hpp> #include <boost/interprocess/mapped_region.hpp
      > #include <cstring> #include <cstdlib> #include <iostream> int main (int argc, char *argv[]) { 
      using namespace boost::interprocess; try {//Opening an existing shared memory object Shared_memory_object sharedmem2 (open_only, "Hello", Read_

      only);

      Map shared Memory object in current address space mapped_region mmap (sharedmem2, read_only);
      Need to Type-cast since get_address returns void* char *str1 = static_cast<char*> (Mmap.get_address ());
      Std::cout << str1 << Std::endl;
      } catch (interprocess_exception& e) {std::cout << e.what () << Std::endl;
} return 0; }

In Listing 4, you use the Open_only and Read_Only properties to create a shared memory object. If the shared memory object cannot be found, an exception is thrown. Now, build and run the code in Listing 3 and listing 4. You should see "Hello world!" on the terminal.

Next, add the following code and rebuild the code after std::cout in the code for the second process (listing 4):

Std::cout code here
shared_memory_object::remove ("Hello");
} catch (interprocess_exception& e) { 

Executes the code two times consecutively, and the second execution displays "No such file or directory", which proves that the shared memory has been deleted.

Back to top of page

Using Message Queuing to implement interprocess communication

Now, explore another popular inter-process communication mechanism: Message Queuing. Each process that participates in the communication can add messages and read messages from the queue in the queue. Message Queuing has the following properties: It has a name, and the process uses the name to access it. When creating a queue, the user must specify the maximum length of the queue and the maximum size of a message. The queue is persistent, which means that it remains in memory after the process that created it dies. You can delete a queue by explicitly calling Boost::interprocess::message_queue::remove.

In the code snippet shown in   listing 5 , the process creates a message queue that can contain 20 integers.
Listing 5. Create a message queue that can contain 20 integers

				
#include <boost/interprocess/ipc/message_queue.hpp>
#include <iostream> 

int main (int argc, Char * argv[]) 
{
    using namespace boost::interprocess;
    try { 
      //Creating a message Queue
      Message_queue MQ (create_only,   //Only create
                                       "MQ",              //Name                 //max Message Count
                                        sizeof (int)      //max message size
                                        );
       ... more code follows
    } catch (interprocess_exception& e) { 
       std::cout << e.what () << Std::endl ;
    } 
}

Note the Create_only property of the constructor passed to Message_queue. Similar to shared memory objects, the Open_only property should be passed to the constructor for open Message Queuing as read-only.

Sending and receiving data

In the sender, use the queue's Send method to add data. The Send method has three input parameters: the pointer to the raw data (void*), the size of the data, and the priority. Currently, all data is sent at the same priority level. Listing 6 shows the code.
listing 6. Sending messages to a queue

				
#include <boost/interprocess/ipc/message_queue.hpp>
#include <iostream> 

int main (int argc, Char * argv[]) 
{
    using namespace boost::interprocess;
    try { 
      //Creating a message Queue
      Message_queue MQ (create_only,   //Only create
                                       "MQ",              //name                 //max Message Count
                                        sizeof (int)      //max message size
                                        );
      Now send the messages
      to the queue for (int i=0; i<20; ++i) 
        mq.send (&i, sizeof (int), 0);//The 3rd AR Gument is the priority 
    } catch (interprocess_exception& e) { 
        std::cout << e.what () << Std::endl ;
    } 
}

On the receiver side, use the Open_only property to create the queue. Gets the message from the queue by calling the Receive method of the Message_queue class. Listing 7 shows the method signature for receive.
Listing 7. Method signature for Message_queue::receive

				
void receive (void *buffer,           
                      std::size_t buffer_size, 
                      std::size_t &recvd_size,
                      unsigned int & Priority
                     );

Let's take a closer look. The first parameter is where the data received from the queue will be stored. The second parameter is the expected size of the received data. The third parameter is the actual size of the data being received. The fourth parameter is the priority of the received message. Obviously, if the second and third arguments are not equal during the execution of the program, an error occurs. Listing 8 shows the code for the recipient process.
Listing 8: Receiving messages from Message Queuing

#include <boost/interprocess/ipc/message_queue.hpp> #include <iostream> int main (int argc, char* argv[]) {
    using namespace boost::interprocess;
                                       try {//opening the message queue whose name is MQ Message_queue MQ (open_only,//Only Open
      "MQ"//name); 
      size_t Recvd_size; 

      unsigned int priority; 
        Now send the messages to the queue for (int i=0; i<20; ++i) {int buffer; 
        Mq.receive ((void*) &buffer, sizeof (int), recvd_size, priority); if (recvd_size! = sizeof (int)); Do the error handling std::cout << buffer << ' << recvd_size << ' << priority
      ;
    }} catch (interprocess_exception& e) {std::cout << e.what () << Std::endl; } 
}

It's quite simple. Note that Message Queuing is still not removed from memory, and the queue is persistent like a shared memory object. To delete a queue, you should add the following line after you have finished using the queue:

Message_queue::remove ("MQ"); Remove the queue using its name

Message priority

In the sender, make the changes shown in Listing 9. The receiver code does not need to be modified.
Listing 9: Modifying the priority of a message

				
      Message_queue::remove ("MQ"); Remove the old queue
      Message_queue MQ (...);//Create
      as before for (int i=0; i<20; ++i) 
        mq.send (&i, sizeof (int), i%2); The 3rd parameter is the priority of the message
      //... rest as usual

When you run the code again, you should see the output shown in Listing 10.
Listing 10: The output seen in the receive process

				
1 4 1
3 4 1
5 4 1 7
4 1
9 4 1
4 1 4 1 4 1 4 1 4 1 0 4 0
4 4 0 6 4 0 8 4 0 4 0 4 0 4 0 +
4 0
18 4 0

Listing 10 confirms that the second process prioritizes receiving messages with high priority.

Back to top of page

Synchronizing access to a file

Shared memory and Message Queuing are good, but file I/O is also an important inter-process communication tool. Synchronizing file access for communication with concurrent processes is not easy, but the file lock feature provided by the Boost IPC Library makes synchronization easier. Before further explanation, take a look at listing 11 to see how the File_lock object works.
Listing 11: Synchronizing file access with the File_lock object

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.