You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 17:05:31