IPC进程间通信---信号量

信号量

   信号量:信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对于临界资源访问的同步。临界资源可以

理解为在某一时刻只能由一个进程或线程操作的资源,这里的资源可以是一段代码、一个变量或某种硬件资源。信号量的

值大于或等于0时表示可供并发进程使用的资源实体数,小于0时表示正在等待使用临界资源的进程数。

    Linux内核也为每个信号集维护了一个semid_ds数据结构实例,该结构定义在头文件linux/sem.h中,各个字段含义为:

struct semid_ds
{
   struct ipc_perm sem_perm;       //对信号进行操作的许可权
   __kernel_time_t sem_otime;     //对信号进行操作的最后时间
   __kernel_time_t em_ctime;       //对信号进行修改的最后时间
   struct sem *sembase;               //指向第一个信号
   struct sem_queue sem_pending;            //等待处理的挂起操作
   struct sem_queue **sem_pending_last;  //最后一个正在挂起的操作
   struct sem_undo *undo;           //撤销的请求
   ushort sem_nsems;                  //数组中的信号数
};

   信号量使用key_t值作为它的名字。头文件<sys/types.h>把key_t这个数据类型定义为一个整数,它通常是一个至少32位

的整数。这些整数值通常由ftok函数赋予的。函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为

IPC键。该函数原形为:

   #include <sys/ipc.h>

   key_t ftok(const char *pathname,int proj_id);

   创建或打开信号量前需要使用ftok函数得到key值,下面是ftok函数的包裹函数:

key_t Ftok(const char *pathname,int proj_id)
{
  key_t key= ftok(pathname,proj_id);
  if(key== -1)
  {
    perror("ftok.");
    exit(1);
  }
  return key;
}

      信号量的创建或打开:

   Linux下使用系统函数semget函数创建或打开信号量。该函数定义在头文件<sys/sem.h>中,该函数的原形为:

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

   该函数执行成功返回一个信号量的标示符,失败返回-1。函数的第一个参数为ftok()函数得到的键值,第二个参数nsems

指明要创建的信号量包含的信号量个数,如果只是打开信号量,把nsems设置为0即可,第三个参数semflg为操作标志,可

以取如下值:

  • IPC_CREAT:调用semget()时,它会将此值与系统中其它信号量的key值进行比较,如果存在相同的key,说明此信号量

已存在,此时返回该信号量的标识符,否则新建一个信号量并返回其标识符。

  • IPC_EXCL:该宏必须和IPC_CREAT一起使用,否则没有意义。当semflg取IPC_CREAT|IPC_EXCL时,表示如果发现信号

量已经存在,则返回错误,错误码为EEXIST。

   信号量的控制:

   使用信号量时,往往需要对信号量进行一些控制操作,比如删除信号量、对内核维护的信号量的数据结构semid_ds进行

设置、获取信号量中信号值等。通过semctl控制函数可以完成这些操作,该函数定义在<sys/sem.h>中,如下所示:

   int semctl(int semid,int semnum,int cmd, ...);

   函数中,参数semid为信号量的标示符(即通过semget函数的到的值);参数semnum标示一个特定的信号;参数cmd指明控制

操作的类型;最后的“...”说明函数的参数是可选的,它依赖于第三个参数cmd,它通过共用体变量semun选择要操作的参数。

union semun
{
   int val;                //仅用于SETVAL操作类型,设置某个信号量的值等于val
   struct semid_ds *buf;   //用于IPC_STAT和IPC_SET操作,存取semid_ds结构
   unsigned short *array;  //用于SETALL和GETALL操作
   struct seminfo *__buf;  //为控制IPC_INFO提供的缓存
};

      参数cmd通过宏来指示操作类型,通常取以下几个宏:

  • GETVAL:把semval的当前值作为函数返回值返回。既然信号量决不会时负数(semval被声明为一个unsigned short整数),

那么成功的返回值总是非负数。

  • SETVAL:把semval值设置为val。如果操作成功,那么相应信号量在所有进程中的信号量调整值将被设置为0。
  • IPC_RMID:把由semid指定的信号量集从系统中删除掉。

   信号量的操作:

      信号量的值与相应资源的使用情况有关,当它的值大于0时,表示当前可用资源的数量,当它的值小于0时,其绝对值表

示等待使用该资源的进程个数。信号量的值仅能由PV操作来改变。使用semget打开一个信号量集后,对其中一个或多个信号

量的操作就使用semop函数来执行。该函数原型为:

       int semop(int semid,struct sembuf *sops,unsigned nsops);

       函数的参数semid为信号量的标示符,参数sops指向进行操作的结构体数组首地址,参数nsops指出将要进行操作的信号量

的个数。semop函数调用成功返回0,否则返回-1。

       semop的第二个参数sops指向的结构体数组中,每个sembuf结构体对应一个特定的信号操作。该结构体为:

struct sembuf
{
   unsigned short sem_num;    //信号量在信号集中的索引
   short sem_op;                      //操作类型
   short sem_flg;                      //操作标志
};

   sem_op的取值和意义:

  •  sem_op>0:信号量加上sem_op的值,表示进程释放控制的资源。
  •  sem_op=0:如果没有设置IPC_NOWAIT,则调用进程进入睡眠状态,直到信号量值为0;否则进程不会睡眠,直接返回

EAGAIN。

  •  sem_op<0:信号量加上sem_op的值。若没有设置IPC_NOWAIT,则调用进程阻塞,直到资源可用;否则进程直接返回

EAGAIN。

     下面是对信号量进行操作的一个例子:

// myipc.h
#pragma once

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

key_t Ftok(const char *pathname,int proj_id)
{
    key_t key= ftok(pathname,proj_id);
    if(key== -1)
    {
        perror("ftok.");
        exit(1);
    }
    return key;
}
#include "myipc.h"

int main()
{
    key_t sem_key= Ftok("mysem",0xff);
    int sem_id= semget(sem_key,1,IPC_CREAT);
    if(sem_id== -1)
    {
        perror("semget.");
        exit(1);
    }
    else
    {
        printf("sem key = 0x%x,sem id = %d\n",sem_key,sem_id);
    }
    int ret;
    union semun init;
    init.val= 3;
    semctl(sem_id,0,SETVAL,init);
    struct sembuf info;
    info.sem_num= 0;
    info.sem_op= 2;
    info.sem_flg= 0;
    semop(sem_id,&info,1);
    ret= semctl(sem_id,0,GETVAL);
    printf("sem value = %d\n",ret);
    ret= semctl(sem_id,0,IPC_RMID);
    if(ret== -1)
    {
        printf("Remove sem error.\n");
    }
    else
        printf("Remove sem ok.\n");
    return 0;
}

                                                      

猜你喜欢

转载自www.cnblogs.com/XNQC1314/p/9027239.html