目录
使用共享内存传递uint8 data和uint16 len,将该共享内存操作写成动态库,便于被其它程序调用。
一、基于共享内存的动态库
1.1 源码
头文件shared_memory.h
内容如下,通过结构体SharedData
定义了传输的消息类型,注意后续因为要设置传输消息类型的内存大小,需要用到sizeof()计算内存大小,因此最好把要传输的数据放到结构体中,便于计算。
// shared_memory.h
#ifndef SHARED_MEMORY_H
#define SHARED_MEMORY_H
#include <cstdint>
typedef uint8_t uint8;
typedef uint16_t uint16;
struct SharedData {
uint8 data;
uint16 len;
};
void create_shared_memory();
void destroy_shared_memory();
SharedData* get_shared_data();
#endif // SHARED_MEMORY_H
函数定义文件shared_memory.cpp
主要包含创建内存空间、销毁内存空间、返回内存空间的地址。
// shared_memory.cpp
// g++ -shared -fPIC shared_memory.cpp -o libshared_memory.so
#include "shared_memory.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
const char* SHM_NAME = "/shared_memory_example";
int shm_fd = -1;
SharedData* shared_data_ptr = nullptr;
void create_shared_memory() {
shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
std::cerr << "Failed to create shared memory." << std::endl;
return;
}
if (ftruncate(shm_fd, sizeof(SharedData)) == -1) {
std::cerr << "Failed to set shared memory size." << std::endl;
return;
}
shared_data_ptr = static_cast<SharedData*>(mmap(nullptr, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
if (shared_data_ptr == MAP_FAILED) {
std::cerr << "Failed to map shared memory." << std::endl;
return;
}
}
void destroy_shared_memory() {
if (shared_data_ptr) {
munmap(shared_data_ptr, sizeof(SharedData));
}
if (shm_fd != -1) {
close(shm_fd);
shm_unlink(SHM_NAME);
}
}
SharedData* get_shared_data() {
return shared_data_ptr;
}
1.2 编译命令
g++ -shared -fPIC shared_memory.cpp -o libshared_memory.so
注意其它程序要动态链接该库,需要添加动态库寻找的路径:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
最后的.
代表动态库处于当前目录。
1.3 函数解释
1.3.1 shm_open()函数
shm_open是一个POSIX共享内存API的函数,用于创建或打开一个命名的共享内存对象。这个函数的原型如下:
int shm_open(const char *name, int oflag, mode_t mode);
参数:
- name:共享内存对象的名称,通常以一个斜杠(/)开头。
- oflag:打开或创建共享内存对象的标志,如O_CREAT(创建)、O_RDWR(读写模式)等。
- mode:设置共享内存对象的权限,如S_IRUSR(用户读权限)和S_IWUSR(用户写权限)等。
- 返回值:成功时返回一个非负整数的文件描述符,失败时返回-1。
1.3.2 ftruncate()函数
ftruncate函数用于调整文件大小,通常用于设置共享内存对象的大小。这个函数的原型如下:
int ftruncate(int fd, off_t length);
参数:
- fd:文件描述符,通常是shm_open函数的返回值。
- length:文件的新大小。
- 返回值:成功时返回0,失败时返回-1。
1.3.3 mmap()函数
mmap函数用于将一个文件或共享内存对象映射到进程的虚拟地址空间。这个函数的原型如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数:
- addr:建议的映射起始地址,通常设置为NULL,让系统自动选择。
- length:映射区域的大小。
- prot:映射区域的保护属性,如PROT_READ(可读)、PROT_WRITE(可写)等。
- flags:映射类型,如MAP_SHARED(共享映射)或MAP_PRIVATE(私有映射)等。
- fd:文件描述符,通常是shm_open函数的返回值。
- offset:文件偏移量。
- 返回值:成功时返回映射区域的起始地址,失败时返回MAP_FAILED。
1.3.4 munmap()函数
munmap函数用于解除进程虚拟地址空间的映射。这个函数的原型如下:
int munmap(void *addr, size_t length);
参数:
- addr:映射区域的起始地址。
- length:映射区域的大小。
- 返回值:成功时返回0,失败时返回-1。
1.3.5 shm_unlink()函数
shm_unlink函数用于删除一个命名的共享内存对象。当共享内存对象的引用计数降至0时,它会被系统释放。这个函数的原型如下:
int shm_unlink(const char *name);
参数:
- name:共享内存对象的名称,通常与shm_open函数中使用的相同。
- 返回值:成功时返回0,失败时返回-1。
1.3.6 close()函数
close函数用于关闭一个文件描述符。这个函数的原型如下:
int close(int fd);
参数:
- fd:要关闭的文件描述符。
- 返回值:成功时返回0,失败时返回-1。
二、sender.cpp
2.1 源码
// sender.cpp
// g++ sender.cpp -o sender -L. -lshared_memory -lrt
// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
#include "shared_memory.h"
#include <iostream>
int main() {
create_shared_memory();
SharedData* shared_data = get_shared_data();
if (shared_data) {
shared_data->data = 42;
shared_data->len = 16;
std::cout << "Set shared data: " << static_cast<int>(shared_data->data) << ", " << shared_data->len << std::endl;
} else {
std::cerr << "Failed to access shared memory." << std::endl;
}
char ch = 0;
ch = getchar();
destroy_shared_memory();
return 0;
}
2.2 编译运行命令
编译:
g++ sender.cpp -o sender -L. -lshared_memory -lrt
运行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./sender
注意运行该文件时,需要export命令添加共享内存动态库的路径。
2.3 源码解释
sender通过create_shared_memory()函数创建了共享内存,其中共享内存的名称已经在动态库中设置为全局变量:
const char* SHM_NAME = "/shared_memory_example";
接着通过get_shared_data()函数获取该名称对应的共享内存地址(每个共享内存的名称对应的地址是唯一的)。
接着对该共享内存中的数据成员进行赋值操作。
shared_data->data = 42;
shared_data->len = 16;
由于需要等待receiver读取共享内存后才能进行销毁,因此这里通过getchar()函数阻塞进程,保持进程存活,待receiver读取后,在send中输入回车按键即可退出。
三、receiver.cpp
3.1 源码
// receiver.cpp
// g++ receiver.cpp -o receiver -L. -lshared_memory -lrt
// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
#include "shared_memory.h"
#include <iostream>
int main() {
create_shared_memory();
SharedData* shared_data = get_shared_data();
if (shared_data) {
std::cout << "Received shared data: " << static_cast<int>(shared_data->data) << ", " << shared_data->len << std::endl;
} else {
std::cerr << "Failed to access shared memory." << std::endl;
}
destroy_shared_memory();
return 0;
}
3.2 编译运行命令
编译:
g++ receiver.cpp -o receiver -L. -lshared_memory -lrt
运行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./receiver
注意运行该文件时,需要export命令添加共享内存动态库的路径。
3.3 源码解释
动态库中shared_data_ptr
这个全局变量在不同程序中是不共享的,因此receiver.cpp也需要运行create_shared_memory()与get_shared_data()函数通过共享内存名称来获取该共享内存地址!
create_shared_memory();
SharedData* shared_data = get_shared_data();
通过共享内存指针获取数据后打印出来,由于cout无法打印uint类型字符,因此将其转换为int进行打印:
std::cout << "Received shared data: " << static_cast<int>(shared_data->data) << ", " << shared_data->len << std::endl;
四、注意事项
如果sender中没有getchar()函数,运行完后销毁共享内存,进程结束,再运行receiver,会发现没有收到数据。