22信号和信号量

信号量:为控制临界资源而产生的一个或一组计数器,本质上是一个整数变量。用于进程的互斥操作

信号量基本操作

P 操作

进程申请临界资源时发出 P 操作

流程:检查信号量取值,

            > 0 则分配临界资源,信号值-1;

           否则表示无空余资源,进程阻塞直到指定资源被释放

V 操作

进程释放临界资源时发出 V 操作

流程:释放临界资源,信号值+1

注意:P 操作和 V 操作都是原子操作

信号与信号量

进程通过kill,sigqueue函数发送信号,通知另一个进程开始工作,实现进程之间同步

进程通过设置信号量,阻塞另一个进程继续工作,实现进程之间互斥

二值信号量:信号量只有 0 和 1 两种值

多值信号量:信号量取值为临界资源数量

步骤:

信号量使用:

1: ftok        使用某个文件做关键字创建 key  

2: semget  使用key创建信号集 semid

3: semctl    初始化信号集(SETALL)

3: semop    P V 操作,更新信号量

4: semctl     删除信号量集

创建信号量集

<sys/types.h>

<sys/ipc.h>

<sys/sem.h>

int semget(key_t key,  int nsems, int semflg)

nsems 表示信号集中信号量个数,0 则不创建信号量,返回 semid

创建信号量

 

信号量集操作

<sys/types.h>

<sys/ipc.h>

<sys/sem.h>

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

参数:

semid   指定信号量集

sops     指定操作集合数组

nsops   需要同时操作的信号量个数( sops 数组长度)

信号量集操作

struct sembuf {

    unsigned short    sem_num;   //信号量序号

    short                  sem_op;      //要执行的操作

    short                  sem_flg;      //操作相关的标志位

}

P 操作的时候,任何一个资源不够,都会导致阻塞

参数:

sem_op

正数:        释放资源,增加信号量(V操作)

负数:        获取资源,减少信号量(P操作)(资源不够则阻塞)

0              等待当前信号量值变为0  (非0则阻塞) (Z操作)

sem_flg

0                           默认,阻塞

IPC_NOWAIT:      资源不够不阻塞,立即返回

SEM_UNDO:       程序异常退出(申请了资源,但是没有释放) 时,系统会自动释放它申请信号量

信号量设置

int semctl(int semid,  int semnum,  int cmd,  *arg)

semid            信号集ID

semnum        修改信号量的编号

cmd               操作类型

arg                 根据不同的cmd,参数类型不同

可能的类型为:

union semun{

    int                          val;              //信号量值

    struct semid_ds    *buf;             //信号相关信息

    unsigned short      *array;         //信号集

    struct seminfo       *__buf;         //信号相关信息

}

注意:信号集一定是unsigned short类型,不可以是其他、

cmd 参数

IPC_STAT      读取内核中 semid_ds 数据到 arg.buf

IPC_SET        设置 arg.buf 中数据到 semid_ds

IPC_RMID      移除信号量,读写消息队列进程返回 EIDRM

IPC_INFO      获取系统级别的信号量集限制到arg.__buf这里 arg.__buf 指向 seminfo 结构数据返回 内核中信号量集数组的最大索引值

SEM_INFO     获取 seminfo消息,同时获取资源消耗情况:

                        semusz      获得系统当前的信号量集的数量

                        semaem    获得系统中信号量的总个数

                        返回 内核中信号量集数组的最大索引值

SEM_STAT    获取 seminfo 消息

                       semid 参数置为内核维护信号量集的数组索引

                       返回 semid指定的信号量标识符

GETALL         获取信号量集的值到 arg.array

GETVAL         返回信号量集中指定信号量的值(单个)

GETNCNT      返回等待信号量增加的进程数(P 阻塞)

GETZCNT      返回等待信号量变为0的进程数(Z 阻塞)

GETPID          返回最后一个调用semop函数操作的进程号

SETALL          设置 arg.array 到信号量集的值

SETVAL          设置 arg.val 到指定信号集中的信号量(单个)

信号量集合 semid_ds

struct semid_ds {

    struct ipc_perm  sem_perm;   //信号量的访问权限

    time_t                  sem_otime;    //最近调用semop时间

    time_t                  sem_ctime;   //最近调用semctl时间

     unsigned short    sem_nsems;  //信号量集合中成员数目

}

信号量中的数据

struct ipc_perm{

    key_t  __key;               //msgget 的key参数

