如何利用Semaphores确保C语言两进程的文件读写执行顺序?
用System V信号量同步C语言读写进程
要实现写进程优先执行、读进程仅在写完成后启动的需求,你可以用System V信号量(对应semget/semop/semctl系统调用)来做进程同步。核心思路是用一个初始值为0的信号量:写进程完成后触发信号量,读进程则阻塞等待这个信号量被激活。
完整实现代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sem.h> #include <sys/wait.h> #include <time.h> #include <fcntl.h> #include <string.h> // 定义semctl需要的联合体(部分系统需要手动定义) union semun { int val; // 用于SETVAL命令 struct semid_ds *buf; // 用于IPC_STAT/IPC_SET命令 unsigned short *array; // 用于GETALL/SETALL命令 }; // P操作:等待信号量(减1,若信号量为0则阻塞) void sem_wait(int sem_id) { struct sembuf sem_op; sem_op.sem_num = 0; // 操作第0个信号量 sem_op.sem_op = -1; // 减1操作 sem_op.sem_flg = 0; // 阻塞模式 semop(sem_id, &sem_op, 1); } // V操作:释放信号量(加1) void sem_post(int sem_id) { struct sembuf sem_op; sem_op.sem_num = 0; sem_op.sem_op = 1; // 加1操作 sem_op.sem_flg = 0; semop(sem_id, &sem_op, 1); } int main() { const char *filename = "random_chars.txt"; const int CHAR_COUNT = 2000; int sem_id; union semun sem_union; // 1. 创建信号量集:包含1个信号量,权限0666 sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); if (sem_id == -1) { perror("semget failed"); exit(EXIT_FAILURE); } // 2. 初始化信号量为0:读进程一开始会阻塞 sem_union.val = 0; if (semctl(sem_id, 0, SETVAL, sem_union) == -1) { perror("semctl SETVAL failed"); exit(EXIT_FAILURE); } // 3. 创建写进程 pid_t write_pid = fork(); if (write_pid == -1) { perror("fork write process failed"); exit(EXIT_FAILURE); } else if (write_pid == 0) { // 写进程逻辑 int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("open file for writing failed"); exit(EXIT_FAILURE); } // 初始化随机数生成器(用进程ID避免和读进程重复) srand(getpid()); for (int i = 0; i < CHAR_COUNT; i++) { char c = 'a' + (rand() % 26); write(fd, &c, 1); } close(fd); printf("写进程完成:已写入%d个随机小写字母\n", CHAR_COUNT); // 完成后释放信号量,通知读进程可以开始 sem_post(sem_id); exit(EXIT_SUCCESS); } // 4. 创建读进程 pid_t read_pid = fork(); if (read_pid == -1) { perror("fork read process failed"); exit(EXIT_FAILURE); } else if (read_pid == 0) { // 读进程逻辑:先等待信号量(写进程完成后才会继续) sem_wait(sem_id); int fd = open(filename, O_RDONLY); if (fd == -1) { perror("open file for reading failed"); exit(EXIT_FAILURE); } char buffer[CHAR_COUNT + 1]; ssize_t bytes_read = read(fd, buffer, CHAR_COUNT); close(fd); if (bytes_read == -1) { perror("read file failed"); exit(EXIT_FAILURE); } buffer[bytes_read] = '\0'; printf("读进程完成:已读取%d个字符,前20个字符为:%.20s\n", (int)bytes_read, buffer); exit(EXIT_SUCCESS); } // 5. 父进程等待两个子进程结束 waitpid(write_pid, NULL, 0); waitpid(read_pid, NULL, 0); // 6. 删除信号量,释放系统资源 if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) { perror("semctl IPC_RMID failed"); exit(EXIT_FAILURE); } return 0; }
关键逻辑说明
- 信号量初始化:用
semget创建私有信号量集,semctl将其初始值设为0。此时读进程执行sem_wait会阻塞,因为信号量为0无法减1。 - 写进程操作:完成文件写入后,调用
sem_post将信号量加1,解除读进程的阻塞状态。 - 读进程操作:启动后立即执行
sem_wait,直到信号量被写进程激活(值变为1),才开始读取文件。 - 资源清理:父进程等待子进程全部结束后,用
semctl的IPC_RMID命令删除信号量,避免系统资源泄漏。
编译与运行
用gcc编译:
gcc sem_sync.c -o sem_sync
运行程序:
./sem_sync
你会看到写进程先输出完成信息,随后读进程输出读取结果,确保了执行顺序。
内容的提问来源于stack exchange,提问作者Lian
相关产品推荐
相关产品推荐

