C语言父子进程TAILQ队列同步异常问题咨询
问题原因
fork()创建子进程后,父子进程拥有完全独立的虚拟地址空间——哪怕fork前初始化了TAILQ队列,fork后双方的队列只是各自地址空间里的副本。子进程往自己的队列插入消息,父进程的队列不会有任何变化,这就是父进程拿不到消息、无输出的根本原因。
解决方案:共享内存+同步锁实现跨进程队列共享
要让父子进程共享TAILQ队列,必须把队列结构、消息数据都放到共享内存中(让双方访问同一块物理内存),同时用进程间同步锁保护队列操作,避免并发修改破坏队列结构。
修正后的代码示例
#define _SVID_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/queue.h> #include <sys/mman.h> #include <pthread.h> #include <errno.h> // 消息结构体 struct message { int num; char status; TAILQ_ENTRY(message) queue_entry; }; // 共享内存中的总结构:包含队列头、同步锁、预分配消息池 #define MAX_MESSAGES 100 struct shared_data { TAILQ_HEAD(head_t, message) head; pthread_mutex_t mutex; struct message msg_pool[MAX_MESSAGES]; // 预分配消息,避免私有内存分配问题 int free_msg_count; // 空闲消息数量 }; int main() { // 创建匿名共享内存,父子进程自动共享 struct shared_data *shm_data = mmap(NULL, sizeof(struct shared_data), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (shm_data == MAP_FAILED) { perror("mmap failed"); exit(EXIT_FAILURE); } // 初始化共享资源(仅父进程在fork前执行) TAILQ_INIT(&shm_data->head); shm_data->free_msg_count = MAX_MESSAGES; // 设置互斥锁为进程共享属性,让父子进程都能使用 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); if (pthread_mutex_init(&shm_data->mutex, &attr) != 0) { perror("pthread_mutex_init failed"); exit(EXIT_FAILURE); } pthread_mutexattr_destroy(&attr); pid_t pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } if (!pid) // 子进程:循环生产消息 { while (1) { pthread_mutex_lock(&shm_data->mutex); // 检查消息池是否有空闲 if (shm_data->free_msg_count <= 0) { pthread_mutex_unlock(&shm_data->mutex); sleep(1); continue; } // 从消息池取一个空闲消息 struct message *p = NULL; for (int i = 0; i < MAX_MESSAGES; i++) { if (!TAILQ_ENTRY_IS_LINKED(&shm_data->msg_pool[i], queue_entry)) { p = &shm_data->msg_pool[i]; break; } } if (p) { p->num = 1; p->status = 's'; TAILQ_INSERT_TAIL(&shm_data->head, p, queue_entry); shm_data->free_msg_count--; } pthread_mutex_unlock(&shm_data->mutex); sleep(1); // 控制生产频率,方便观察输出 } } else // 父进程:循环消费消息 { while (1) { struct message *p = NULL; pthread_mutex_lock(&shm_data->mutex); p = TAILQ_FIRST(&shm_data->head); if (p) { TAILQ_REMOVE(&shm_data->head, p, queue_entry); shm_data->free_msg_count++; } pthread_mutex_unlock(&shm_data->mutex); if (p) { printf("Num %d and status %c\n", p->num, p->status); } sleep(1); } } // 清理代码(死循环不会执行,实际退出时需调用) pthread_mutex_destroy(&shm_data->mutex); munmap(shm_data, sizeof(struct shared_data)); return 0; }
关键细节说明
- 共享内存:用
mmap创建匿名共享内存(MAP_SHARED | MAP_ANONYMOUS),无需手动管理共享内存键,父子进程自动共享同一块物理内存区域。 - 预分配消息池:消息结构体直接放在共享内存的消息池中,避免子进程用
malloc分配私有内存(父进程无法访问私有内存)的问题。 - 进程共享锁:通过
pthread_mutexattr_setpshared将互斥锁设置为进程共享属性,确保父子进程能通过同一把锁同步队列操作,防止并发修改破坏TAILQ的链表结构。 - 锁保护范围:所有对队列、消息池的读写操作都在加锁后执行,操作完成立即解锁,减少锁竞争时间。
内容的提问来源于stack exchange,提问作者Astrogrammer
相关产品推荐
相关产品推荐