    uid_t   uid                //消息队列所有者 euid

    gid_t   gid;              //消息队列所有者 egid

    uid_t   cuid;             //消息队列创建者 euid

    gid_t   cgid;             //消息队列创建者 egid

    unsigned short mode;  //访问权限

    unsigned short __seq;     //序列号

}

信号量数据

struct seminfo{

    int semmap;         //信号量层映射里的记录数量(未使用)

    int semmni;          //信号量集最多个数

    int semmns;         //所有信号量集中信号量的最多个数

    int semmnu;      //系统最大信号量撤销数 (未使用)

    int semmsl;       //一个信号量集中,最多可容纳的信号量数

    int semopm;     //semop 函数最大操作次数

    int semume;      //每个进程最大信号量撤销数(未使用)

    int semusz;      //结构体 sem_undo 的个数(信号集个数)

    int semvmx; //最大可取的信号量值

    int semaem;      // 可以被调整的信号量个数(总信号量)

}

信号量数组 sem

struct sem {

    unsigned short semval;   //信号量取值

    pid_t                 sempid;   //最近访问的进程ID

    unsigned short semncnt; // P 阻塞的进程数

    unsigned short semzcnt; // Z  阻塞的进程数

}

例子:

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

#include<sys/ipc.h>

#include<sys/sem.h>

#define SEM_NUM 5

//PV操作

void testPV(int semid)

{

      /*

      struct sembuf sops;

      sops.sem_num = 0; //operator semphore-index-0

      //sops.sem_op  = -2; //P - operation    

      sops.sem_op  = 2;  //V - operation

      sops.sem_flg = 0;

      semop(semid, &sops, 1);

      */

      struct sembuf semArr[SEM_NUM];

      int i;

      int val;

      int iRet;

      while(1)

      {

           //初始化信号集

           fprintf(stderr, "Input %d num for PV:", SEM_NUM);

           for (i=0; i<SEM_NUM; i++)

           {

                 scanf("%d", &val);

                 semArr[i].sem_num = i;

                 semArr[i].sem_op = (short)val;

                 //semArr[i].sem_flg = 0;

                 if (i==0)

                 {

                      semArr[i].sem_flg = IPC_NOWAIT;

                 }

                 semArr[i].sem_flg = SEM_UNDO;

           }

           //更新PV操作

           iRet = semop(semid, semArr, SEM_NUM);

           if (iRet)

           {

                 perror("semop failed!");

                 continue;

           }

           else

           {

                 printf("semop success!\n");

           }

      }

      return ;

}

// struct semid_ds结构信息

void printSemInfo(struct semid_ds* pstSem)

{

      printf("----------------- SEMID_DS  -----------------\n");

      printf("Key        : %#x\n", pstSem->sem_perm.__key);

      printf("Ownner uid : %d\n",  pstSem->sem_perm.uid);

      printf("Ownner gid : %d\n",  pstSem->sem_perm.gid);

      printf("Creater uid: %d\n",  pstSem->sem_perm.cuid);

      printf("Creater gid: %d\n",  pstSem->sem_perm.cgid);

      printf("Mode       : %#o\n", pstSem->sem_perm.mode);

      printf("Seque      : %d\n",  pstSem->sem_perm.__seq);

      printf("\n");

      printf("Last PV   time :%d\n", (int)pstSem->sem_otime);

      printf("Last Ctrl time :%d\n", (int)pstSem->sem_ctime);

      printf("Sem-Num        :%d\n", (int)pstSem->sem_nsems);

      printf("---------------------------------------------\n");

}

//提示

void Usage()

{

      printf("\tsetall : init semphore-set value\n");

      printf("\tgetall : print semphore-set value\n");

      printf("\tipcstat: print semphore-set info\n");

      printf("\tipcset : set mode for semphore-set\n");

      printf("\texit   : exit process\n");

      return;

}

//信号量设置

void testSemCtl(int semid)

