一、信号量

信号量有两种,一种的有名信号,一种是无名信号。有名信号一般用于进程间同步,无名信号一般用于于线程间同步。创建或打开一个信号的函数:

#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, ...);

name参数致命信号量的名字,由于信号量内部保存在系统内核中,多个进程间可以直接通过指定信号量的名字来相互通信。oflag表示信号的属性:可读、可写、可执行或者其他,如果指定了O_CREAT属性,则会创建信号量,否则表示打开已有信号量。后面的可选参数有两个:

  1. mode: 信号量的权限,和文件属性一样,0755或者其他。
  2. value: 默认值,信号量内部维持了一个计数,value就是设置计数的默认值。

操作信号量的方法:

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);

wait表示等待一个信号量,当信号量中的计数不为0时,会把计数减1,然后继续执行后面的逻辑,如果计数为0,wait操作就会阻塞,直到计数不为0。trywait是wait的非阻塞版本,它会尝试wait,如果计数为0也直接返回。post操作会把计数加1,它和wait相对。只有通过它把计数加1后,阻塞的信号量才最继续往下执行。

关闭信号量:

int sem_close(sem_t *sem);
int sem_unlink(const char *name);

close用于关闭信号量,这个操作会导致信号量的引用计数减1。当引用计数到达0后,执行过unlink的信号量会自动删除。

信号量的主要应用场景是同步,适合同步多个进程的状态同事到达某一个点。

二、示例代码

示例代码中创建了两个进程,每个进程都对一个变量执行三次自加操作。信号量的作用就是保证两个进程对变量自加操作的进度相同(意思是你自加了1次,我也自加1次,不存在你自加2次我才自加1次的情况)。

#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>

#define SEMAPHORE_CHILD_NAME "sem_child"
#define SEMAPHORE_PARENT_NAME "sem_parent"

int main() {
    int i, count;
    sem_t *sem_child, *sem_parent;
    pid_t pid;

    // 创建两个信号量,用于父子进程间相互制约
    sem_child = sem_open(SEMAPHORE_CHILD_NAME, O_RDWR | O_CREAT, 0755, 1);
    sem_parent = sem_open(SEMAPHORE_PARENT_NAME, O_RDWR | O_CREAT, 0755, 1);
    if (sem_child == NULL || sem_parent == NULL) {
        perror("sem_open error");
        return -1;
    }
    // 信号量使用完成后删除
    sem_unlink(SEMAPHORE_CHILD_NAME);
    sem_unlink(SEMAPHORE_PARENT_NAME);

    pid = fork();

    count = 0;
    if (pid == 0) {
        for (i = 0; i < 3; i++) {
            // 等待父进程信号量
            sem_wait(sem_parent);
            count++;
            // 设置子进程信号量
            sem_post(sem_child);
            printf("pid[%u]: count = %d\n", getpid(), count);
        }
    } else if (pid > 0) {
        for (i = 0; i < 3; i++) {
            // 等待子进程信号量
            sem_wait(sem_child);
            count++;
            // 发送父进程信号量
            sem_post(sem_parent);
            printf("pid[%u]: count = %d\n", getpid(), count);
        }
    } else {
        perror("fork error");
    }

    // 关闭信号量
    sem_close(sem_parent);
    sem_close(sem_child);

    return 0;
}

执行结果:

可以看到,两个进程间的字节都是交替进行的。

最后修改:2020 年 02 月 23 日
如果觉得我的文章对你有用,请随意赞赏