忙碌的等待和共享内存

时间:2015-05-09 04:04:30

标签: c fork

我目前正在尝试实现一个C程序,为给定进程创建共享内存区域,然后将此进程分配到一个子进程中,使子进程写入共享内存的给定位置并让父进程等待直到孩子写到那个位置。我使用了一个简单的忙等待方法,让父进程等到孩子用while循环结束他的写作。问题是它只有在我在该循环中引入一些延迟时才有效。任何人都知道为什么会这样?

代码:

int shmid;
int *shmptr;
int i, j, ret;

key_t key = SHM_KEY;

// Create shared memory segment
if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0600)) < 0)
{
    printf("shmget error: %s\n", strerror(errno));
    return -1;
}

// Attach shared memory segment
if ((shmptr = shmat(shmid, 0, 0)) == (void *) -1)
{
    puts("shmat error");
    return -1;
}   

shmptr[6] = '%';

ret = fork();
if (ret > 0) 
{/*parent*/
    /*here is the loop that implements the busy waiting approach*/
    while (shmptr[6] != '^') {
        sleep(1);
    }
    for (i = 0; i < 7; i++) printf("%c", shmptr[i]);
    puts("");

    int status = 0;
    wait(&status);
}
else 
{/*child*/
    shmptr[0] = 's';
    shmptr[1] = 'h';
    shmptr[2] = 'a';
    shmptr[3] = 'r';
    shmptr[4] = 'e';
    shmptr[5] = 'd';

    /*tell parent process ithas finished its writing*/
    shmptr[6] = '^';

    exit(0);
}

3 个答案:

答案 0 :(得分:1)

易失性(参见前面的评论可能仅适用于单核方案)。假设您在具有多个内核的CPU上运行,则需要原子地处理共享内存区域中每个位置的访问。如果使用符合C ++ 11的编译器,则需要假定该区域的每个位置都是std::atomic<int>类型。

由于您可能正在使用C而不是C ++,并且使用GCC,因此请考虑使用原子内置GCC Atomic Builtins

所以,你的

shmptr[0] = 's';

语句应该用原子集运算符替换:

_sync_val_compare_and_swap(&shmptr[0], 's');

并为所有集合执行等效操作。然后,在循环中执行等效操作以检查返回值(这将是您想要的字符)。

另一个答案中的信号量可能有效,但是,不能保证其他位置将通过CPU写入后电路,通过源上的缓存控制器,等等通过接收CPU的控制器,特别是如果被访问的地址跨越缓存行。

我还建议做一些sleep(0)或yield(),以允许其他程序在主程序运行的核心上获取时间片,否则,你将浪费CPU资源。

答案 1 :(得分:0)

您希望同步对共享内存(SHM)的访问。

这可以通过使用semphore来完成。

  1. fork()给孩子打电话sem_open()之前。
  2. 在阅读SHM之前让父母等待sem_wait()
  3. 在写完SHM后让孩子给sem_post()打电话。

答案 2 :(得分:0)

我想现在发生的事情是孩子终止得太快了。

您可以使用非挂起waitpid(2)并将其添加到循环中:

/*here is the loop that implements the busy waiting approach*/
int status= 0;
while (shmptr[6] != '^') {
    if (waitpid(ret, &status, WNOHANG) == ret) break;
    sleep(1);
}

但是,正如我评论的那样, 忙着等待在Linux用户空间程序中总是总是 (至少它强调你的系统)。阅读sem_overview(7),或者设置pipe(7)eventfd(2)signalfd(2)poll(2)。或设置一个SIGCHLD信号处理程序(仔细阅读 signal(7)),只需设置一个volatile sigatomic_t标记即可在循环中进行测试。

您还应该声明volatile int*shmptr;,因为编译器可能已经优化了它的使用。