【问题标题】:Shared memory is not so shared with processes in C共享内存不与 C 中的进程共享
【发布时间】:2012-07-29 19:34:24
【问题描述】:

在尝试解决一些调试问题时,我在代码中添加了一些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_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. 似乎每个进程都有自己的信号量,即使它是在它们之间共享的,那么出了什么问题?

谢谢

【问题讨论】:

  • 已经在另一个问题中向您展示了如何正确使用信号量;你为什么坚持不这样做?
  • @KarolyHorvath:已更新,请看文末。
  • @ron,考虑到你从 SO 上的人那里获得的关于这个单一项目的大量帮助,当你提交完成的项目时,你真的应该指出 SO 作为你的代码的共同作者 :)
  • @HristoIliev:保留所有权利...
  • @HristoIliev:顺便说一句,我的朋友,我从不要求任何人为我解决我的硬件问题。我总是问一个问题,我在哪里展示我的代码,以及到目前为止我做了什么。

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


【解决方案1】:

由于您对mmap 使用了MAP_ANONYMOUS 标志,因此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(&amp;sema, anything, 0);(NPTL 忽略 pshared 标志),但这不能移植到其他系统。

根据 Karoly 对另一个答案的评论,公开电话中还有一个由于 O_TRUNC 引起的竞争条件。如果第二个线程在第一个线程已经开始修改信号量之后调用open,则 TRUNC 将破坏信号量状态。可能最好的解决方案是将创建、打开和映射共享内存的代码移至调用 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();

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

【讨论】:

  • ,我怎样才能避免这里的竞争条件?他们确实在比赛
  • @ron:将赛车代码(至少 shm_open,可能还有 ftruncate 和 mmap)移动到一个单独的函数,该函数在 fork 之前只调用一次。
  • 感谢您的大力帮助,但有一件事还不是很清楚:如果我想用我的程序运行多个进程,让我们以func2() 之前的单个fork 为例( func2main()) 然后在“正常生活”中,这应该是可能的,对吧?坦率地说,我仍在尝试根据您的好建议改进我的代码,但我仍然会遇到一些竞争条件......
【解决方案2】:

一些想法......

  1. 我认为这是对 POSIX 信号量如何工作的根本误解。我没有看到对 sem_initsem_open 的呼叫。如果不做比你做的更明确的事情,你不应该能够跨进程使用它们。

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

MAP_SHARED
分享这个映射。映射此文件的其他进程可以看到映射的更新,并传递到底层文件。在调用 msync(2) 或 munmap() 之前,该文件可能不会真正更新。

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

【讨论】:

  • 可能还有很多问题,例如:O_TRUNC。刷新不是问题,他不从磁盘读取。
  • 它不是真正的文件;它是共享内存。就像在同一个 RAM 中映射到两个不同的进程一样。您是正确的,这里的错误是 (a) 没有使用 pshared=1 调用 sem_init,以及 (b) 使用 MAP_ANONYMOUS,这在这种情况下是错误的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-26
  • 2019-06-23
  • 1970-01-01
相关资源
最近更新 更多