如何在主服务器与代理服务器间实现进程级互斥锁及条件变量
Alright, let's get your mainserver-proxy shared memory setup properly synchronized with mutexes and condition variables! The key here is to coordinate their actions so the proxy doesn't overwrite data while the mainserver is reading, and the mainserver doesn't waste CPU cycles polling for new filenames. Here's a step-by-step implementation tailored to your workflow:
Core Synchronization Design
We'll use POSIX pthread mutexes and condition variables configured for inter-process sharing. The setup needs two condition variables:
- One for the proxy to signal the mainserver when a new filename is ready
- One for the mainserver to signal the proxy when the file content is available
Plus a mutex to protect all shared state from race conditions.
Shared Memory Structure
First, define a struct that holds both your data and synchronization primitives. This lives entirely in shared memory so both processes can access it:
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define SHM_NAME "/my_shared_memory" #define MAX_FILENAME_LEN 256 #define MAX_CONTENT_SIZE 4096 // Shared data structure with synchronization primitives typedef struct { pthread_mutex_t mutex; pthread_cond_t cond_proxy_to_main; // Proxy → Mainserver: New filename ready pthread_cond_t cond_main_to_proxy; // Mainserver → Proxy: Content ready char filename[MAX_FILENAME_LEN]; char file_content[MAX_CONTENT_SIZE]; int has_new_filename; // Flag: 1 if proxy wrote a new filename int content_ready; // Flag: 1 if mainserver loaded content int is_initialized; // Flag: 1 if sync primitives are set up } SharedData;
Proxy Server Code
This is the process that writes filenames and reads the resulting content:
int main() { // Create/open shared memory int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666); if (shm_fd == -1) { perror("shm_open failed"); exit(EXIT_FAILURE); } ftruncate(shm_fd, sizeof(SharedData)); SharedData* shared_data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (shared_data == MAP_FAILED) { perror("mmap failed"); exit(EXIT_FAILURE); } // Initialize sync primitives (only once, first process to start does this) if (!shared_data->is_initialized) { pthread_mutexattr_t mutex_attr; pthread_mutexattr_init(&mutex_attr); pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(&shared_data->mutex, &mutex_attr); pthread_mutexattr_destroy(&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->cond_proxy_to_main, &cond_attr); pthread_cond_init(&shared_data->cond_main_to_proxy, &cond_attr); pthread_condattr_destroy(&cond_attr); shared_data->is_initialized = 1; shared_data->has_new_filename = 0; shared_data->content_ready = 1; // Start ready for first filename } // Example workflow: Send a filename and wait for content const char* target_file = "example.txt"; pthread_mutex_lock(&shared_data->mutex); // Wait for previous content to be processed (if any) while (!shared_data->content_ready) { pthread_cond_wait(&shared_data->cond_main_to_proxy, &shared_data->mutex); } shared_data->content_ready = 0; // Write the new filename strncpy(shared_data->filename, target_file, MAX_FILENAME_LEN - 1); shared_data->filename[MAX_FILENAME_LEN - 1] = '\0'; shared_data->has_new_filename = 1; // Signal mainserver to process the filename pthread_cond_signal(&shared_data->cond_proxy_to_main); // Wait for mainserver to load content while (!shared_data->content_ready) { pthread_cond_wait(&shared_data->cond_main_to_proxy, &shared_data->mutex); } // Read and use the content printf("Proxy received content:\n%s\n", shared_data->file_content); pthread_mutex_unlock(&shared_data->mutex); // Cleanup (optional, run when shutting down) // munmap(shared_data, sizeof(SharedData)); // close(shm_fd); // shm_unlink(SHM_NAME); return 0; }
Main Server Code
This is the process that reads filenames, loads content, and signals the proxy:
int main() { // Open existing shared memory (created by proxy) int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666); if (shm_fd == -1) { perror("shm_open failed"); exit(EXIT_FAILURE); } SharedData* shared_data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (shared_data == MAP_FAILED) { perror("mmap failed"); exit(EXIT_FAILURE); } // Ensure sync primitives are initialized if (!shared_data->is_initialized) { perror("Shared memory not initialized by proxy"); exit(EXIT_FAILURE); } // Continuous loop to process filenames while (1) { pthread_mutex_lock(&shared_data->mutex); // Wait for proxy to send a new filename while (!shared_data->has_new_filename) { pthread_cond_wait(&shared_data->cond_proxy_to_main, &shared_data->mutex); } // Load file content into shared memory FILE* fp = fopen(shared_data->filename, "r"); if (fp != NULL) { memset(shared_data->file_content, 0, MAX_CONTENT_SIZE); fread(shared_data->file_content, 1, MAX_CONTENT_SIZE - 1, fp); fclose(fp); } else { strcpy(shared_data->file_content, "Error: File not found or unreadable"); } // Update state and signal proxy shared_data->has_new_filename = 0; shared_data->content_ready = 1; pthread_cond_signal(&shared_data->cond_main_to_proxy); pthread_mutex_unlock(&shared_data->mutex); } // Cleanup (run if loop exits) // pthread_mutex_destroy(&shared_data->mutex); // pthread_cond_destroy(&shared_data->cond_proxy_to_main); // pthread_cond_destroy(&shared_data->cond_main_to_proxy); // munmap(shared_data, sizeof(SharedData)); // close(shm_fd); return 0; }
Key Notes to Remember
- Process-Shared Attributes: The
PTHREAD_PROCESS_SHAREDflag is critical—without it, mutexes/cond vars only work within a single process. - Loop Conditions: Always use
while(notif) when waiting on condition variables. This handles "spurious wakeups" (unexpected wakeups that don't mean your condition is met). - Initialization Guard: The
is_initializedflag prevents multiple processes from re-initializing sync primitives, which causes undefined behavior. - Error Handling: I added basic error checking here, but in production code, you should validate every system call return value (e.g.,
pthread_mutex_lockcan fail!). - Cleanup: Don't forget to unmap shared memory and unlink it when your processes shut down to avoid leftover resources.
内容的提问来源于stack exchange,提问作者cranberry

