Стандарт POSIX позволяет именованному блоку общей памяти содержать взаимное исключение и условную переменную?
Мы пытались использовать взаимное исключение и условную переменную для синхронизации доступа к именованной общей памяти двумя процессами в (совместимой на POSIX) системе LynuxWorks LynxOS-SE.
Один блок общей памяти называют "/sync"
и содержит взаимное исключение, и условная переменная, другой "/data"
и содержит фактические данные, к которым мы синхронизируем доступ.
Мы видим отказы от pthread_cond_signal()
если оба процесса не работают mmap()
призывает точно тот же порядок, или если один процесс mmaps в некоторой другой части общей памяти перед ним mmaps "/sync"
память.
Этот пример кода почти так короток, как я могу сделать его:
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
static const string shm_name_sync("/sync");
static const string shm_name_data("/data");
struct shared_memory_sync
{
pthread_mutex_t mutex;
pthread_cond_t condition;
};
struct shared_memory_data
{
int a;
int b;
};
//Create 2 shared memory objects
// - sync contains 2 shared synchronisation objects (mutex and condition)
// - data not important
void create()
{
// Create and map 'sync' shared memory
int fd_sync = shm_open(shm_name_sync.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
ftruncate(fd_sync, sizeof(shared_memory_sync));
void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0);
shared_memory_sync* p_sync = static_cast (addr_sync);
// init the cond and mutex
pthread_condattr_t cond_attr;
pthread_condattr_init(&cond_attr);
pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&(p_sync->condition), &cond_attr);
pthread_condattr_destroy(&cond_attr);
pthread_mutexattr_t m_attr;
pthread_mutexattr_init(&m_attr);
pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&(p_sync->mutex), &m_attr);
pthread_mutexattr_destroy(&m_attr);
// Create the 'data' shared memory
int fd_data = shm_open(shm_name_data.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
ftruncate(fd_data, sizeof(shared_memory_data));
void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0);
shared_memory_data* p_data = static_cast (addr_data);
// Run the second process while it sleeps here.
sleep(10);
int res = pthread_cond_signal(&(p_sync->condition));
assert(res==0); // <--- !!!THIS ASSERT WILL FAIL ON LYNXOS!!!
munmap(addr_sync, sizeof(shared_memory_sync));
shm_unlink(shm_name_sync.c_str());
munmap(addr_data, sizeof(shared_memory_data));
shm_unlink(shm_name_data.c_str());
}
//Open the same 2 shared memory objects but in reverse order
// - data
// - sync
void open()
{
sleep(2);
int fd_data = shm_open(shm_name_data.c_str(), O_RDWR, S_IRUSR|S_IWUSR);
void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0);
shared_memory_data* p_data = static_cast (addr_data);
int fd_sync = shm_open(shm_name_sync.c_str(), O_RDWR, S_IRUSR|S_IWUSR);
void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0);
shared_memory_sync* p_sync = static_cast (addr_sync);
// Wait on the condvar
pthread_mutex_lock(&(p_sync->mutex));
pthread_cond_wait(&(p_sync->condition), &(p_sync->mutex));
pthread_mutex_unlock(&(p_sync->mutex));
munmap(addr_sync, sizeof(shared_memory_sync));
munmap(addr_data, sizeof(shared_memory_data));
}
int main(int argc, char** argv)
{
if(argc>1)
{
open();
}
else
{
create();
}
return (0);
}
Запустите эту программу без args, затем другую копию с args, и первый перестанет работать при утверждать проверке pthread_cond_signal()
. Но измените порядок open()
функция к mmap()
"/sync
"память перед "/data"
и это будет все хорошо работать.
Это походит на главную ошибку в LynxOS мне, но LynuxWorks утверждает, что использование взаимного исключения и условной переменной в именованной общей памяти таким образом не охвачено стандартом POSIX, таким образом, им не интересно.
Кто-либо может определить, нарушает ли этот код действительно на самом деле POSIX?
Или у кого-либо есть какая-либо убедительная документация, что это - совместимый POSIX?
Править: мы знаем это PTHREAD_PROCESS_SHARED
POSIX и поддерживается LynxOS. Область конкуренции - могут ли взаимные исключения и семафоры использоваться в именованной общей памяти (поскольку мы сделали), или если POSIX только позволяет им использоваться, когда один процесс создает и mmaps общая память и затем разветвляет второй процесс.
Я легко могу понять, как PTHREAD_PROCESS_SHARED может быть сложно реализовать на уровне ОС (например, в MacOS этого нет, кроме rwlocks, кажется). Но только из чтения стандарта кажется, что у вас есть дело.
Для полноты картины, возможно, вы захотите проверить sysconf(_SC_THREAD_PROCESS_SHARED) и возвращаемое значение вызовов функции *_setpshared() - возможно, вас ждет еще один "сюрприз" (но из комментариев я вижу, что вы уже проверили, что SHARED действительно поддерживается).
@JesperE: возможно, вы захотите обратиться к документации по API в OpenGroup вместо документации HP.
Функция pthread_mutexattr_setpshared
может использоваться для разрешения доступа к мьютексу pthread в разделяемой памяти любому потоку который имеет доступ к этой памяти, даже к потокам в разных процессах. Согласно эта ссылка , pthread_mutex_setpshared
соответствует POSIX P1003.1c. (То же самое и с условной переменной, см. pthread_condattr_setpshared
.)
Связанный вопрос: условные переменные pthread в Linux, странное поведение
Возможно, в pthread_cond_t
(без pshared) есть какие-то указатели, поэтому вы должны поместить их в одинаковые адреса в обоих потоках/процессах. С одинаково упорядоченными mmaps вы можете получить одинаковые адреса для обоих процессов.
В glibc указатель в cond_t был на дескриптор потока, принадлежащего мьютексу/cond.
Вы можете управлять адресами с помощью не-NULL первого параметра mmap.