一、信号量
信号量有两种,一种的有名信号,一种是无名信号。有名信号一般用于进程间同步,无名信号一般用于于线程间同步。创建或打开一个信号的函数:
1 2 |
#include <semaphore.h> sem_t *sem_open(const char *name, int oflag, ...); |
name参数致命信号量的名字,由于信号量内部保存在系统内核中,多个进程间可以直接通过指定信号量的名字来相互通信。oflag表示信号的属性:可读、可写、可执行或者其他,如果指定了O_CREAT属性,则会创建信号量,否则表示打开已有信号量。后面的可选参数有两个:
mode
: 信号量的权限,和文件属性一样,0755或者其他。value
: 默认值,信号量内部维持了一个计数,value就是设置计数的默认值。
操作信号量的方法:
1 2 3 |
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后,阻塞的信号量才最继续往下执行。
关闭信号量:
1 2 |
int sem_close(sem_t *sem); int sem_unlink(const char *name); |
close用于关闭信号量,这个操作会导致信号量的引用计数减1。当引用计数到达0后,执行过unlink的信号量会自动删除。
信号量的主要应用场景是同步,适合同步多个进程的状态同事到达某一个点。
二、示例代码
示例代码中创建了两个进程,每个进程都对一个变量执行三次自加操作。信号量的作用就是保证两个进程对变量自加操作的进度相同(意思是你自加了1次,我也自加1次,不存在你自加2次我才自加1次的情况)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#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; } |
执行结果:
可以看到,两个进程间的字节都是交替进行的。
评论