• 九、进程间通信信号量


    一、概述

    1.信号量

      信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

    2.信号量的工作原理

    由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

    (1)P(sv)

      如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行

    (2)V(sv)

      如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

    在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)

    原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的

    3、作用

    • 互斥
    • 同步
    • 若要在进程间传递数据需要结合共享内存

    4、二元信号量

      二元信号量(Binary Semaphore)是最简单的一种锁(互斥锁),它只用两种状态:占用与非占用。所以它的引用计数为1。

    5.进程如何获得共享资源

    (1)测试控制该资源的信号量

    (2)信号量的值为正,进程获得该资源的使用权,进程将信号量减1,表示它使用了一个资源单位

    (3)若此时信号量的值为0,则进程进入挂起状态(进程状态改变),直到信号量的值大于0,若进程被唤醒则返回至第一步。

     

    注:信号量通过同步与互斥保证访问资源的一致性。

    二、信号量的用法

    1、定义一个唯一key(ftok)

     #include <sys/types.h>
     #include <sys/ipc.h>
    
     key_t ftok(const char *pathname, int proj_id);

    2、构造一个信号量(semget)

    int semget(key_t key,int nsems,int semflg) //获取信号量ID

    (1)参数:

    • key:信号量键值
    • nsems:信号量数量
    • semflg
      • IPC_CREATE:信号量不存在则创建
      • mode:信号量的权限

    (2)返回值:

    • 成功:信号量ID.

    • 失败:-1

    3、初始化信号量(semctl SETVA)

    int semctl(int semid,int semnum,int cmd,union semun arg)
    

    (1)参数

    • semid:信号量ID

    • semnum:信号量编号

    • cmd

      •  IPC_STAT:获取信号量的属性信息

      • IPC_SET:设置信号量的属性

      • IPC_RMID:删除信号量

      • IPC_SETVAL:设置信号量的值

    • arg:
      union semun
      {
          int val;
          struct semid_ds *buf;
      }
    

    (2)返回值

    • 成功:由cmd类型决定。

    • 失败:-1

    4、对信号量进行P/V操作(semop)

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

    (1)参数

    • semid :信号量ID

    • sops:信号量操作结构体数组
     struct sembuf
      {
      ​	short sem_num;	//信号量编号
      ​	short sem_op;        //信号量P/V操作
      ​	short sem_flg;	       //信号量行为,SEM_UNDO
      }  
    • nsops:信号量数量

    (2)返回值

    • 成功:0

    • 失败: -1

    5、删除信号量(semctl RMID)

     #include <sys/types.h>
     #include <sys/ipc.h>
     #include <sys/sem.h>
    //信号量控制操作
     int semctl(int semid, int semnum, int cmd, ...);
    

    (1)参数

    • semid:信号量ID.

    • semnum:信号量数量

    • cmd:

      • IPC_STAT : 读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。

      • IPC_SET   :   设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。

      • IPC_RMID  : 将信号量集从内存中删除。

      • IPC_INFO  :  返回有关系统范围的信号量限制和参数的信息

      • GETALL     :用于读取信号量集中的所有信号量的值

      • GETNCNT :返回正在等待资源的进程数目。

      • GETPID     : 返回最后一个执行semop操作的进程的PID。

      • GETVAL    : 返回信号量集中的一个单个的信号量的值。

      • GETZCNT : 返回正在等待完全空闲的资源的进程数目。

      • SETALL    : 设置信号量集中的所有的信号量的值。

      • SETVAL   : 设置信号量集中的一个单独的信号量的值。

    (2)返回值:

    • 失败:返回-1
    • 成功:非负值,返回值由cmd决定

    三、实例

    信号量用于子进程和父进程的同步

     

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/ipc.h>
    #include<sys/sem.h>
    
    #define DELAY_TIME 3
    union semun{
        int val;
        struct seimd_ds *buf;
    };
    
    
    /*初始化信号量*/
    int init_sem(int sem_id,int init_value)
    {
        union semun sem_union;
        sem_union.val = init_value;
          /*设置信号量集中的一个单独的信号量的值*/
        if(semctl(sem_id,0,SETVAL,sem_union) == -1) 
        {
            printf("Initialize semaphore failed!\n");
            return -1;
        }
        return 0;
    }
    
    /*删除信号量*/
    int del_sem(int sem_id)
    {
        union semun sem_union;
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        {
            perror("Delete semaohore fail!\n");
            return -1;
        }
        return 0;
    }
    
    /*P 操作:执行P操作时,信号量-1*/  
    
    int sem_p(int sem_id)
    {
        struct sembuf sops;
        sops.sem_num = 0;
        sops.sem_op  = -1;
        sops.sem_flg = SEM_UNDO;/*系统自动释放将会在系统中残留的信号量*/
    
        if(semop(sem_id,&sops,1) == -1)
        {
            perror("P operation failed\n");
            return -1;
        }
        return 0;
    }
    
    /*V 操作:执行P操作时,信号量+1*/
    int sem_v(int sem_id)
    {
        struct sembuf sops;
        sops.sem_num = 0;   /*单个信号量的编号应该为 0*/
        sops.sem_op  = 1;    /*表示V操作*/
        sops.sem_flg = SEM_UNDO;  /*系统自动释放将会在系统中残留的信号量*/
    
        if(semop(sem_id,&sops,1) == -1)
        {
            perror("V operation failed\n");
            return -1;
        }
        return 0;
    }
    
    void main(void)
    {
        pid_t result;
        int sem_id;
    
        sem_id = semget((key_t)6666,1,0666|IPC_CREAT);/*创建一个信号量*/
    
        init_sem(sem_id,0);  //初始化信号量的值为0,只能先执行V操作+1,然后才能执行P操作-1
    
        /*调用fork()函数*/
        result = fork();
        if(result == -1)
        {
            perror("Fork\n");
        }
        else if(result == 0)
        {
            printf("Child proess will wait for some seconds...\n");
            sleep(DELAY_TIME);  
            printf("the child process is running...\r\n");
            sem_v(sem_id); //如果有其他进程因等待信号量而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
        }
        else /*返回值大于0代表父进程*/
        {
            sem_p(sem_id); //如果信号量等于0,则进程挂起,直到信号量大于0,执行V操作
            printf("the father process is running...\r\n");
            del_sem(sem_id);
        }
        
        exit(0);
    }

    执行结果:

     由于信号量的值初始化为0,所以父进程一致挂起不能进行P操作,等待子进程执行完毕V操作后,信号量+1,此时信号量大于0,可以进行P操作执行父进程。执行完父进程后删除信号量。

  • 相关阅读:
    FPGA-中值滤波
    FPGA-中值滤波
    FPGA-shift_ram代码
    FPGA实现-shift_ram_3x3矩阵实现
    图像处理-中值滤波
    python-str
    ignore-certificate-errors(chrome)
    selenium-python-Cookie跳过登录验证码
    Angular2
    Angular2
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/16199614.html
Copyright © 2020-2023  润新知