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

时间:2012-07-31 22:55:18

标签: c linux operating-system fork shared-memory

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

我使用了那段代码:

struct PipeShm
{
    int init;
    sem_t sema;
        ...
        ...
}

struct PipeShm * sharedPipe = NULL;

func2:

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_mkfifo:文件存在

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,所有流程之间共享细分受众群,因此为什么两个流程都具有相同的beforeafter值?

  2. 似乎每个进程都有自己的信号量,即使它们之间是共享的,所以出了什么问题?

  3. 谢谢

2 个答案:

答案 0 :(得分:4)

由于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.

如果你摆脱MAP_ANONYMOUS,那么你只会有一个共享内存块,但是你会遇到不调用sem_init的问题。在使用NPTL的Linux上它实际上会工作,因为将sem_t清除为所有0字节(此处的初始状态)等同于sem_init(&sema, anything, 0);(NPTL忽略pshared标志),但这不能移植到其他系统。

Per Karoly对另一个答案的评论,在公开电话中也有因O_TRUNC而出现竞争状况。如果第二个线程在第一个线程已经开始修改信号量后调用open,则该TRUNC将破坏信号量状态。可能最好的解决方案是将代码创建,打开和mmaping共享内存移动到另一个名为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之前添加一个清理:

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

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

答案 1 :(得分:2)

一些想法......

  1. 我认为这是对POSIX信号量如何工作的一个有趣的误解。我没有看到sem_initsem_open的来电。如果没有比你做得更明确,那么你不应该跨进程使用它们。

  2. 我对Linux上的mmap的实现并不是那么新鲜,以及MAP_ANONYMOUS可能会如何影响这一点,但一般来说,对映射区域的写入实际上并不是即时的。 manpage on linux.die says

  3. MAP_SHARED
    分享这个映射。映射的更新对于映射此文件的其他进程是可见的,并且会传递到基础文件。在调用msync(2)或munmap()之前,实际上可能不会更新该文件。

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