{

      Usage();

      struct semid_ds  stSem;

      int i;

      int iRet;

      unsigned short arr[SEM_NUM] = {};

      char szCmd[100] = {};

      while(1)

      {

           fprintf(stderr, "->");

           scanf("%s", szCmd);

           //初识信号集

           if (!strcmp(szCmd, "setall"))

           {

                 int val;

                 fprintf(stderr, "Input %d num for init semphore-set:", SEM_NUM);

                 for (i=0; i<SEM_NUM; i++)

                 {

                      scanf("%d", &val);

                      if (val < 0 || val > 0xffff)

                      {

                            printf("Semphore-value must bigger than 0\n");

                            break;

                      }

                      arr[i] = (unsigned short)val;

                 }

                 if (i != SEM_NUM)

                 {

                      continue;

                 }

                 //更新

                 iRet = semctl(semid, SEM_NUM, SETALL, arr);

                 if (iRet)

                 {

                      perror("Fail to SETALL!");

                      continue;

                 }

           }

           //获取信号集

           else if (!strcmp(szCmd, "getall"))

           {

                 iRet = semctl(semid, SEM_NUM, GETALL, arr);

                 if (iRet)

                 {

                      perror("Fail to GETALL!");

                      continue;

                 }

                

                 printf("Sem-Value: ");

                 for (i=0; i<SEM_NUM; i++)

                 {

                      printf("%d ", arr[i]);

                 }

                 printf("\n");

           }

           //ipc状态

           else if (!strcmp(szCmd, "ipcstat"))

           {

                 iRet = semctl(semid, SEM_NUM, IPC_STAT, &stSem);

                 if (iRet)

                 {

                      perror("Fail to IPC_STAT!");

                      continue;

                 }

                 printSemInfo(&stSem);

           }

           //修改ipc模式

           else if (!strcmp(szCmd, "ipcset"))

           {

                 int mode;

                 iRet = semctl(semid, SEM_NUM, IPC_STAT, &stSem);

                 if (iRet)

                 {

                      printf("Fail to IPC_STAT!");

                      continue;

                 }

                 printf("Current Mode: %#o\n", stSem.sem_perm.mode);

                 fprintf(stderr, "New Mode(eg:600):");

                 scanf("%o", &mode);

                 if (mode < 0 || mode > 0777)

                 {

                      printf("Mode is invalid(range 0 to 0777).\n");

                      continue;

                 }

                

                 stSem.sem_perm.mode = mode;

                 iRet = semctl(semid, SEM_NUM, IPC_SET, &stSem);

                 if (iRet)

                 {

                      perror("Fail to IPC_SET!");

                      continue;

                 }

                 printf("Set mode success!\n");

           }

           //退出

           else if (!strcmp(szCmd, "exit"))

           {

                 break;

           }

           else

           {

                 Usage();

           }

      }   

      //删除信号集

      fprintf(stderr, "Delete Semphore-set [%d]?(y/n):", semid);

      scanf("%s",szCmd);

      if (!strcmp(szCmd, "y"))

      {

           iRet = semctl(semid, SEM_NUM, IPC_RMID, NULL);

           if (iRet)

           {

                 perror("Fail to IPC_RMID!");

                 return;

           }

           printf("Delete success!\n");

      }

      return;

}

int main(int argc, char ** argv)

{

      if (argc != 2 || (strcmp(argv[1], "pv") && strcmp(argv[1], "c" )))

      {

           printf("Usage: %s [pv | c]\n", argv[0]);

           printf("\t pv: For PV operation\n");

           printf("\t c : For Ctrl semphore-set\n");

           return 0;

      }

      char szFile[] = "123";

      //创建文件关键字

      key_t key = ftok(szFile, 321);

      if (key==-1)

      {

           perror("Fail to ftok!");

           return -1;

      }

      printf("KEY: %#x\n", key);

      //创建信号集

      int semid = semget(key, SEM_NUM, IPC_CREAT | 0660);

      if (semid < 0)

      {

           perror("fail to semget!");

           return -1;

      }

      printf("semid: %d\n", semid);

     

      /*

      //semid = semget(key, SEM_NUM, IPC_CREAT | 0666);

      //semid = semget(key, SEM_NUM, IPC_CREAT|IPC_EXCL| 0666);

      //semid = semget(key, SEM_NUM - 1, IPC_CREAT | 0666);

      semid = semget(key, SEM_NUM+1, IPC_CREAT | 0666);

      if (semid < 0)

      {

           perror("[2]fail to semget!");

           return -1;

      }

      printf("[2]semid: %d\n", semid);

      //*/

      if (argv[1][0] == 'p')

      {

           testPV(semid);

      }   

      else

      {

           testSemCtl(semid);

      }

      return 0;

}

猜你喜欢

转载自www.cnblogs.com/gd-luojialin/p/9216021.html
今日推荐