【Linux】Linux进程间通信之信号量

1、信号量概念引入

在我们过去学习的进程间通信中,共享内存无疑是最快的进程间通信,但是共享内存没有进行同步互斥,而信号量的功能就是实现同步和互斥

2、同步和互斥概念介绍

那么什么是同步和互斥呢?

以下我们一一进行介绍:

先来介绍进程互斥相关概念: 
(1)由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥,就是一个进程使用这份资源另外一个进程就不能使用,这样两个进程之间就是互斥关系
(2)系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
(3)在进程中涉及到互斥资源的程序段叫临界区

再来介绍进程同步概念:

进程同步指的是多个进程要相互配合共同完成一项任务。

为了实现同步互斥,Dijkstra(迪杰斯特拉)提出信号量和P、V原语概念

那么他是怎样利用P、V原语来实现同步和互斥呢?

他提出将P、V在同一个进程中进而来实现互斥关系
将P、V放在不同进程中进而来实现同步关系

3、信号量到底是什么东西呢?

信号量值含义
S>0:S表示可用资源的个数
S=0:表示无可用资源,无等待进程
S<0:|S|表示等待队列中进程个数

可以这么理解信号量就是一个计数器

struct semaphore
{
    int value;
    pointer_PCB queue;
};

4、什么是P、V原语?

将信号量当做一个计数器的话,那么对计数器的操作就有两个,+和-

P原语 就相当于--操作
P(s)
{
    s.value = s.value--;
    if (s.value < 0)
    {
        该进程状态置为等待状状态
        将该进程的PCB插入相应的等待队列s.queue末尾
    }
}
V原语 相当于++操作
V(s)
{
    s.value = s.value++;
    if (s.value < =0)
    {
        唤醒相应等待队列s.queue中等待的一个进程
        改变其状态为就绪态
        并将其插入就绪队列
    }

5、有的时候一个信号量可能并不能满足我们的需要,这里我们就引入了信号量集的概念

信号量结构

struct semid_ds {
    struct ipc_perm sem_perm;  /* Ownership and permissions */
    time_t       sem_otime; /* Last semop time */
    time_t       sem_ctime; /* Last change time */
    unsigned short  sem_nsems; /* No. of semaphores in set */
};

6、介绍一下信号量集函数

(1)semget函数

【1】函数原型

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/sem.h>
 int semget(key_t key, int nsems, int semflg);

【2】函数功能

用来创建和访问一个信号量集

【3】参数解释

参数一·:信号量集的名字

参数二:信号量级中信号量的个数

参数三:由九个权限标志位组成,它们的用法和创建文件的用法一样

【4】返回值

成功返回一个非负整数,即为信号量集的标识码,失败返回-1

(2)semctl函数

【1】函数原型

#include <sys/types.h>

#include <sys/ipc.h>
#include <sys/sem.h>
 int semctl(int semid, int semnum, int cmd, ...);

【2】函数功能

用于控制信号量集

【3】参数解释

参数一:函数semget返回的所创建信号量集的标识码

参数二:信号量集中信号量的序号

参数三:将要采取的动作(有三个可采取的值)IPC_STAT,IPC_SET,IPC_RMID

【4】返回值

成功返回0,失败返回-1

(3)semop函数

【1】函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);

【2】函数功能

用来创建和访问一个信号量集

【3】函数参数解释

参数一:信号量集标识码

参数二:一个结构体的实例

sembuf结构体:

struct sembuf

{

    unsigned short sem_num;  /* semaphore number */
           short          sem_op;   /* semaphore operation */
           short          sem_flg;  /* operation flags */

};

sem_num是信号量的编号。
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值:
    一个是“-1”,也就是P操作,等待信号量变得可用;
    另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用
sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO

参数三:信号量的个数

【4】返回值

成功返回0,失败返回-1

示例代码,我们按照二元信号量来测试:

代码如下:

Makefile

【Linux】Linux进程间通信之信号量

comm.h

【Linux】Linux进程间通信之信号量

comm.c

【Linux】Linux进程间通信之信号量【Linux】Linux进程间通信之信号量【Linux】Linux进程间通信之信号量

test_sem.c

【Linux】Linux进程间通信之信号量

【Linux】Linux进程间通信之信号量

运行结果:打印成对的A或者B

【Linux】Linux进程间通信之信号量

我们将测试代码中的P,V操作注释掉会出现如下现象,打印出的A和B随机组合或者不组合单独出现

【Linux】Linux进程间通信之信号量

信号量也和共享内存,消息队列一样,生命周期随内核,必须使用相应的指令来删除

使用ipcs -s来查看信号量集

使用ipcrm -s来删除信号量集

【Linux】Linux进程间通信之信号量