繁体   English   中英

命名共享内存:shm_open没有提供正确的位置

[英]Named Shared Memory: shm_open not providing the correct location

我在使用共享内存时遇到困难。 我相信我误解了共享内存应该如何工作。 我正在尝试为我的项目创建一个模板类,以包装POSIX共享内存API。 当我第二次调用mmap()时,在相同的过程中,我期望看到的返回的ptr与第一次相同。 这不是我所看到的。 我的第二个地址偏移了0x1000(我假设这是页面边界)。 当我写入第一个内存映射位置时,数据不会在第二个中显示。

我可能认为数据未同步,所以我尝试调用msync()。 这没有帮助。

我最怀疑mmap()返回的不同地址。 似乎这可能是一个指针指针,但是文档或示例均未显示任何有关指针指针的内容。 所以...

显然,这使我相信我做错了什么或误解了命名共享内存应该如何在同一进程中工作。

我一直在手册页和整个网站上寻找所需的答案,但无济于事。

有人可以告诉我访问相同的命名共享内存位置时我做错了吗? 或指出要我回答的问题,以提供我需要重新获得帮助的解释。

请原谅所有调试提示和过多的文档,我试图了解/学习shared_memory API。

注意: 我了解在同一过程中有更好的共享数据的方法。 但是,这只是一个简短的测试驱动程序,该模板将用于多进程环境。

编辑:我不确定这很重要,但我正在尝试在Suse Linux 3.0.101上运行



“帮我Obi-Wan Kenobi,你是我唯一的希望!”

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

编辑:我的第一个答案完全错过了分数。 因此,我有义务做出一些有益的贡献。

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

mmap()手册页中

mmap()在调用过程的虚拟地址空间中创建一个新的映射。

因此,您得到的是一个虚拟地址,它与相同的物理地址匹配。 由于您要通过两个不同的进程mmap()来访问同一内存区域,因此您将获得不同的虚拟地址。

您应该阅读有关linux内存管理的更多信息(例如在此处 )。

您对mmap说法不正确MAP_ANONYMOUS选项。

这样做是显式地忽略shm_open文件描述符,而是为该进程创建了私有映射。

MAP_ANONYMOUS mmap MAP_ANONYMOUS页指示:

映射没有任何文件支持; 其内容初始化为零。 fd和offset参数将被忽略。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM