2

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

谢谢

4

2 回答 2

4

由于您使用MAP_ANONYMOUS标志 to 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 标志),但这不能移植到其他系统。

根据 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();

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

于 2012-08-01T01:10:49.030 回答
2

一些想法...

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

  2. mmap对 Linux 上的实现以及如何MAP_ANONYMOUS影响这一点并不新鲜,但一般来说,写入映射区域并不是即时的。linux.die 上的联机帮助页说:

MAP_SHARED
共享此映射。映射的更新对于映射该文件的其他进程是可见的,并被传递到底层文件。在调用 msync(2) 或 munmap() 之前,该文件实际上可能不会被更新。

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

于 2012-07-31T23:15:02.740 回答