代码之家  ›  专栏  ›  技术社区  ›  JAN

共享内存不与C中的进程共享

  •  2
  • JAN  · 技术社区  · 14 年前

    在尝试解决一些调试问题时,我添加了一些 printf -s到我的代码:

    我用了那个代码:

    struct PipeShm
    {
        int init;
        sem_t sema;
            ...
            ...
    }
    
    struct PipeShm * sharedPipe = NULL;
    

    函数2:

    int func2()
    {
    if (!sharedPipe)
    {
    
        int myFd = shm_open ("/myregion", O_CREAT | O_TRUNC | O_RDWR, 0666);
        if (myFd == -1)
            error_out ("shm_open");
    
        // allocate some memory in the region in the size of the struct
        int retAlloc = ftruncate (myFd, sizeof * sharedPipe);
        if (retAlloc < 0)  // check if allocation failed
            error_out("ftruncate");
    
        // map the region and shared in with all the processes
        sharedPipe = mmap (NULL, sizeof * sharedPipe,PROT_READ | PROT_WRITE,MAP_SHARED , myFd, 0);
    
        if (sharedPipe == MAP_FAILED)  // check if the allocation failed
            error_out("mmap");
    
        // put initial value
        int value = -10;
        // get the value of the semaphore
        sem_getvalue(&sharedPipe->semaphore, &value);
    
    
        if (sharedPipe->init != TRUE) // get in here only if init is NOT TRUE !
        {
            if (!sem_init (&sharedPipe->semaphore, 1, 1)) // initialize the semaphore to 0
            {
    
                sharedPipe->init = TRUE;
                sharedPipe->flag = FALSE;
                sharedPipe->ptr1 = NULL;
                sharedPipe->ptr2 = NULL;
                sharedPipe->status1 = -10;
                sharedPipe->status2 = -10;
                sharedPipe->semaphoreFlag = FALSE;
                sharedPipe->currentPipeIndex = 0;
                printf("\nI'm inside the critical section! my init is: %d\n" , sharedPipe->init);
    
            }
            else
                perror ("shm_pipe_init");
            printf("\nI'm out the critical section! my init is: %d\n" , sharedPipe->init);
    
        }
    
    
    }
    return 1;   // always successful
    }
    

    有了这条主线:

    int main()
    
    {
        int spd, pid, rb;
        char buff[4096];
        fork();
        func2();
        return 0;
    }
    

    得到了这个:

    shm_pipe_mkifo:文件存在

    I'm inside the critical section! my init is: 1
    
    I'm out the critical section! my init is: 1
    Output:hello world!
    I'm inside the critical section! my init is: 1
    
    I'm out the critical section! my init is: 1
    

    共享的记忆似乎并不那么共享,为什么?

    1. 由于 MAP_SHARED | MAP_ANONYMOUS ,那么为什么两个过程都相同 before after 价值观

    2. 似乎每个进程都有自己的信号量,尽管它们之间是共享的,那么出了什么问题呢?

    谢谢

    2 回复  |  直到 14 年前
        1
  •  4
  •   Chris Dodd    14 年前

    由于您使用 MAP_ANONYMOUS 标记到 mmap 这个 myFd 参数被忽略,并且您创建了两个独立的共享内存块,每个进程中一个,它们彼此没有关系。

      MAP_ANONYMOUS
              The mapping is not backed by any file; its contents are initial‐
              ized to zero.  The fd and offset arguments are ignored; however,
              some implementations require fd to be -1  if  MAP_ANONYMOUS  (or
              MAP_ANON)  is specified, and portable applications should ensure
              this.  The use of MAP_ANONYMOUS in conjunction  with  MAP_SHARED
              is only supported on Linux since kernel 2.4.
    

    如果你摆脱了 地图_匿名 然后,您将只有一个共享内存块,但随后会出现不调用的问题 sem_init 在带有NPTL的Linux上,它实际上可以工作,因为将sem_t清除为所有0字节(此处为初始状态)相当于 sem_init(&sema, anything, 0); (NPTL忽略pshared标志),但这不能移植到其他系统。

    根据Karoly对另一个答案的评论,还有一个比赛条件是O_TRUNC在公开赛中。如果第二个线程调用 open 在第一个线程已经开始修改信号量之后,TRUNC将破坏信号量状态。也许最好的解决方案是将创建、打开和管理共享内存的代码转移到一个不同的函数中,该函数被称为BEFORE调用fork。

    编辑

    要解决O_TRUNC问题,不能有多个进程使用O_TRUNC调用shm_open。但是,如果您刚刚摆脱了O_TRUNC,那么您就有了启动问题,即如果共享内存对象已经存在(来自程序的前一次运行),那么它可能不会处于可预测的状态。关于可能性是将func2的开头分开:

    main() {
        func1();
        fork();
        func2();
    }
    
    func1() {
        int myFd = shm_open ("/myregion", O_CREAT | O_TRUNC | O_RDWR, 0666);
        if (myFd == -1)
            error_out ("shm_open");
        // allocate some memory in the region in the size of the struct
        int retAlloc = ftruncate (myFd, sizeof *sharedPipe);
        if (retAlloc < 0)  // check if allocation failed
            error_out("ftruncate");
        // map the region and shared in with all the processes
        sharedPipe = mmap (NULL, sizeof *sharedPipe, PROT_READ|PROT_WRITE, MAP_SHARED, myFd, 0);
        if (sharedPipe == MAP_FAILED)  // check if the allocation failed
            error_out("mmap");
    }
    
    func2() {
        // put initial value
        int value = -10;
        // get the value of the semaphore
        sem_getvalue(&sharedPipe->semaphore, &value);
    
        :
    

    或者,您可以保留相同的代码(只需去掉O_TRUNC),并在fork之前添加一个cleanup:

    main() {
        shm_unlink("/myregion");
        fork();
        func2();
    

    在所有情况下,如果您同时运行程序的多个副本,您仍然会遇到问题。

        2
  •  2
  •   asveikau    14 年前

    一些想法。。。

    1. 我认为这是对POSIX信号量如何工作的基本误解。我看不到打给的电话 sem_init sem_open 。如果不做比你所做的更明确的事情,你就不应该能够在整个过程中使用它们。

    2. 我对 mmap 关于Linux以及如何 MAP_ANONYMOUS 可能会影响这一点,但通常情况下,对映射区域的写入不可能是即时的。这个 manpage on linux.die says 以下为:

    映射共享(_S)
    共享此映射。映射的更新对映射此文件的其他进程可见,并一直传递到基础文件。在调用msync(2)或munmap()之前,文件实际上可能不会更新。

    原因是您的内存访问陷入了页面错误,此时内核将填充文件描述符中的内容,然后让您在RAM中进行写入,然后在稍后的某个时间点内核将刷新回文件描述符。