C语言主进程主动等待子进程就绪的优化方案咨询
高效进程同步方案替代主动轮询等待
我编写了一段C语言代码,通过fork创建子进程,主进程需等待子进程完成初始化就绪后再执行后续逻辑。目前采用while (check_activation_flag() == 1)的主动等待方式实现,但该方式效率低下,希望替换此实现。以下是主进程、子进程代码及相关共享内存操作函数,寻求高效的进程同步方案:
主进程代码
ChildPID = fork(); if (ChildPID == 0) { execl("./child", "./child", NULL); exit(0); } else if (ChildPID < -1) { kill(getpid(), SIGINT); exit(EXIT_FAILURE); } atomSharedPidWriteSingle(sharedPID, getpid(), ChildPID); while (check_activation_flag() == 1) sleep(1); while (1) { sleep(1); while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Figlio con PID %d terminato con stato %d\n", pid, WEXITSTATUS(status)); } else { printf("Figlio con PID %d terminato in modo anomalo\n", pid); } } print_statistics(); update_energy_consumed(energy_demand); setEnAvailable(-energy_demand); energy_available = getEnAValabe(); printf("Total energy available: %d\n", energy_available); if (energy_available < 0) kill(getpid(), SIGUSR2); }
子进程代码
int main(){ signal(SIGTERM, handle_sigterm); int sharedPID = SharedPidInit("/my_shm"); shared_pid_data* a = (shared_pid_data*) malloc(sizeof(shared_pid_data)); init_ipc(); update_activation_flag(); while(1){ sleep(STEP_ATTIVATORE); a = SharedPidRead(sharedPID); if(a == NULL){ printf("Memoria condivisa fallita "); exit(EXIT_FAILURE); } srand((unsigned int)(time(NULL) ^ getpid())); int pos = rand() % a->dimensione; kill(a->atomi_pid[pos], SIGUSR1); } close(sharedPIDATOMO); aSharedPidDelete("/my_shm"); return EXIT_SUCCESS; }
相关操作函数
void atomSharedPidWriteSingle(int shm_fd, pid_t master,pid_t alimentatore,pid_t attivatore) { // Mappare la memoria condivisa nello spazio degli indirizzi del processo lock(); shared_pid_atom = mmap(0, sizeof(shared_pid_data), PROT_WRITE, MAP_SHARED, shm_fd, 0); if (shared_pid_atom == MAP_FAILED) { perror("mmap"); exit(1); } shared_pid_atom->alimentatore = alimentatore; shared_pid_atom->attivatore = attivatore; shared_pid_atom->master = master; unlock(); } void update_activation_flag() { lock(); shared_data->activation_flag += 1; unlock(); } int check_activation_flag() { int flag; flag = shared_data->activation_flag; return (flag>=shared_data->num_ChildProcesses?0:1); }
高效同步方案推荐
方案1:基于自定义信号的同步
利用进程间信号通知,主进程休眠等待子进程的就绪信号,完全避免轮询开销。
修改主进程
- 注册信号处理函数,设置就绪标志:
#include <signal.h> volatile sig_atomic_t child_ready = 0; void sig_child_ready(int sig) { child_ready = 1; } // 在fork操作前注册信号 signal(SIGUSR1, sig_child_ready);
- 替换原有轮询循环为信号等待逻辑:
// 替换 while (check_activation_flag() == 1) sleep(1); while (!child_ready) { sigset_t mask; sigemptyset(&mask); sigsuspend(&mask); // 进程休眠直到收到信号,无额外CPU消耗 }
修改子进程
在update_activation_flag()完成后,向主进程发送就绪信号:
// 在update_activation_flag()之后添加 shared_pid_data* pid_data = SharedPidRead(sharedPID); if (pid_data != NULL) { kill(pid_data->master, SIGUSR1); }
方案2:基于共享内存+POSIX条件变量
适合多子进程场景,可精准等待所有子进程就绪后唤醒主进程,同步逻辑更严谨。
1. 扩展共享内存结构体
添加进程共享的互斥锁和条件变量:
#include <pthread.h> typedef struct { // 原有字段 int activation_flag; int num_ChildProcesses; // 新增同步字段 pthread_mutex_t sync_mutex; pthread_cond_t sync_cond; } shared_data_t;
2. 初始化同步对象
在共享内存初始化时,设置进程共享属性:
// 初始化互斥锁 pthread_mutexattr_t mutex_attr; pthread_mutexattr_init(&mutex_attr); pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(&shared_data->sync_mutex, &mutex_attr); // 初始化条件变量 pthread_condattr_t cond_attr; pthread_condattr_init(&cond_attr); pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); pthread_cond_init(&shared_data->sync_cond, &cond_attr);
3. 修改主进程等待逻辑
// 替换原有轮询循环 pthread_mutex_lock(&shared_data->sync_mutex); while (check_activation_flag() == 1) { // 等待条件满足,自动释放锁,被唤醒后重新获取锁 pthread_cond_wait(&shared_data->sync_cond, &shared_data->sync_mutex); } pthread_mutex_unlock(&shared_data->sync_mutex);
4. 修改子进程就绪通知逻辑
更新update_activation_flag(),在所有子进程就绪时唤醒主进程:
void update_activation_flag() { pthread_mutex_lock(&shared_data->sync_mutex); shared_data->activation_flag += 1; // 检查是否所有子进程已就绪 if (shared_data->activation_flag >= shared_data->num_ChildProcesses) { pthread_cond_signal(&shared_data->sync_cond); } pthread_mutex_unlock(&shared_data->sync_mutex); }
方案3:基于管道的同步
利用匿名管道的阻塞读特性,主进程等待子进程写入就绪标记,实现无轮询同步。
修改主进程
- 创建匿名管道,fork后关闭无用端:
int pipe_fd[2]; if (pipe(pipe_fd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } ChildPID = fork(); if (ChildPID == 0) { close(pipe_fd[0]); // 子进程关闭读端 execl("./child", "./child", NULL); exit(0); } else if (ChildPID < -1) { kill(getpid(), SIGINT); exit(EXIT_FAILURE); } close(pipe_fd[1]); // 主进程关闭写端
- 替换轮询循环为管道读操作:
// 等待子进程写入就绪标记 char ready_flag; read(pipe_fd[0], &ready_flag, 1); close(pipe_fd[0]);
修改子进程
在初始化完成后,向管道写入标记(需通过共享内存传递管道写端FD):
// 扩展shared_pid_data结构体,添加pipe_write_fd字段 // 在update_activation_flag()之后添加 shared_pid_data* pid_data = SharedPidRead(sharedPID); if (pid_data != NULL) { write(pid_data->pipe_write_fd, "1", 1); close(pid_data->pipe_write_fd); }
内容的提问来源于stack exchange,提问作者MiusiZ
相关产品推荐
相关产品推荐

