我在使用共享内存方面遇到了困难。我相信我误解了共享内存应该如何工作。我正在尝试为我的项目创建一个模板类来包装POSIX共享内存API。当我第二次调用mmap()时,在同一个进程中,我希望看到第一次返回相同的ptr。这不是我所看到的。我的第二个地址偏移了0x1000(我假设这是页面边界)。当我写入第一个内存映射位置时,数据不会显示在第二个内存中。
我想可能数据没有得到同步,所以我尝试调用msync()。这没有用。
我最怀疑mmap()返回的不同地址。看起来这可能是一个指针指针,但没有任何文档或示例显示指针指针的任何内容。所以...
显然,这让我相信我做错了什么或者误解了命名共享内存应该在同一个过程中如何工作。
我一直在倾倒man pages以及所有的SO寻找无济于事的答案。
有些人可以告诉我我在做什么 错误 来访问相同的命名共享内存位置?或者指出我的答案提供了我需要的解释,让我再次上路。
请原谅所有调试cout和过多的文档,我试图了解/学习shared_memory API。
注意: 我知道有更好的方法可以在同一个流程中共享数据。但是,这只是一个简短的测试驱动程序,此模板将用于多进程环境。
编辑:我不确定这是否重要,但我想尝试在Suse Linux 3.0.101上运行
“帮助我Obi-Wan Kenobi,你是我唯一的希望!”
< / p>
shmem.h
// ****************************************************************************
// POSIX Shared Memory
// as document by Richard Stevens
// "UNIX Network Programming: Interprocess Communications" Vol 2, 2nd Ed.
// -------------------
//
// Shared memory is the fastest form of IPC available, because one copy of the
// data in the shared memory is available to all the threads or processes that
// share the memory. Some form of synchronization is normally required,
// however, to coordinate the threads or processes that are sharing the memory.
//
// POSIX provides two ways to share memory between unrelated processes.
// 1. Memory-mapped files: a file is opened by open, and the resulting
// descriptor is mapped into the address space of the process by mmap.
// Memory-mapped files can also be shared between unrelated processes.
// 2. Shared memory objects: the function shm_open opens a POSIX IPC name
// (perhaps a pathname in the filesystem), returning a descriptor that
// is then mapped into the address space of the process by mmap.
//
// Both techniques require the call to mmap. What differs is how the descriptor
// that is an argument to mmap is obtained: by open or shm_open.
//
// ****************************************************************************
#ifndef SHMEM_H_
#define SHMEM_H_
#include <errno.h> // error checking
#include <fcntl.h> // O_ constants
#include <semaphore.h> // semaphore API
#include <stddef.h> // defines NULL
#include <sys/mman.h> // shared memory API
#include <sys/stat.h> // mode constants
#include <unistd.h> // for close()
#include <iostream>
using namespace std;
template <class T, long count = 1>
class shmem
{
public:
// ------------------------------------------------------------------------
shmem(const char* name) :
pName(name), pShmData(0), mShmFd(0), mCreated(true)
{
cout << "START: shmem(\"" << pName << "\", " << count << ") Constructor" << endl<< flush;
// --------------------------------------------------------------------
// The two-step process involved with POSIX shared memory requires:
// 1. calling shm_open, specifying a name argument, to either
// create a new shared memory object or to open an existing
// shared memory object, followed by
// 2. calling mmap to map the shared memory into the address space
// of the calling process.
int flags = O_RDWR|O_CREAT|O_EXCL;
mode_t mode = S_IRUSR|S_IWUSR;
// flag indicating that the shared memory is the same as the data
// passed in
bool valid = true;
// Determine the amount of memory should include the
// header + the data buffer
const size_t len = sizeof(shmem_data_t);
cout << "Shmem_open()... "<< flush;
mShmFd = shm_open(pName, flags, mode);
// Check to see if the shared memory has been created yet
if (mShmFd == -1)
{
cout << "failed. ********************* errno: " << errno << endl<< flush;
// Remove flags (O_EXCL, O_CREAT) and try to open shared memory
// that already exists
flags &= ~O_EXCL;
flags &= ~O_CREAT;
cout << "Shmem_open (Again)... "<< flush;
mShmFd = shm_open(pName, flags, mode);
// Check to see if an error occurred while trying to open
valid = (mShmFd != -1);
if (valid)
{
cout << "success!" << endl<< flush;
// Indicate that the shared memory already existed
mCreated = false;
}
else
{
cout << "failed. ********************* errno: " << errno << endl<< flush;
}
} else
{
cout << "success!" << endl << flush;
}
cout << "mmap()... "<< flush;
// The mmap function maps a POSIX shared memory object (T) + Header
// into the address space of a process.
pShmData = reinterpret_cast<shmem_data_t*> (
mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, mShmFd, 0));
if (pShmData == NULL)
{
int error = errno;
switch (error)
{
default:
// Undefined Error
cout << "failed: ********************* errno: " << error << endl<< flush;
break;
}
} else
{
cout << "success: " << hex << "0x" << pShmData << dec << endl << flush;
}
// Check to see if we are the first user to request this shared memory
// location.
if (mCreated)
{
cout << "CREATED!" << endl;
cout << "Initial Header Data: Size " << pShmData->size << endl;
cout << "Initial Header Data: Count " << pShmData->len << endl;
// Initialize the header if we created the SHM
cout << "sem_init()" << endl<< flush;
sem_init(&pShmData->mutex,1,1);
cout << "sem_wait()... " << endl<< flush;
sem_wait(&pShmData->mutex);
cout << "Got It!" << endl<< flush;
pShmData->size = len;
pShmData->len = count;
cout << "release semaphore" << endl<< flush;
sem_post(&pShmData->mutex);
cout << "Initialization complete" << endl<< flush;
cout << "Header Data: Size " << pShmData->size << endl;
cout << "Header Data: Count " << pShmData->len << endl;
} else if (valid)
{
cout << "Validating Shared Memory... " ;
// Validate the Shared Memory that was acquired
valid &= (pShmData->size == len);
valid &= (pShmData->len == count);
if (valid)
cout << "success!" << endl<< flush;
else
cout << "failed. ********************* " << endl<< flush;
cout << "Header Data: Size " << pShmData->size << endl;
cout << "Header Data: Count " << pShmData->len << endl;
}
else
{
shm_unlink(pName);
exit(1);
}
// FIXME: What should we do if we aren't valid?!
cout << "END: Shmem Constructor" << endl<< flush;
}
// ------------------------------------------------------------------------
// Copy Constructor - Increment Use count for Shared Memory.
shmem(const shmem& that) :
pName(that.pName), pShmData(0), mShmFd(0)
{
cout << "START: shmem Copy Constructor" << endl << flush;
// --------------------------------------------------------------------
// The two-step process involved with POSIX shared memory requires:
// 1. calling shm_open, specifying a name argument, to either
// create a new shared memory object or to open an existing
// shared memory object, followed by
// 2. calling mmap to map the shared memory into the address space
// of the calling process.
int flags = O_RDWR;
mode_t mode = S_IRUSR|S_IWUSR;
// flag indicating that the we allocated valid shared memory is the
// same as the data passed in
bool valid = true;
// Determine the amount of memory should include the
// header + the data buffer
const size_t len = sizeof(shmem_data_t);
mShmFd = shm_open(pName, flags, mode);
// Check to see if an error occurred while trying to open
valid = (mShmFd != -1);
// The mmap function maps a POSIX shared memory object (T) + Header
// into the address space of a process.
pShmData = mmap(NULL, that.mShmFd->size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, mShmFd, 0);
cout << "close()... "<< flush;
// The close() function will deallocate the file descriptor.
// All outstanding record locks owned by the process on the file
// associated with the file descriptor will be removed (that is,
// unlocked).
//
// If a shared memory object remains referenced at the last close (that is, a
// process has it mapped), then the entire contents of the memory object persist
// until the memory object becomes unreferenced. If this is the last close of a
// memory object and the close results in the memory object becoming
// unreferenced, and the memory object has been unlinked, then the memory object
// will be removed.
close(mShmFd);
cout << "success." << endl<< flush;
cout << "END: shmem Copy Constructor" << endl << flush;
}
// ------------------------------------------------------------------------
virtual ~shmem()
{
cout << "START: ~shmem() Destructor" << endl<< flush;
if (mCreated)
{
cout << "shm_unlink( \"" << pName << "\")... "<< flush;
// The shm_unlink function removes the name of a shared memory object.
// As with all the other unlink functions, unlinking a name has no
// effect on existing references to the underlying object, until all
// references to that object are closed. Unlinking a name just prevents
// any subsequent call to open, mq_open, or sem_open from succeeding.
if(shm_unlink(pName) == -1)
{
int error = errno;
switch (error)
{
case EACCES:
// Permission is denied to unlink the named shared memory
// object.
cout << "Failed: ********************* EACCES " << endl<< flush;
break;
case ENAMETOOLONG:
// The length of the name argument exceeds {PATH_MAX} or a
// pathname component is longer than {NAME_MAX}.
cout << "Failed: ********************* ENAMETOOLONG" << endl<< flush;
break;
case ENOENT:
// The named shared memory object does not exist.
cout << "Failed: ********************* ENOENT" << endl<< flush;
break;
default:
// Undefined Error
cout << "Failed: ********************* <UNKNOWN> errno: " << error << endl<< flush;
break;
}
} else
{
cout << "Success!" << endl<< flush;
}
}
cout << "close()... " << flush;
// The close() function will deallocate the file descriptor.
// All outstanding record locks owned by the process on the file
// associated with the file descriptor will be removed (that is,
// unlocked).
//
// If a shared memory object remains referenced at the last close (that is, a
// process has it mapped), then the entire contents of the memory object persist
// until the memory object becomes unreferenced. If this is the last close of a
// memory object and the close results in the memory object becoming
// unreferenced, and the memory object has been unlinked, then the memory object
// will be removed.
close(mShmFd);
cout << "success." << endl << flush;
cout << "END: ~shmem() Destructor" << endl<< flush;
}
// ------------------------------------------------------------------------
// Returns address only to the indexed object in shared memory
T* Obj_Addr(uint32_t n = 0)
{
cout << "shmem.Obj_Addr()" << endl << flush;
return &pShmData->buf[n];
}
// ------------------------------------------------------------------------
// sync...
void Sync()
{
cout << "shmem.Sync()... ";
if (msync(pShmData, sizeof(shmem_data_t), MS_SYNC) == -1)
{
cout << "failed: ********************* errno: " << errno << endl<< flush;
} else
{
cout << "success. " << endl << flush;
}
}
// ------------------------------------------------------------------------
// Returns reference only to the indexed object in shared memory
T& Obj(uint32_t n = 0)
{
cout << "shmem.Obj()" << endl << flush;
return pShmData->buf[n];
}
// ------------------------------------------------------------------------
// Returns reference only to the indexed object in shared memory
T& operator[] (uint32_t n)
{
cout << "Accessing shmem[" << n << "] == " << flush;
cout << pShmData->buf[n] << "!" << endl << flush;
return pShmData->buf[n];
}
private:
// ------------------------------------------------------------------------
// Hide default constructor
shmem() : pName(0), pShmData(0), mShmFd(0)
{
}
private:
struct shmem_data_t
{
size_t size;
uint32_t len;
sem_t mutex;
T buf[count];
};
const char* pName;
shmem_data_t* pShmData;
int mShmFd;
// Flag indicating that we created the shared memory
bool mCreated;
};
#endif /* SHMEM_H_ */
的main.cpp
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <iostream> // ** FIXME ** DEBUG
using namespace std;
#include "stdint.h"
#include "shmem.h"
bool done = false;
// ----------------------------------------------------------------------------
void my_handler(int s)
{
cout << "Goodbye! SIG: " << s << endl << flush;
done = true;
}
// ----------------------------------------------------------------------------
void test_shmem()
{
cout << endl << endl << "Testing Shmem Template" << endl;
cout << "-------------------------------------------" << endl;
shmem<int,13> x("/jco");
cout << "-------------------------------------------" << endl;
shmem<int,13> y("/jco");
cout << "-------------------------------------------" << endl;
x[5] = 7;
x.Sync();
cout << "-------------------------------------------" << endl;
cout << "X[5] = " << x[5] << endl;
cout << "-------------------------------------------" << endl;
cout << "Y[5] = " << y[5] << endl;
cout << "-------------------------------------------" << endl;
cout << endl << "*** Testing Complete." << endl << endl;
sleep(10);
}
// ----------------------------------------------------------------------------
int main()
{
cout << "MAIN" << endl;
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = my_handler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
test_shmem();
// RUN
while(not done)
{
sleep(1);
}
return 0;
}
控制台输出:
MAIN
Testing Shmem Template
-------------------------------------------
START: shmem("/jco", 13) Constructor
Shmem_open()... success!
mmap()... success: 0x0x7f32113ad000
CREATED!
Initial Header Data: Size 0
Initial Header Data: Count 0
sem_init()
sem_wait()...
Got It!
release semaphore
Initialization complete
Header Data: Size 104
Header Data: Count 13
END: Shmem Constructor
-------------------------------------------
START: shmem("/jco", 13) Constructor
Shmem_open()... failed. ********************* errno: 17
Shmem_open (Again)... success!
mmap()... success: 0x0x7f32113ac000
Validating Shared Memory... failed. *********************
Header Data: Size 0
Header Data: Count 0
END: Shmem Constructor
-------------------------------------------
Accessing shmem[5] == 0!
shmem.Sync()... success.
-------------------------------------------
Accessing shmem[5] == 7!
X[5] = 7
-------------------------------------------
Accessing shmem[5] == 0!
Y[5] = 0
-------------------------------------------
*** Testing Complete.
START: ~shmem() Destructor
close()... success.
END: ~shmem() Destructor
START: ~shmem() Destructor
shm_unlink( "/jco")... Success!
close()... success.
END: ~shmem() Destructor
Goodbye! SIG: 2
答案 0 :(得分:2)
编辑:我的第一个回答完全错过了标记。所以我觉得有义务做出有用的贡献。
Petesh和BЈови都给出了正确的答案。首先,您不应该使用MAP_ANONYMOUS
。其次,您应该意识到从mmap
返回的(虚拟)地址与第一个地址不同。当您两次调用mmap
时,您将为同一共享内存创建两个单独的映射。但是你可以使用这两个地址中的任何一个,它们将指向同一块共享内存。
我写了这个小程序来演示。它基本上完成了你的程序所做的事情,并表明即使两个mmap
调用返回两个不同的地址,两个地址都在读取和写入同一个共享内存。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#define TEMP_FILENAME "shm.tmp"
int main(void)
{
int fd1 = shm_open(TEMP_FILENAME, O_CREAT | O_RDWR, 0777);
int fd2 = shm_open(TEMP_FILENAME, O_RDWR, 0777);
int *p1, *p2;
int buf[1024] = {0x12345678};
// Write initial contents to shared memory.
write(fd1, buf, 4096);
p1 = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
p2 = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
printf("fd1 = %d, p1 = %p\n", fd1, p1);
printf("fd2 = %d, p2 = %p\n", fd2, p2);
printf("p1[0] = 0x%08x, p2[0] = 0x%08x\n", p1[0], p2[0]);
p1[0] = 0xdeadbeef;
printf("p1[0] = 0x%08x, p2[0] = 0x%08x\n", p1[0], p2[0]);
close(fd2);
close(fd1);
shm_unlink(TEMP_FILENAME);
return 0;
}
输出:
fd1 = 3, p1 = 0x7f2b3d434000
fd2 = 4, p2 = 0x7f2b3d433000
p1[0] = 0x12345678, p2[0] = 0x12345678
p1[0] = 0xdeadbeef, p2[0] = 0xdeadbeef
答案 1 :(得分:2)
mmap()在调用进程的虚拟地址空间中创建新的映射。
所以,你得到的是一个虚拟地址,匹配相同的物理地址。由于你是mmap() - 在两个不同的进程中使用相同的内存区域,你将得到不同的虚拟地址。
您应该阅读有关Linux内存管理的更多信息(例如here)。
答案 2 :(得分:2)
您对[{1}} - mmap
选项的参数不正确。
这样做明确地忽略来自MAP_ANONYMOUS
的文件描述符,而是为该进程创建一个私有映射。
shm_open
的mmap联机帮助页指示:
任何文件都不支持映射;其内容初始化为零。忽略fd和offset参数。