进程间通信-信号量

Linux内核信号量集用结构体semid_ds结构体表示,semid_ds的结构体定义如下:

/* Data structure describing a set of semaphores.  */
struct semid_ds
{
  struct ipc_perm sem_perm;        /* operation permission struct */
  __time_t sem_otime;            /* last semop() time */
  __syscall_ulong_t __glibc_reserved1;
  __time_t sem_ctime;            /* last time changed by semctl() */
  __syscall_ulong_t __glibc_reserved2;
  __syscall_ulong_t sem_nsems;        /* number of semaphores in set */
  __syscall_ulong_t __glibc_reserved3;
  __syscall_ulong_t __glibc_reserved4;
};

每个信号量则描述为:

struct sem{
       int    semval ;
       int    sempid ;     
       int    semcnt ;
       int    semzcnt;
};  

信号量的基本操作包括创建信号量、信号量的值操作、获取或设置信号量属性,对应的相关函数的分别是semget、semop、semctl。

1.创建信号量集

semget函数用于创建信号量,如果参数key指定的信号量集已经存在,则就返回该信号量集。

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

key:一个整数类型的键值,用来命名某个特定的信号量集。

nsems:指定打开或者新创建的信号量集包含的信号量数目。

falg:9个位的权限标志。

返回值:成功返回信号量集描述字,否则返回-1。

2.信号量值操作

信号量本质上是一个计数器,进程可以使用函数semop来增加或者减少信号量值,以表示释放或者申请共享资源。

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

sem_id:semget函数返回的信号量集描述字。

nsops:本次操作的信号量数目,也是sops指向的数组的大小。

sops:指向一个类型为sembuf的结构体数组。

sembuf结构体:

/* Structure used for argument to `semop' to describe operations.  */
struct sembuf
{
  unsigned short int sem_num;    /* semaphore number */
  short int sem_op;        /* semaphore operation */
  short int sem_flg;        /* operation flag */
};

如果sem_op为负数,就从信号量值中减去sem_op的绝对值,表示进程获取资源;如果sem_op为正数,就把它加到信号量上,表示归还资源;如果sem_op为0,则调用进程睡眠,直到信号量值为0。sem_flag一般设置为0。

3.获取或者设置信号量属性

系统中的每个信号量集都对应一个struct sem_ds结构体,该结构体记录信号量集的各种信息,存放于内核空间。为了设置、获取信号量集的各种信息及属性,在用户空间中有一个联合体union semnu与之对应。

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_INF    (Linux-specific) */
};

信号量属性操作的函数原型:

#include <linux/sem.h>
int semctl(int semid, int semnum, int cmd, union semun arg);

semid:信号量集描述字。

semnum:待操作的信号量在信号集semid中的索引。

cmd:指定具体的操作类型,常见的操作有:

  SETVAL:设置semnum所代表信号量的值为arg.val。

  SETALL:通过arg.val更新所有信号量的值。

  IPC_RMID:从内核主存中删除信号量集。

  GETVAL:返回semnum所代表信号量的值。

使用信号量创建自定义P/V操作函数库

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


int createsem(key_t key){
    return semget(key,1,IPC_CREAT|0666);
}


int getsem(key_t key){
    return semget(key,1,0666);
}



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) */
};




int initsem(int semid,int initval){
    union semun arg;
    arg.val = initval;
    return semctl(semid,0,SETVAL,arg);
}


int P(int semid){
    struct sembuf operation[1];
    operation[0].sem_num = 0;
    operation[0].sem_op = -1;
    return semop(semid,operation,1);
}


int V(int semid){
    struct sembuf operation[1];
    operation[0].sem_num = 0;
    operation[0].sem_op = 1;
    return semop(semid,operation,1);
}
int delsem(int semid){
    union semun arg;
    semctl(semid,0,IPC_RMID,arg);
}


int main(int argc,char * argv[]){
    int semid = createsem(0x123456);
    int seccess =  initsem(semid,1);
    printf("semid:%d
",semid);
    printf("seccess:%d
",seccess);




   // int v = V(semid);
   // printf("v:%d
",v);


    int p = P(semid);
    printf("p:%d
",p);


    int close =  delsem(semid);
    printf("close:%d
",close);
    return 0;
} 
原文地址:https://www.cnblogs.com/iuyy/p/13665796.html