目录

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


信号量概述:

一:信号量的本质

   信号量(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

如果有第四个参数,它通常是一个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);//删除信号量ID
        pid=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;
}