System V 信号量

System V 信号量在内核中维护,其中包括二值信号量 、计数信号量、计数信号量集。
二值信号量 : 其值只有0、1 两种选择,0表示资源被锁,1表示资源可用;
计数信号量:其值在0 和某个限定值之间,不限定资源数只在0 1 之间;
计数信号量集 :多个信号量的集合组成信号量集
内核维护的信号量集结构信息如下:定义在头文件<sys/sem.h>

struct semid_ds {    
struct     ipc_perm     sem_perm;    
struct     sem          *sem_base;    
ushort                  sem_nsems;  //信号量个数
time_t                  sem_otime;
time_t                  sem_ctime;
};

其中ipc_perm 结构是内核给每个进程间通信对象维护的一个信息结构,其成员包含所有者用户id,所有者组id、创建者及其组id,以及访问模式等;

semid_ds结构体中的sem结构是内核用于维护某个给定信号量的一组值的内部结构,其结构定义:

struct sem {   
   int semval;     /* current value */
   int sempid;     /* pid of last operation */
   struct list_head sem_pending; /* pending single-sop operations */
 };

其中senval变量代表当前信号量的值,sempid 为最后一个成功操作该信号量的进程id,该结构体在内核以双向链表进行 维护
semid_ds结构体中的sem_nsems成员代表该信号量标示符的信号量个数
主要函数介绍:

创建一个信号量或访问一个已经存在的信号量集。
int semget(key_t key, int nsems, int semflg);
该函数执行成功返回信号量标示符,失败返回-1
参数key是通过调用ftok函数得到的键值,

nsems代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0,我们一旦创建了该信号量,就不能更改其信号量个数,只要你不删除该信号量,你就是重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建;
semflg 指定该信号量的读写权限,当创建信号量时不许加IPC_CREAT ,若指定IPC_CREAT |IPC_EXCL则创建是存在该信号量,创建失败。

===>?????

通过semget函数创建一个信号量集程序如下:(semsemget.c)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
//
#define SEM_R 0400
#define SEM_A 0200
#define SVSEM_MODE (SEM_R|SEM_A|SEM_R>>3|SEM_A>>6)
int main(int argc,char** argv)
{
   int flag,semid,nsems;
   int c;   //getopt
   flag = SVSEM_MODE | IPC_CREAT;
  
   //根据命令行判断是否执行的IPC_EXCL
   while((c = getopt(argc,argv,"e")) != -1)
   {
        switch(c)
        {
            case 'e':
                 flag |= IPC_EXCL;
                 break;
        }
   }
   
   if(optind != argc-2)
   {
       perror("usage: semget [-e] <path> <nsems>\n");
       exit(0);
   }
   //使用optind 是为了处理可变的参数个数
   nsems = atoi(argv[optind + 1]);
   //创建信号量
   semid = semget(ftok(argv[optind],0),nsems,flag);
   // to - do with sem
   exit(0);
}

打开一个信号量集后,对其中一个或多个信号量的操作。
int semop(int semid, struct sembuf *sops, unsigned nsops);
该函数执行成功返回0,失败返回-1;
第一个参数semid 为信号量标示符;nops为第二个参数的操作数组的个数,第二个参数sops为一个结构体数组指针,结构体定义在sys/sem.h中,结构体如下

struct sembuf {
   unsigned short sem_num; /* semaphore index in array */
   short sem_op;    /* semaphore operation */
   short sem_flg;   /* operation flags */
};

sem_num 操作信号的下标,其值可以为0 到nops
sem_flg为该信号操作的标志:其值可以为0、IPC_NOWAIT 、 SEM_UNDO


0 在对信号量的操作不能执行的情况下,该操作阻塞到可以执行为止;
IPC_NOWAIT 在对信号量的操作不能执行的情况下,该操作立即返回;
SEM_UNDO当操作的进程推出后,该进程对sem进行的操作将被取消;


sem_op取值 >0 则信号量加上它的值,等价于进程释放信号量控制的资源
sem_op取值 =0若没有设置IPC_NOWAIT, 那么调用进程将进入睡眠状态,直到信号量的值为0,否则进程直接返回
sem_op取值 <0则信号量加上它的值,等价于进程申请信号量控制的资源,若进程设置IPC_NOWAIT则进程再没有可用资源情况下,进程阻塞,否则直接返回。

