C语言如何使用semaphores(信号量)对共享缓冲区加锁实现同步
问题原因
- 你当前的代码属于典型的多进程竞态问题:共享缓冲区没有访问控制,多个读写进程可以同时操作缓冲区,读写操作中途会被其他进程打断,因此出现0和1混杂的输出结果。
- 你要求的同步逻辑本质是两个规则:
- 缓冲区互斥访问:同一时间只能有一个进程执行读或者写操作
- 操作顺序保证:0的写入、读取全流程完成后,再执行1的相关操作
解决方案实现
我们采用POSIX无名信号量实现同步,由于信号量需要在多个进程间共享,因此需要将信号量存储在共享内存区域中。
完整修改后代码
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <sys/times.h> #include <fcntl.h> #include <stdlib.h> #include <sys/wait.h> #include <semaphore.h> // 新增信号量头文件 #define FILE_SIZE 11 #define NO_PROC 10 int DelayCount = 0; int readerID = 0; int writerID = 0; char* shared_buffer; sem_t* shared_mutex; // 互斥信号量,控制缓冲区访问 sem_t* write0_done; // 顺序控制信号量,标记writer0写+读完成 const int LOOP_TIME = 3; /* The reader */ void reader() { int i,j; char results[FILE_SIZE]; printf("Reader %d (pid = %d) arrives\n", readerID, getpid()); for (i=0; i<LOOP_TIME; i++) { printf(" Reader %d starts round %d\n", readerID,i); // 申请缓冲区访问权限 sem_wait(shared_mutex); /* read data from shared data */ for (j=0; j<FILE_SIZE; j++) { results[j] = shared_buffer[j]; sleep(2); } /* display result */ results[j] = 0; printf("Reader %d (round %d) gets results : %s\n", readerID, i, results); // 释放缓冲区访问权限 sem_post(shared_mutex); // 第一轮读完0后,标记0操作完成 if(i == 0) { sem_post(write0_done); } sleep(1); } } /* The writer */ void writer() { int i,j; char data[FILE_SIZE]; for (j=0; j<FILE_SIZE-1; j++) { data[j]= writerID + '0'; } data[j]= 0; printf("Writer %d (pid = %d) arrives, writing %s to buffer\n", writerID, getpid(), data); // 写1的进程先等待0的写+读全流程完成 if(writerID == 1) { sem_wait(write0_done); } for (i=0; i<LOOP_TIME; i++){ printf(" Write %d starts round %d\n", writerID,i); // 申请缓冲区访问权限 sem_wait(shared_mutex); /* write to shared buffer */ for (j=0; j<FILE_SIZE-1; j++) { shared_buffer[j]= data[j]; sleep(1); } printf("Writer %d (round %d) finishes\n", writerID, i); // 释放缓冲区访问权限 sem_post(shared_mutex); sleep(1); } } /* creating readers and writers */ void create_reader() { if (0 == fork()) { reader(); exit(0); } readerID++; } void create_writer() { if (0 == fork()) { writer(); exit(0); } writerID++; } /* main */ int main() { char InitData[]="0000000000\n"; int i; int fd; // 共享内存大小扩容:缓冲区大小 + 两个信号量的大小 int shm_size = FILE_SIZE + sizeof(sem_t) * 2; /* creates a memory region shared by all child processes */ fd = open("race.dat", O_RDWR | O_CREAT | O_TRUNC, 0600); if ( fd < 0 ) { perror("race.dat "); exit(1); } // 扩容文件到共享内存大小 ftruncate(fd, shm_size); write(fd, InitData, FILE_SIZE); unlink("race.dat"); void *shm_ptr = mmap(0, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if ( shm_ptr == (caddr_t) -1) { perror("mmap"); exit(2); } // 分配共享内存空间:前两个位置存信号量,后面存缓冲区 shared_mutex = (sem_t*)shm_ptr; write0_done = (sem_t*)shm_ptr + 1; shared_buffer = (char*)shm_ptr + sizeof(sem_t)*2; // 初始化信号量:第二个参数1代表进程间共享 sem_init(shared_mutex, 1, 1); // 互斥信号量初始值1 sem_init(write0_done, 1, 0); // 顺序信号量初始值0,等待0操作完成 /* Create readers and writes */ create_reader(); sleep(1); create_writer(); sleep(1); create_writer(); /* Wait until all children terminate */ for (i=0; i<(readerID+writerID); i++) { wait(NULL); } // 销毁信号量、释放资源 sem_destroy(shared_mutex); sem_destroy(write0_done); munmap(shm_ptr, shm_size); close(fd); return 0; }
运行说明
编译时需要链接pthread库:gcc your_code.c -o sync_demo -lpthread
内容的提问来源于stack exchange,提问作者ark
相关产品推荐
相关产品推荐

