C++标准库中的<mutex>、<conditional_variable>库和共享内存

5

POSIX线程的C API需要设置特殊标志,以便在共享内存之间共享互斥锁-请参阅sem_init()。我不太清楚区别是什么,但我尝试在共享内存中使用C++ std::condition_variable时遇到了问题-出现了分段故障。我在C++文档或构造函数中没有看到任何提到这一点的内容。我想知道如何/是否可以在共享内存中使用C++线程互斥锁。下面是我的测试代码供参考。注意,squeue只是一个简单的(POD)静态大小的循环队列,不相关的东西被省略:

#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
#include "squeue.h"
#define SHM_FILENAME "/shimmy-foo"
#define SQUEUE_LENGTH 10

typedef struct {
        squeue<int,SQUEUE_LENGTH> queue;
        std::mutex mutex;
        std::condition_variable_any condvar;
} SHM;

int main() {
        int shm_fd = 0;
        SHM * shm_ptr = NULL;
        squeue<int,SQUEUE_LENGTH> * queue = NULL;
        std::mutex * mutex;
        std::condition_variable_any * condvar;

        // Init SHM. ftruncate() will zero area.
        if((shm_fd = shm_open(SHM_FILENAME, O_CREAT|O_RDWR|O_EXCL, S_IREAD|S_IWRITE)) == -1 ) {
            fprintf (stderr, "Could not open shm object. %s\n", strerror(errno));
            return errno;
        }
        else {
            fprintf (stderr, "Open shm OK. %d\n", shm_fd);
        }
        ftruncate(shm_fd, sizeof(SHM));

        // Connect the shmptr pointer to set to the shared memory area,
        // with desired permissions
        if((shm_ptr = (SHM*)mmap(0, sizeof(SHM), PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
            fprintf (stderr, "Could not map shm. %s\n", strerror(errno));
            return errno;
        }
        else {
        fprintf(stderr, "Mapped shm OK. %p\n", shm_ptr);
        }

        // Create queue and mutex.
        queue = new(&shm_ptr->queue) squeue<int,SQUEUE_LENGTH>();
        mutex = new(&shm_ptr->mutex) std::mutex();
        condvar = new(&shm_ptr->condvar) std::condition_variable_any();

        srand(time(NULL));
        while(true) {
            cout << "Waiting on lock" << endl;
            mutex->lock();
            if(!queue->full()) {
                int value = rand()%100;
                queue->push(value);
                cout << "Pushed " << value << endl;
            } else {
                cout << "Que is full!" << endl;
            };
            condvar->notify_all(); //Seg fault.
            mutex->unlock();
            sleep(1);
        }
    }

@FrancisMBacon:这就是我所说的:如果与错误无关,完全删除它! - stefan
@n.m. 我怎么知道互斥锁等不是 POD?还有,为什么? - Francis M. Bacon
1
@n.m.: 有人用过 Boost.IPC 吗?有状态的分配器?我们为什么要做这么多工作来给元组分配器扩展... - Kerrek SB
你不需要这样做。这就是重点。您还不能使用分配内存并存储指针的对象,超出您的控制范围,而std::queue绝对会这样做(如果我没记错,您在代码中使用了std::queue,直到它从问题中删除)。为什么 --- 请参见此处 - n. m.
@n.m 我没有使用 std::queue,因此我猜想 std::condition_variable 中一定有动态内存分配。 - Francis M. Bacon
显示剩余5条评论
1个回答

5
我使用类似的模式,但是标准的互斥锁和条件变量并不适用于进程间共享。原因在于POSIX要求对进程共享的互斥锁和条件变量设置PTHREAD_PROCESS_SHARED属性,但标准C++原语不这样做。在Windows上可能更加复杂。
您可以尝试使用boost的 进程共享互斥锁进程共享条件变量。或为POSIX接口创建自己的包装器。
也有可能squeue超出其缓冲区而破坏内存,覆盖了在内存中位于互斥锁和条件变量上方的struct SHM。我建议注释掉推入队列的代码,看看是否仍然会崩溃。我尝试将您的代码与队列代码注释掉后一起运行,效果如预期。
您可能还想使用condition_variable而不是condition_variable_any,因为后者维护其自己的互斥锁,但是如果您在保持关联的互斥锁已锁定的情况下通知该条件变量,则不需要使用那个互斥锁。

1
“很棒,C++类不会设置PTHREAD_PROCESS_SHARED,这几乎是我在寻找的答案。 我用pthread_(cond|mutex)_t替换了C++代码,现在一切都正常工作了。谢谢。” - Francis M. Bacon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接