采用setmop函数对一个信号量执行操作程序如下:(semop.c)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
int main(int argc,char ** argv)
{
   int c;
   int i, flag,semid,nops;
   struct sembuf* ptr;
   
   flag = 0;
   while((c=getopt(argc,argv,"nu")) != -1)
   {
       switch(c)
       {
           case 'n':
               flag |= IPC_NOWAIT;//
               break;
           case 'u':
               flag |= SEM_UNDO;
               break;
       }
   }
   if(argc - optind < 2)
   {
       perror("usage: semops [-n] [-u] <path> op...\n");
       exit(0);
   }
   //打开存在的信号量
   if((semid = semget(ftok(argv[optind],0),0,0)) == -1)
   {
      perror("semget() error.\n");
      exit(1);
   }
   
   optind++;
   nops = argc - optind; //信号量的个数  
   ptr = calloc(nops,sizeof(struct sembuf));
   
   for(i=0;i<nops;i++)
   {
       ptr[i].sem_num = i;  //index
       ptr[i].sem_op = atoi(argv[optind+i]); //设置信号量的值
       ptr[i].sem_flg = flag;
   }
   //信号量操作
   if(semop(semid,ptr,nops) == -1)
   {
      perror("semop() error.\n");
      exit(1);
   }
   
   return 0;
}

对信号量执行各种控制操作。
int semctl(int semid, int semnum, int cmd, ...);
该函数执行成功返回非负值,失败返回-1
参数semid为信号集的标识符,参数 semnum标识一个特定信号,

该参数仅用于 SETVAL、GETVAL、GETPID命令
cmd控制类型,...说明函数参数是可选的,通过该共用体变量semun选择操作参数,各字段如下:

union semun {    
    int val; /* value for SETVAL */
    struct semid_ds __user *buf; /* buffer for IPC_STAT & IPC_SET */
    unsigned short __user *array; /* array for GETALL & SETALL */
    struct seminfo __user *__buf; /* buffer for IPC_INFO */
    void __user *__pad;
 };

IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
IPC_RMID将信号量集从系统中删除
GETALL用于读取信号量集中的所有信号量的值,存于semnu的array中
SETALL 设置所指定的信号量集的每个成员semval的值
GETPID返回最后一个执行semop操作的进程的PID。
LSETVAL把的val数据成员设置为当前资源数
GETVAL把semval中的当前值作为函数的返回,即现有的资源数,返回值为非负数。

调用semctl函数设置信号量的值程序如下(semsetvalues.c):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
//信号量操作共同体结构
union _semun
{
    int              val;
    struct semid_ds  *buf;
    unsigned short   *array;
};
int main(int argc,char** argv)
{
   int semid,nsems;
   int i;
   struct semid_ds seminfo; 
   unsigned short *ptr;
   union _semun arg;
   
   if(argc < 2)
   {
       perror("usage: semsetv <path> [value...]");
       exit(1);
   }
   //打开信号量
   semid = semget(ftok(argv[1],0),0,0);
   arg.buf = &seminfo;
   //读取信号量的相关信息
   semctl(semid,0,IPC_STAT,arg);
   nsems = arg.buf->sem_nsems;
   
   if(argc != nsems + 2)
   {
      printf("%d semaphores in set, %d values specified.\n",nsems,argc-2);
      exit(0);
   }
   //分配信号量
   ptr = calloc(nsems,sizeof(unsigned short));
   arg.array = ptr;
   
   for(i=0; i<nsems; i++)
       ptr[i] = atoi(argv[i+2]);
   //信号量赋值
   semctl(semid, 0, SETALL,arg);
   return 0;
}

调用semctl获取信号量的值,程序如下(semgetvalues.c):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
union _semun
{
    int                  val;
    struct  semid_ds     *buf;
    unsigned short       *array;
};
int main(int argc, char** argv)
{
   int semid,nsems,i;
   struct semid_ds seminfo;
   
   unsigned short* ptr;
   union _semun arg;
   
   if(argc != 2)
   {
       perror("usage: semgetv <path>\n");
       exit(1);
   }
   
   //打开信号量
   semid = semget(ftok(argv[1],0),0,0);
   arg.buf = &seminfo;
   
   //读取信号量的信息
   semctl(semid,0,IPC_STAT,arg);
   nsems = arg.buf->sem_nsems;
   
   ptr = calloc(nsems,sizeof(unsigned short));
   arg.array = ptr;
   
   semctl(semid,0,GETALL,arg);
   for(i =0; i<nsems; i++)
       printf("semval[%d]=%d\n",i,ptr[i]);
    exit(0);
  
   return 0;
}

System V 信号量是具有内核的持续性,可以结合上面介绍的三个函数和程序进行简单的测试。测试结果如下所示:System V 信号量

演示SEM_UNDO属性,测试结果如下:

System V 信号量


你可能感兴趣的:(System,信号量,v)