进程共享pthread_cond_t在gcc 8.4失效 gcc 4.8.5运行正常问题求助
问题描述
近期将操作系统从RHEL 7.6(搭载gcc 4.8.5)升级到RHEL 8.4(搭载gcc 8.4)后,基于pthread_mutex_t和pthread_cond_t实现的跨进程同步逻辑出现异常。未使用C++标准库的std::mutex和std::condition_variable是因为这两个类不支持跨进程同步。该逻辑在gcc 4.8.5环境下运行正常,升级到gcc 8.4后失效。
复现步骤
- 先启动
First_Process进程 - 再启动
Second_Process进程 - 用Ctrl+C强制终止
Second_Process进程 - 重新运行
Second_Process进程,此时两个进程都不再有终端输出
相关代码
Binary_Semaphore.h
#ifndef BINARY_SEMAPHORE_H #define BINARY_SEMAPHORE_H #include <iostream> #include <string> #include <cstdlib> #include <unistd.h> #include <pthread.h> #include <sys/stat.h> #include <sys/mman.h> #include <sys/types.h> #include <fcntl.h> struct binary_semaphore_attr { pthread_mutex_t mutex; pthread_cond_t cvar; bool flag; }; class Binary_Semaphore { struct binary_semaphore_attr *bin_sem_attr; const std::string bin_sem_attr_shm_ID; const bool is_process_shared; const bool is_to_be_created; public: Binary_Semaphore(const std::string& bin_sem_attr_shm_ID, const bool is_process_shared, const bool is_to_be_created); ~Binary_Semaphore(); void post(); void wait(); template<typename T> static void create_shared_memory(T **shm, const std::string& shm_ID, const bool is_to_be_created, const int o_flags, const int mode) { int shm_fd; if ((shm_fd = shm_open(shm_ID.c_str(), o_flags, mode)) == -1) { std::cerr << "shm_open failed with " << shm_ID << "\n"; exit(EXIT_FAILURE); } if (is_to_be_created) { if (ftruncate(shm_fd, sizeof(T)) == -1) { std::cerr << "ftruncate failed with " << shm_ID << "\n"; exit(EXIT_FAILURE); } } if ((*shm = reinterpret_cast<T*>(mmap(nullptr, sizeof(T), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0))) == MAP_FAILED) { std::cerr << "mmap failed with " << shm_ID << "\n"; exit(EXIT_FAILURE); } close(shm_fd); } }; #endif
Binary_Semaphore.cpp
#include "Binary_Semaphore.h" Binary_Semaphore::Binary_Semaphore(const std::string& bin_sem_attr_shm_ID, const bool is_process_shared, const bool is_to_be_created) : bin_sem_attr_shm_ID(bin_sem_attr_shm_ID), is_process_shared(is_process_shared), is_to_be_created(is_to_be_created) { /* set binary semaphore attribute */ if (is_to_be_created) { if (is_process_shared) { create_shared_memory(&bin_sem_attr, bin_sem_attr_shm_ID, is_to_be_created, O_CREAT | O_RDWR | O_TRUNC, S_IRWXU | S_IRWXG); /* set mutex shared between processes */ pthread_mutexattr_t mutex_attr; pthread_mutexattr_init(&mutex_attr); pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST); pthread_mutex_init(&bin_sem_attr->mutex, &mutex_attr); pthread_mutexattr_destroy(&mutex_attr); /* set cvar shared between processes */ pthread_condattr_t cvar_attr; pthread_condattr_init(&cvar_attr); pthread_condattr_setpshared(&cvar_attr, PTHREAD_PROCESS_SHARED); pthread_cond_init(&bin_sem_attr->cvar, &cvar_attr); pthread_condattr_destroy(&cvar_attr); } else bin_sem_attr = new binary_semaphore_attr(); } else { if (is_process_shared) create_shared_memory(&bin_sem_attr, bin_sem_attr_shm_ID, is_to_be_created, O_RDWR, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP); } } Binary_Semaphore::~Binary_Semaphore() { if (is_to_be_created) { pthread_mutex_destroy(&bin_sem_attr->mutex); pthread_cond_destroy(&bin_sem_attr->cvar); } if (is_process_shared) { munmap(bin_sem_attr, sizeof(binary_semaphore_attr)); shm_unlink(bin_sem_attr_shm_ID.c_str()); } } void Binary_Semaphore::post() { if (pthread_mutex_lock(&bin_sem_attr->mutex) == EOWNERDEAD) pthread_mutex_consistent(&bin_sem_attr->mutex); bin_sem_attr->flag = true; pthread_mutex_unlock(&bin_sem_attr->mutex); pthread_cond_signal(&bin_sem_attr->cvar); } void Binary_Semaphore::wait() { if (pthread_mutex_lock(&bin_sem_attr->mutex) == EOWNERDEAD) pthread_mutex_consistent(&bin_sem_attr->mutex); while (!bin_sem_attr->flag) { if (pthread_cond_wait(&bin_sem_attr->cvar, &bin_sem_attr->mutex) == EOWNERDEAD) pthread_mutex_consistent(&bin_sem_attr->mutex); } bin_sem_attr->flag = false; pthread_mutex_unlock(&bin_sem_attr->mutex); }
First_Process.cpp
#include <iostream> #include <string> #include <chrono> #include <thread> #include "Binary_Semaphore.h" int main() { static const std::string BSEM = R"(/BSEM)"; Binary_Semaphore *binary_sem = new Binary_Semaphore(BSEM, true, true); while (true) { binary_sem->post(); std::cout << "signal posted" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1LL)); } }
Second_Process.cpp
#include <iostream> #include <string> #include "Binary_Semaphore.h" int main() { static const std::string BSEM = R"(/BSEM)"; Binary_Semaphore *binary_sem = new Binary_Semaphore(BSEM, true, false); while (true) { binary_sem->wait(); std::cout << "signal received" << std::endl; } }
问题原因
该问题本质不是gcc版本差异导致,而是RHEL 8对应的glibc版本升级后,进程共享同步原语、健壮互斥量的逻辑校验更严格,旧代码的未定义行为在旧版glibc下偶然可用,新版环境下问题暴露:
- 析构函数逻辑错误:所有使用共享内存的进程退出时都会调用
shm_unlink删除共享内存名,导致后续新启动的进程无法找到共享内存对象。 - 未显式初始化共享内存中的flag标志:仅依赖
ftruncate对共享内存的清零行为,未显式赋值初始值,存在未定义行为风险。 - 缺失完整的错误处理逻辑:健壮互斥量返回
ENOTRECOVERABLE等错误时未做处理,直接操作互斥量会导致流程卡住。 - 条件变量未显式指定时钟属性:不同glibc版本对进程共享条件变量的默认时钟设置存在差异,可能导致条件等待逻辑异常。
修复方案
- 修正析构函数逻辑,仅共享内存的创建者有权限执行
shm_unlink:
Binary_Semaphore::~Binary_Semaphore() { if (is_to_be_created) { pthread_mutex_destroy(&bin_sem_attr->mutex); pthread_cond_destroy(&bin_sem_attr->cvar); // 仅创建者删除共享内存名 shm_unlink(bin_sem_attr_shm_ID.c_str()); } if (is_process_shared) { munmap(bin_sem_attr, sizeof(binary_semaphore_attr)); } }
- 创建共享内存时显式初始化flag标志,在构造函数初始化完互斥量和条件变量后添加:
bin_sem_attr->flag = false;
- 完善健壮互斥量的错误处理,对
pthread_mutex_lock返回的所有错误做分支处理,遇到ENOTRECOVERABLE错误时重新初始化同步原语,避免流程卡住。 - 显式指定条件变量的时钟属性,初始化条件变量属性时添加:
pthread_condattr_setclock(&cvar_attr, CLOCK_MONOTONIC);
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

