zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",ret);
        ret= semctl(sem_id,0,IPC_RMID);
        if(ret== -1)
        {
            printf("Remove sem error.
    ");
        }
        else
            printf("Remove sem ok.
    ");
        return 0;
    }

                                                          

  • 相关阅读:
    并行数据的并行转串行
    色彩空间转换仿真与模型搭建
    布隆过滤器介绍和在java中应用举例
    java9初探
    个人博客开通啦!
    MyBatis多租户隔离插件开发
    手动解析Excel获取文件元数据
    解决Shiro+SpringBoot自定义Filter不生效问题
    基于Redis的分布式锁实现
    解决tomcat同时部署两个SpringBoot应用提示InstanceAlreadyExistsException
  • 原文地址:https://www.cnblogs.com/XNQC1314/p/9027239.html
Copyright © 2011-2022 走看看