进程同步与信号量实现

在多进程环境中,信号量用于进程间的同步,以确保多个进程在访问共享资源时不会发生冲突。信号量机制通常由 P 操作(等待)和 V 操作(释放)组成,通过控制资源的访问权限来实现同步。

信号量的实现

信号量是一种用于进程同步的整数,通常包含以下基本操作:

数据结构与系统调用

我们定义了一个信号量的结构体:

struct semaphore {
    int value;            // 信号量当前值
    struct spinlock lock; // 用于保护信号量的锁
};

系统调用包括:

uint64 sys_sem_init() {
    uint64 addr;
    int val;
    struct semaphore *sem;

    argaddr(0, &addr);
    argint(1, &val);

    sem = alloc_semaphore();  // 分配信号量内存
    if (!sem) return -1;

    sem_init(sem, val); // 初始化信号量
    if (copyout(myproc()->pagetable, addr, (char*)&sem, sizeof(sem)) < 0)
        return -1;

    return 0;
}

uint64 sys_sem_signal() {
    uint64 addr;
    struct semaphore *sem;

    argaddr(0, &addr);
    if (copyin(myproc()->pagetable, (char*)&sem, addr, sizeof(sem)) < 0)
        return -1;

    sem_signal(sem);  // 执行 V 操作
    return 0;
}

uint64 sys_sem_wait() {
    uint64 addr;
    struct semaphore *sem;

    argaddr(0, &addr);
    if (copyin(myproc()->pagetable, (char*)&sem, addr, sizeof(sem)) < 0)
        return -1;

    sem_wait(sem);  // 执行 P 操作
    return 0;
}

内存分配与信号量地址一致性

我们为信号量分配内存,确保多个进程能共享相同的信号量地址,避免因信号量地址不一致导致进程无法互相唤醒的问题。

struct semaphore* alloc_semaphore() {
    for (int i = 0; i < MAX_SEMAPHORES; i++) {
        if (semaphores[i] == 0) {
            semaphores[i] = (struct semaphore*)kalloc(); // kalloc 为内存分配函数
            return semaphores[i];
        }
    }
    return 0;
}

信号量应用示例

我们通过两个测试程序演示了信号量的实际应用:

  1. 交替打印数字: 通过两个进程交替打印奇数和偶数,父进程使用信号量通知子进程,确保打印顺序。