首页 文章

如何附加到现有共享内存段

提问于
浏览
3

我遇到共享内存问题 . 我有一个创建和写入共享内存段的进程就好了 . 但我无法获得第二个进程来附加相同的现有段 . 如果我使用IPC_CREATE标志,我的第二个进程可以创建一个新的共享段,但我需要附加到第一个进程创建的现有共享段 .

这是我在第二个过程中的代码:

int nSharedMemoryID = 10;
key_t tKey = ftok("/dev/null", nSharedMemoryID);
if (tKey == -1)  {
    std::cerr << "ERROR: ftok(id: " << nSharedMemoryID << ") failed, " << strerror(errno) << std::endl;
    exit(3);
}
std::cout << "ftok() successful " << std::endl;

size_t nSharedMemorySize = 10000;
int id = shmget(tKey, nSharedMemorySize, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (id == -1)  {
    std::cerr << "ERROR: shmget() failed, " << strerror(errno) << std::endl << std::endl;
    exit(4);
}
std::cout << "shmget() successful, id: " << id << std::endl;

unsigned char *pBaseSM = (unsigned char *)shmat(id, (const void *)NULL, SHM_RDONLY);
if (pBaseSM == (unsigned char *)-1)  {
    std::cerr << "ERROR: shmat() failed, " << strerror(errno) << std::endl << std::endl;
    exit(5);
}
std::cout << "shmat() successful " << std::endl;

问题是第二个进程总是在调用shmget()时出错,并且出现“没有这样的文件或目录”错误 . 但这是我在第一个过程中使用的完全相同的代码,它在那里工作得很好 . 在创建共享段的第一个进程中,我可以写入内存段,我可以用“ipcs -m”看到它 . 另外,如果我从段的“ipcs -m”命令获取shmid并硬编码在我的第二个过程和第二个过程可以附加到它就好了 . 因此,问题似乎是生成两个进程用于标识单个共享段的公共ID .

我有几个问题:

(1)是否有更简单的方法来获取现有共享内存段的shmid?我似乎很疯狂,我必须将第一个进程(创建段)的三个独立参数传递给第二个进程,这样第二个进程才能获得相同的共享段 . 我可以忍受必须传递2个参数:文件名如"/dev/null"和相同的共享ID(我的代码中的nSharedMemoryID) . 但是为了获得shmid而必须传递给shmget()例程的段的大小似乎毫无意义,因为我不知道实际分配了多少内存(因为页面大小问题)所以我不能确定它是一样的 . (2)我在第二个过程中使用的分段大小是否必须与第一个过程中用于初始创建分段的分段大小相同?我试图将其指定为0,但我仍然遇到错误 . (3)同样,权限是否必须相同?也就是说,如果共享段是使用用户/组/世界的读/写创建的,那么第二个进程是否只能对用户使用读取? (两个进程的用户相同) . (4)为什么当两个进程明显存在文件_3039493时,shmget()会因"No such file or directory"错误而失败?我假设第一个进程没有对该节点进行某种锁定,因为这将是毫无意义的 .

感谢任何人都能给予的帮助 . 我一直在努力奋斗这几个小时 - 这意味着我可能正在做一些非常愚蠢的事情,并且当有人指出我的错误时最终会让自己难堪:-)

谢谢,-Andres

2 回答

  • 3

    (1)作为一种不同的方式:附加过程扫描用户的现有段,尝试附加所需的大小,检查段开头的“魔术字节序列”(以排除同一用户的其他程序) ) . 或者,您可以检查附加的过程是否是您期望的过程 . 如果其中一个步骤失败,这是第一个,并将创建该段......繁琐的是,我在70年代的代码中看到它 .

    最终你可以评估使用符合POSIX标准的 shm_open() 替代方案 - 应该更简单或至少更现代......

    (2)关于大小,重要的是指定的大小小于/等于现有段的大小,因此如果它舍入到下一个内存页大小则没有问题 . 只有在EINVAL变大时才会出现EINVAL错误 .

    (3)模式标志仅在您第一次创建段时(大多数是确定的)相关 .

    (4) shmget() 与"No such file or directory"失败的事实仅表示它没有找到具有该键的段(现在是迂腐的:不是id - 带有我们通常通过 shmget() 引用值的返回网络,随后使用) - 你有没有检查 tKey 是否相同?您的代码在我的系统上正常运行 . 刚刚添加了一个main() .

    编辑:附上工作计划

    #include <iostream>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    
    int main(int argc, char **argv) {
    
      int nSharedMemoryID = 10;
      if (argc > 1) {
        nSharedMemoryID = atoi(argv[1]);
      }
    
      key_t tKey = ftok("/dev/null", nSharedMemoryID);
      if (tKey == -1)  {
        std::cerr << "ERROR: ftok(id: " << nSharedMemoryID << ") failed, " << strerror(errno) << std::endl;
        exit(3);
      }
      std::cout << "ftok() successful. key = " << tKey << std::endl;
    
      size_t nSharedMemorySize = 10000;
      int id = shmget(tKey, nSharedMemorySize, 0);
      if (id == -1)  {
        std::cerr << "ERROR: shmget() failed (WILL TRY TO CREATE IT NEW), " << strerror(errno) << std::endl << std::endl;
        id = shmget(tKey, nSharedMemorySize, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | IPC_CREAT);
        if (id == -1)  {
          std::cerr << "ERROR: shmget() failed, " << strerror(errno) << std::endl << std::endl;
          exit(4);
        }
      }
      std::cout << "shmget() successful, id: " << id << std::endl;
    
    unsigned char *pBaseSM = (unsigned char *)shmat(id, (const void *)NULL, SHM_RDONLY);
    if (pBaseSM == (unsigned char *)-1)  {
        std::cerr << "ERROR: shmat() failed, " << strerror(errno) << std::endl << std::endl;
        exit(5);
    }
    std::cout << "shmat() successful " << std::endl;
    }
    

    编辑:输出

    $ ./a.out 33
    ftok() successful. key = 553976853
    ERROR: shmget() failed (WILL TRY TO CREATE IT NEW), No such file or directory
    
    shmget() successful, id: 20381699
    shmat() successful 
    $ ./a.out 33
    ftok() successful. key = 553976853
    shmget() successful, id: 20381699
    shmat() successful
    

    解决方案 - 聊天后(哇SO聊天!)讨论:

    最后问题是,在原始代码中,他在稍后调用 shmctl() 告诉将该段拆分为最后一个进程分离它,然后再附加另一个进程 .

    问题是,这实际上使该段私有化 . 它的密钥被 ipcs -m 标记为0x00000000,并且不能被其他进程连接 - 事实上它标记为延迟删除 .

  • 1

    我只想发布Sigismondo给我的所有帮助的结果并发布这个问题的解决方案,以防万一其他人有同样的问题 .

    线索是使用“ipcs -m”并注意到键值为0,这意味着共享段是私有的,因此第二个进程无法附加到它 .

    另外一个怪癖是:我打电话给以下人:

    int nReturnCode = shmctl(id,IPC_RMID,&m_stCtrlStruct);

    我的目的是为段设置模式,以便在使用它的所有进程都退出时删除它 . 但是,此调用具有使段成为私有的副作用,即使它是在不使用IPC_EXCL标志的情况下创建的 .

    希望这将有助于其他任何访问此问题的人 .

    而且,很多,非常感谢Sigismondo花时间帮助我 - 我从聊天中学到了很多东西!

    -Andres

相关问题