64位Ubuntu系统下使用-m32编译选项时sem_wait调用阻塞的解决方法咨询
解决32位编译下sem_wait阻塞的问题
这个问题的核心原因大概率是信号量初始化不规范或者32/64位环境下sem_t结构的内存布局差异导致的,我来给你拆解具体原因和可行的解决方案:
问题根源分析
- 缺失信号量初始化步骤:你提供的代码片段里没有展示共享内存的创建和sem_t的初始化逻辑。如果在
fork()前没有正确初始化信号量(尤其是进程间共享的信号量),32位环境下sem_wait会因为信号量处于无效状态而无限阻塞。 - 32/64位sem_t结构差异:Linux系统中,32位和64位程序的
sem_t结构体大小、成员偏移可能不同。如果共享内存是由64位程序创建的,32位程序直接访问会导致信号量读写错误,进而触发阻塞。
解决方案一:规范初始化无名信号量(基于共享内存)
如果坚持用共享内存存放无名信号量,必须在fork()前完成共享内存的创建、映射和信号量的初始化,确保父子进程都能访问到正确的信号量结构。修改后的完整代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/wait.h> #define HSDPA_MT_PRINT_LOCK_SEM_IDX 0 #define HSDPA_MT_ZERO 0 // 定义包含信号量的共享内存结构 typedef struct { sem_t sem; } SharedSemData; SharedSemData *SemOpenShmAddr; void func1() { sleep(2); sem_wait(&SemOpenShmAddr->sem); // 临界区操作 printf("func1: 进入临界区\n"); sem_post(&SemOpenShmAddr->sem); printf(" exit func1\n"); } void func2() { sleep(2); sem_wait(&SemOpenShmAddr->sem); // 临界区操作 printf("func2: 进入临界区\n"); sem_post(&SemOpenShmAddr->sem); printf(" exit func2\n"); } int main (int argc, char **argv) { pid_t pid; int shm_fd; const char *shm_name = "/mt_print_lock_shm"; // 1. 创建/打开共享内存 shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666); if (shm_fd == -1) { perror("shm_open 失败"); exit(EXIT_FAILURE); } // 2. 设置共享内存大小为结构体大小 if (ftruncate(shm_fd, sizeof(SharedSemData)) == -1) { perror("ftruncate 失败"); exit(EXIT_FAILURE); } // 3. 映射共享内存到进程地址空间 SemOpenShmAddr = mmap(NULL, sizeof(SharedSemData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (SemOpenShmAddr == MAP_FAILED) { perror("mmap 失败"); exit(EXIT_FAILURE); } // 4. 初始化信号量:第二个参数为1表示允许进程间共享,第三个参数为1表示初始值为1(互斥锁) if (sem_init(&SemOpenShmAddr->sem, 1, 1) == -1) { perror("sem_init 失败"); exit(EXIT_FAILURE); } pid = fork(); if (HSDPA_MT_ZERO == pid) { // 子进程 func1(); munmap(SemOpenShmAddr, sizeof(SharedSemData)); close(shm_fd); } else { // 父进程 func2(); wait(NULL); // 等待子进程结束 // 清理资源 sem_destroy(&SemOpenShmAddr->sem); munmap(SemOpenShmAddr, sizeof(SharedSemData)); close(shm_fd); shm_unlink(shm_name); } return 0; }
32位编译注意事项
- 先安装32位开发依赖:
sudo apt-get install libc6-dev-i386 lib32stdc++6 - 编译时需要链接实时库
-lrt(因为shm_open属于实时扩展):gcc -g -m32 sem_sample1.c -o sem_sample1 -lpthread -lrt
解决方案二:使用有名信号量(更简单的跨位兼容方案)
有名信号量由操作系统内核管理,不需要手动处理共享内存,天然避免32/64位结构差异问题。修改后的代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <sys/wait.h> #define HSDPA_MT_PRINT_LOCK_SEM_IDX 0 #define HSDPA_MT_ZERO 0 sem_t *print_sem; void func1() { sleep(2); sem_wait(print_sem); // 临界区操作 printf("func1: 进入临界区\n"); sem_post(print_sem); printf(" exit func1\n"); } void func2() { sleep(2); sem_wait(print_sem); // 临界区操作 printf("func2: 进入临界区\n"); sem_post(print_sem); printf(" exit func2\n"); } int main (int argc, char **argv) { pid_t pid; const char *sem_name = "/mt_print_lock_sem"; // 创建/打开有名信号量:初始值设为1(互斥锁) print_sem = sem_open(sem_name, O_CREAT | O_RDWR, 0666, 1); if (print_sem == SEM_FAILED) { perror("sem_open 失败"); exit(EXIT_FAILURE); } pid = fork(); if (HSDPA_MT_ZERO == pid) { // 子进程 func1(); sem_close(print_sem); } else { // 父进程 func2(); wait(NULL); // 等待子进程结束 sem_close(print_sem); sem_unlink(sem_name); // 删除有名信号量 } return 0; }
32位编译命令
同样需要链接-lrt和-lpthread:
gcc -g -m32 sem_sample1.c -o sem_sample1 -lpthread -lrt
关键总结
- 信号量必须在
fork()前完成初始化,确保父子进程共享同一个有效的信号量实例; - 无名信号量用于进程间同步时,必须放在共享内存中,且
sem_init的第二个参数要设为1; - 32位编译必须安装对应的32位依赖库,否则会出现链接错误;
- 有名信号量是更省心的跨位兼容方案,不需要手动管理共享内存。
内容的提问来源于stack exchange,提问作者mukuntan balasundaram
相关产品推荐
相关产品推荐

