(转)使用 Boost 的 IPC
来源:互联网 发布:java printf居中 编辑:程序博客网 时间:2024/06/05 20:27
本文中将学习如何使用 Boost IPC 库实现共享内存对象、消息队列和同步文件锁。通过使用 Boost MPI 库,了解 environment 和 communicator 类,以及如何实现分布式通信。
注意:本文中的代码已经用 gcc-4.3.4 和 boost-1.45 包测试过了。
使用 Boost IPC 库
Boost Interprocess 是一个只由头文件组成的库,所以您需要做的只是在自己的源代码中包含适当的头文件并让编译器知道 include 路径。这是一个非常好的特性;您只需下载 Boost 源代码(见 参考资料 中的链接),然后就可以开始使用了。例如,要想在自己的代码中使用共享内存,就使用 清单 1 所示的 include。
清单 1. Boost IPC 库只由头文件组成
#include <boost/interprocess/shared_memory_object.hpp>using namespace boost::interprocess; //… your sources follow …
在把信息传递给编译器时,您要求进程根据安装相应地修改 include 路径。然后,编译代码:
bash-4.1$ g++ ipc1.cpp –I../boost_1_45_0
创建共享内存对象
我们先从传统的 "Hello World!" 程序开始。有两个进程:第一个进程把字符串 "Hello World!" 写入内存,另一个进程读取并显示此字符串。像 清单 2 这样创建共享内存对象。
清单 2. 创建共享内存对象
#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 (256); // … more code follows } catch (interprocess_exception& e) { // .. . clean up } }
sharedmem1 对象的类型是 shared_memory_object(在 Boost 头文件中声明并定义),它的构造函数有三个参数:
- 第一个参数 —
create_only
— 表示要创建这个共享内存对象而且还没有创建它。如果已经存在同名的共享对象,就会抛出异常。对于希望访问已经创建的共享内存的进程,第一个参数应该是open_only
。 - 第二个参数 —
Hello
— 是共享内存区域的名称。另一个进程将使用这个名称访问这个共享内存。 - 第三个参数 —
read_write
— 是共享内存对象的访问指示符。因为这个进程要修改共享内存对象的内容,所以使用read_write
。只从共享内存读取数据的进程使用read_only
指示符。
使用共享内存对象写数据
使用共享内存对象的进程必须在自己的地址空间中映射对象。使用在头文件 mapped_region.hpp 中声明并定义的 mapped_region 类执行映射。使用 mapped_region 的另一个好处是可以对共享内存对象进行完全和部分访问。清单 3 演示如何使用 mapped_region。
清单 3. 使用 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 (256); // map the shared memory to current process mapped_region mmap (sharedmem1, 256); // access the mapped region using get_address std::strcpy(static_cast<char* >(region.get_address()), "Hello World!\n"); } catch (interprocess_exception& e) { // .. . clean up } }
就这么简单。现在已经创建了您自己的 mapped_region 对象并使用 get_address 方法访问了它。执行了 static_cast,因为 get_address 返回一个 void*。
当主进程退出时共享内存会怎么样?
当主进程退出时,并不删除共享内存。要想删除共享内存,需要调用 shared_memory_object::remove。第二个进程的访问机制也很简单:清单 4 证明了这一点。
清单 4. 从第二个进程访问共享内存对象
#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;}
在清单 4 中,使用 open_only 和 read_only 属性创建共享内存对象。如果无法找到这个共享内存对象,就会抛出异常。现在,构建并运行 清单 3 和 清单 4 中的代码。应该会在终端上看到 "Hello World!"。
接下来,在第二个进程的代码(清单 4)中 std::cout 后面添加以下代码并重新构建代码:
// std::cout code hereshared_memory_object::remove("Hello");// } catch(interprocess_exception& e) {
连续执行代码两次,第二次执行会显示 "No such file or directory",这证明共享内存已经被删除了。
使用消息队列实现进程间通信
现在,研究另一种流行的进程间通信机制:消息队列。每个参与通信的进程都可以在队列中添加消息和从队列读取消息。消息队列具有以下性质:
- 它有名称,进程使用名称访问它。
- 在创建队列时,用户必须指定队列的最大长度和一个消息的最大大小。
- 队列是持久的,这意味着当创建它的进程死亡之后它仍然留在内存中。可以通过显式地调用
boost::interprocess::message_queue::remove
删除队列。
在 清单 5 所示的代码片段中,进程创建了一个可包含 20 个整数的消息队列。
清单 5. 创建一个可包含 20 个整数的消息队列
#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 20, //max message count sizeof(int) //max message size ); // … more code follows } catch (interprocess_exception& e) { std::cout << e.what( ) << std::endl; } }
注意传递给 message_queue 的构造函数的 create_only 属性。与共享内存对象相似,对于以只读方式打开消息队列,应该把 open_only 属性传递给构造函数。
发送和接收数据
在发送方,使用队列的 send 方法添加数据。send 方法有三个输入参数:原始数据的指针 (void*)、数据的大小和优先级。目前,以相同的优先级发送所有数据。清单 6 给出代码。
清单 6. 向队列发送消息
#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 20, //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 argument is the priority } catch (interprocess_exception& e) { std::cout << e.what( ) << std::endl; } }
在接收方,使用 open_only 属性创建队列。通过调用 message_queue 类的 receive 方法从队列获取消息。清单 7 给出 receive 的方法签名。
清单 7. message_queue::receive 的方法签名
void receive (void *buffer, std::size_t buffer_size, std::size_t &recvd_size, unsigned int &priority );
我们来仔细看一下。第一个参数是从队列接收的数据将被存储到的位置。第二个参数是接收的数据的预期大小。第三个参数是接收的数据的实际大小。第四个参数是接收的消息的优先级。显然,如果在执行程序期间第二个和第三个参数不相等,就是出现错误了。清单 8 给出接收者进程的代码。
清单 8. 从消息队列接收消息
#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; } }
这相当简单。注意,仍然没有从内存中删除消息队列;与共享内存对象一样,这个队列是持久的。要想删除队列,应该在使用完队列之后添加以下行:
message_queue::remove("mq"); // remove the queue using its name
消息优先级
在发送方,做 清单 9 所示的修改。接收方代码不需要修改。
清单 9. 修改消息的优先级
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); // 第 3 个参数为消息的优先级 // … rest as usual
再次运行代码时,应该会看到 清单 10 所示的输出。
清单 10. 在接收进程中看到的输出
1 4 13 4 15 4 17 4 19 4 111 4 113 4 115 4 117 4 119 4 10 4 02 4 04 4 06 4 08 4 010 4 012 4 014 4 016 4 018 4 0
清单 10 证实,第二个进程优先接收优先级高的消息。
同步对文件的访问
共享内存和消息队列很不错,但是文件 I/O 也是重要的进程间通信工具。对并发进程用于通信的文件访问进行同步并非易事,但是 Boost IPC 库提供的文件锁功能让同步变得简单了。在进一步解释之前,来看一下 清单 11,了解 file_lock 对象是如何工作的。
清单 11. 使用 file_lock 对象同步文件访问
#include <fstream> #include <iostream> #include <boost/interprocess/sync/file_lock.hpp> #include <cstdlib>int main() { using namespace boost::interprocess; std::string fileName("test"); std::fstream file; file.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); if (!file.is_open() || file.bad()) { std::cout << "Open failed" << std::endl; exit(-1); } try { file_lock f_lock(fileName.c_str()); f_lock.lock(); std::cout << "Locked in Process 1" << std::endl; file.write("Process 1", 9); file.flush(); f_lock.unlock(); std::cout << "Unlocked from Process 1" << std::endl; } catch (interprocess_exception& e) { std::cout << e.what( ) << std::endl; } file.close(); return 0; }
代码首先打开一个文件,然后使用 file_lock 锁定它。写操作完成之后,它刷新文件缓冲区并解除文件锁。使用 lock 方法获得对文件的独占访问。如果另一个进程也试图对此文件进行写操作并已经请求了锁,那么它会等待,直到第一个进程使用 unlock 自愿地放弃锁。file_lock类的构造函数接受要锁定的文件的名称,一定要在调用 lock 之前打开文件;否则会抛出异常。
现在,复制 清单 11 中的代码并做一些修改。具体地说,让第二个进程请求这个锁。清单 12 给出相关修改。
清单 12. 试图访问文件的第二个进程的代码
// .. as in Listing 11 file_lock f_lock(fileName.c_str()); f_lock.lock(); std::cout << "Locked in Process 2" << std::endl; system("sleep 4"); file.write("Process 2", 9); file.flush(); f_lock.unlock(); std::cout << "Unlocked from Process 2" << std::endl; // file.close();
现在,如果这两个进程同时运行,有 50% 的机会看到第一个进程等待 4 秒后才获得 file_lock,其他情况都不变。
在使用 file_lock 时,必须记住几点。这里讨论的主题是进程间通信,重点在进程 上。这意味着,不是使用 file_lock 来同步同一进程中各个线程的数据访问。在与 POSIX 兼容的系统上,文件句柄是进程属性,而不是 线程属性。下面是使用文件锁的几条规则:
- 对于每个进程,每个文件使用一个
file_lock
对象。 - 使用相同的线程来锁定和解锁文件。
- 在解锁文件之前,通过调用
C
的flush
库例程或flush
方法(如果喜欢使用C++ fstream
的话),刷新写入者进程中的数据。
结合使用 file_lock 和有范围(scope)的锁
在执行程序时,可能会出现抛出异常而文件没有解锁的情况。这种情况可能会导致意外的程序行为。为了避免这种情况,可以考虑把file_lock 对象放在(boost/interprocess/sync/scoped_lock.hpp 中定义的)scoped_lock 中。如果使用 scoped_lock,就不需要显式地锁定或解锁文件;锁定发生在构造器内,每当您退出该范围,就会自动发生解锁。清单 13 给出对 清单 11 的修改,使之使用有范围的锁。
清单 13. 结合使用 scoped_lock 和 file_lock
#include <boost/interprocess/sync/scoped_lock.hpp>#include <boost/interprocess/sync/file_lock.hpp>//… code as in Listing 11file_lock f_lock(fileName.c_str());scoped_lock<file_lock> s_lock(f_lock); // internally calls f_lock.lock( ); // No need to call explicit lock anymorestd::cout << "Locked in Process 1" << std::endl;file.write("Process 1", 9);// … code as in Listing 11
- (转)使用 Boost 的 IPC
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- 使用 Boost 的 IPC 和 MPI 库进行并发编程
- IBM文章使用 Boost 的 IPC 和 MPI 库进行并发编程
- IPC----pipe的使用
- 使用Messenger的IPC
- boost的使用(一)
- 【boost】boost::bind的使用
- 黑马程序员——【OC】点语法以及成员变量的作用域
- CSS3 @font-face
- Java中Comparable和Comparator实现对象比较
- 关于wbrtc单独编译
- Android开发如何使用真机测试
- (转)使用 Boost 的 IPC
- ios-day15-01(Quartz 2D之画圆的两种方式、画矩形的4种方式)
- JSP 几种返回按钮的使用
- JQuery实现高级检索功能
- 蓝桥杯 历届试题 回文数字【水题】
- 可以让你少奋斗十年的工作经验
- selenium webdriver - explicit wait vs implicit wait
- Android的AlertDialog详解
- C# UdpClient使用Receive和BeginReceive接收消息时的不同写法