信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即 P(信号变量))和发 送(即 V(信号变量))信息操作。最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式, 叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。这里主要讨论二进制信号量。 由于信号量只能进行两种操作等待和发送信号,即 P(sv)和 V(sv),他们的行为是这样的: P(sv):如果 sv 的值大于零,就给它减 1;如果它的值为零,就挂起该进程的执行 V(sv):如果有其他进程因等待 sv 而被挂起,就让它恢复运行,如果没有进程因等待 sv 而挂起,就给它加 1。
信号灯也叫信号量,它能够用来同步进程的动作,不能传输数据。它的应用场景就像红绿灯,控制各 进程使用共享资源的顺序。Posix 无名信号灯用于线程同步, Posix 有名信号灯,System V 信号灯。信号灯 相当于一个值大于或等于 0 计数器,信号灯值大于 0,进程就可以申请资源,信号灯值-1,如果信号灯值为 0,一个进程还想对它进行-1,那么这个进程就会阻塞,直到信号灯值大于 1
使用 System V 信号量的步骤如下:
1. 使用 semget()创建或打开一个信号灯集。
2. 使用 semctl()初始化信号灯集,。
3. 使用 semop()操作信号灯值,即进行 P/V 操作。
P 操作:申请资源,申清完后信号灯值-1;
V 操作:释放资源,释放资源后信号灯值+1
Linux 提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会 对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件 sys/sem.h 中。
实验代码:
指定哪个进程运行,可以使用进程间通信的知识,或者使用信号量,这里以使用信号量为例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
union semun
{
int val;
};
int main(void)
{
int semid;
int key;
pid_t pid;
struct sembuf sem;
union semun semun_union;
key = ftok("./a.c", 0666);
semid = semget(key, 1, 0666 | IPC_CREAT);
semun_union.val = 0;
semctl(semid, 0, SETVAL, semun_union);
pid = fork();
if (pid > 0)
{
sem.sem_num = 0;
sem.sem_op = -1;
sem.sem_flg = 0;
semop(semid, &sem, 1);
printf("This is parents\n");
sem.sem_num = 0;
sem.sem_op = 1;
sem.sem_flg = 0;
semop(semid, &sem, 1);
}
if (pid == 0)
{
sleep(2);
sem.sem_num = 0;
sem.sem_op = 1;
sem.sem_flg = 0;
semop(semid, &sem, 1);
printf("This is son\n");
}
return 0;
}
编译运行程序如下图所示:
信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即 P(信号变量))和发 送(即 V(信号变量))信息操作。我们通常通过信号来解决多个进程对同一资源的访问竞争的问题,使在任一 时刻只能有一个执行线程访问代码的临界区域,也可以说它是协调进程间的对同一资源的访问权,也就是 用于同步进程的。