当前位置: 代码迷 >> 综合 >> 16信号量(semaphore)
  详细解决方案

16信号量(semaphore)

热度:19   发布时间:2023-11-24 03:50:23.0

目录

  • 信号量概述:
    • 一:信号量的本质
    • 二:信号量的机制
  • 相关的API
      • ***①int semget(key_t key,int num_sems,int sem_flags);***
      • ***②int semop(int semid,struct sembuf semoparray[ ],size_t semops);***
      • ***③int semctl (int semid,int sem_num,int cmd,…)***
  • 例子

信号量概述:

一:信号量的本质

???信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。==当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。

二:信号量的机制

??信号量用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据。举例子来理解这句话:
  把一个临界资源(也可以认为是共享内存)比作一个上了锁的房间,某人比作一个进程,而开锁的钥匙就是信号量。某人有钥匙的话,就能开锁进入房间里,做它需要做的事情,如果这时候有第二个人想进入房间里的话,是进不去的,因为它没有钥匙(信号量(钥匙)<0)。直到进去的那个人出了房间把钥匙放在某个地方(使信号量(钥匙)>=0),然后第二人拿到了钥匙,才能渠道房间里去。这钥匙起到的作用就是信号量。若有100把钥匙,可以使101个人同时进入房间。
  PV操作的定义:
P:
  ①将信号量S的值减1,即进行S = S-1;
??②如果S < 0,则该进程进入阻塞队列;
??③如果S >= 0, 则该进程继续执行;
??④执行一次P操作其实就是意味请求分配一个资源。
V:
??①将信号量S的值加1,即 S = S + 1;
??②如果S > 0,则该进程继续执行;
??③如果S < 0, 则释放阻塞队列中的第一个等待信号量的进程;
??④执行一次V操作其实就是意味释放一个资源。

相关的API

头文件:

       #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>

①int semget(key_t key,int num_sems,int sem_flags);

??功能:(semphore get)创建或获取信号量,返回信号量D。
??参数
???? key:信号量索引值,可使用ftok获取
????num_sems:指定需要的信号量数目,它的值几乎总是1
????sem_flags:一般用IPC_CREAT 加上权限创建信号量集
??返回值:成功返回信号集的ID;失败返回 ==-1 ==;

②int semop(int semid,struct sembuf semoparray[ ],size_t semops);

??功能:(semphore operation)它的作用是改变信号量的值(PV操作)
??参数
???? semid:目标信号量的ID
????semoparray[]:结构体数组,它有三个成员:
???? ????(1)sem_num:需要操作第几个信号量;
???? ????(2)sem_op:信号量的值,-1为p操作(拿钥匙);1为v操作(放钥匙)。
???? ????(3)sem_flg:一般赋值SEM_UNDO相当于wait;
???? semops :semoparray[] 数组的个数。
??返回值: 成功返回0;失败返回 -1

③int semctl (int semid,int sem_num,int cmd,…)

如果有第四个参数,它通常是一个union semun结构,定义如下:

 union semun {
    int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};

控制信号量函数。用来控制信号,比如初始化,移除信号量等。
参数说明:
????1. semid 被操作目标信号量的ID号;
????2. sem_num 信号量的个数,即需要操作第几个信号量;从0开始计数。
????3. cmd 操作指令,它有几个宏,常用一般用SETVAL (初始化信号量的值相当于钥匙的数量IPC_RMID:用于删除一个信号量标识符。
????4. 其他 这个参数,是配合SETVAL指令来使用,它是个联合体而且需要定义。

例子

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
#include<stdlib.h>
// int semget(key_t key, int nsems, int semflg);
// int semctl(int semid, int semnum, int cmd, ...);union semun {
    int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};
void Pop(int semid){
    int ret;struct sembuf sembuf[1];sembuf[0].sem_num=0;sembuf[0].sem_op=-1;sembuf[0].sem_flg=SEM_UNDO;ret=semop(semid,sembuf,1);if(ret == -1){
    printf("get key faied\n");}else{
    printf("get key sucess\n");}
}
void Vop(int semid){
    int ret;struct sembuf sembuf[1];sembuf[0].sem_num=0;sembuf[0].sem_op=1;sembuf[0].sem_flg=SEM_UNDO;ret=semop(semid,sembuf,1);if(ret == -1){
    printf("return key faied\n");}else{
    printf("return key sucess\n");}
}
int main(){
    key_t key;int semid;int pid;union semun semclt;semclt.val=0;   //信号量的数量key=ftok(".",6);semid=semget(key,1,IPC_CREAT|0666);semctl(semid,0,SETVAL);//删除信号量IDpid=fork();if(pid>0){
    Pop(semid);	// 减去一个信号量,由于<0程序阻塞printf("this is parent pid\n");semctl(semid,0,IPC_RMID,semctl);}                                                                  else if(pid==0){
    printf("this is child pid\n");Vop(semid);//加上一个信号量。使信号量=0父进程运行}else{
    printf("fork faile\n");}return 0;
}
  相关解决方案