zoukankan      html  css  js  c++  java
  • 进程间通信——信号量

    1、信号量介绍

        多个进程同时访问系统上的同一资源时,就需要考虑同步问题,以确保在任一时刻只有一个进程对资源独占式访问。通常,我们称访问共享资源的代码为临界区,进程同步,也就是确保同一时刻,只有一个进程进入临界区。

        信号量是实现进程同步的一种方式,信号量只能取自然数并且只支持两种操作,即等待和信号,在Linux中称为P、V操作。比如有SV信号量,则P、V操作的含义如下:

    • P(SV),如果SV的值大于0,就将其减1;如果SV的值等于0,则挂起该进程的执行
    • V(SV),如果有其他进程因为等待信号量SV而挂起,则唤醒;如果没有,则将SV加1

        信号量可以取任意自然数,但是最常用的信号量为二进制信号量,它只能取0和1,这里只讨论二进制信号量。关于信号量的系统调用主要有3个:semget、semop、semctl。此三个函数都是被设计用来操作信号量集,而不是单个信号量。

    2、semget系统调用

    #include<sys/sem.h>
    int semget(key_t key, int num_sems, int sem_flags)

        函数功能为用来创建一个信号量集或获取一个已经存在的信号量集,参数信息如下:

    • key参数是一个键值,用来标识一个全局的唯一的信号量集。通过信号量通信的进程使用相同的键值来创建获取该信号量。当key为特殊键值IPC_PRIVATE(值为0)时,无论该信号量是否已经存在,都将会创建一个新的信号量集。信号量键值一般只有semget函数使用,其他的有关信号量函数,使用semget返回的信号量集标识符来访问该信号量。
    • num_sems参数指定要创建/获取的信号量集中的信号量数目。如果是创建信号量,该值必须被设置,如果是获取,则可以置为0。
    • sem_flags参数指定一组标志。低9位指示了信号量的权限。它可以和IPC_CREAT标志做按位或操作,用来创建信号量,此时即使信号量已经存在,semget也不会产生错误。还可以联合使用IPC_CREAT和IPC_EXCL标志来确保创建一组新的、唯一的信号量集,这种情况下,如果信号量集已经存在,则semget返回错误并置errno为EEXIST。
    • semget成功时返回信号量集标识符(正整数值),失败时返回-1,并设置errno。

    3、semop系统调用

    #include<sys/sem.h>
    int semop(int sem_id, struct sembuf* sem_ops, size_t num_sem_ops)

        semop函数用来改变信号量的值,即执行P、V操作,参数信息如下:

    • sem_id参数是由semget调用返回的信号量集标识符,指定要改变的信号量集。
    • sem_ops参数是一个指向sembuf结构数组的指针。sembuf结构体定义如下:
    struct sembuf
    {
        unsigned short int sem_num;
        short int sem_op;
        short int sem_flg;
    }

        sem_num成员指定信号量集中的信号量编号,从0开始。sem_op成员指定操作类型,其值可为正整数、0和负整数,信号量集操作类型较为复杂(可参见APUE),这里就只以二进制信号量为例来说明,sem_op为-1时即为P操作,为1时即为V操作。sem_flg成员可选值为IPC_NOWAIT和SEM_UNDO,IPC_NOWAIT表示系统调用无论是否操作成功,都将立即返回,类似于非阻塞;而SEM_UNDO则表示使内核跟踪信号量,如果进程没有释放信号量而异常退出,则由内核来释放该信号量值。

    • num_sem_ops指定sem_ops数组中的元素个数,即要操作的个数。
    • semop成功时返回0,失败时返回-1并设置errno。失败时,sem_ops数组中指定的所有操作都不被执行。

    4、semctl系统调用

    #include<sys/sem.h>
    int semctl(int sem_id, int sem_num, int command, ...)

        semctl函数用于对信号量进行控制,参数信息如下:

    • sem_id参数是由semget调用返回的信号量集标识符。
    • sem_num参数指定被操作的信号量的编号,从0开始计数。
    • command参数指定要执行的命令,有的命令需要调用者传递第四个参数,第四个参数的类型由用户自定义,推荐格式如下:
    union semun
    {
        int val;
        struct semid_ds* buf;
        unsigned short* array;
        struct seminfo* _buf;
    }
    

        command常用的操作有两种,其一,SETVAL,用来把信号量初始化为一个已知值,这个值由union semun中的val成员指定,其作用是在信号量初次使用前进行设置;其二,IPC_RMID,用于删除一个不需要再使用的信号量集,并唤醒所有等待该信号量集的进程。

    5、使用IPC_PRIVATE信号量进行进程间通信示例

    #include<sys/sem.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    
    #define SEM_FLAGS 0666
    
    union semun
    {
    	int val;
    	struct semid_ds* buf;
    	unsigned short int* array;
    	struct seminfo* _buf;
    };
    
    void pv(int sem_id, int option)   //pv操作,option为-1时为p操作,option为1时为v操作
    {
    	struct sembuf sem_buf;
    	sem_buf.sem_num = 0;
    	sem_buf.sem_op = option;
    	sem_buf.sem_flg = SEM_UNDO;
    	semop(sem_id, &sem_buf, 1);
    }
    
    int main(int argc, char* argv[])
    {
    	int sem_id = semget(IPC_PRIVATE, 1, SEM_FLAGS);  //创建信号量
    	union semun sem_un;
    	sem_un.val = 1;
    	semctl(sem_id, 0, SETVAL, sem_un);   //初始化信号量的值为1
    
    	pid_t pid = fork();
    	if(0 == id)
    	{
    		pv(sem_id, -1);
    		printf("child process
    ");
    		sleep(5);
    		pv(sem_id, 1);
    		exit(0);
    	}
    	else if(id > 0)
    	{
    		pv(sem_id, -1);
    		printf("parent process
    ");
    		sleep(5);
    		pv(sem_id, 1);
    	}
    	else
    	{
    		printf("fork error
    ");
    		return 1;
    	}
    
    	wait(NULL);
    	semctl(sem_id, 0, IPC_RMID, sem_un);   //删除信号量
    	return 0;
    }
  • 相关阅读:
    placeholder在ie浏览器里不显示的问题解决
    条件注释判断浏览器版本<!--[if lt IE 9]>
    在CSS中,BOX的Padding属性的数值赋予顺序为
    PhpStorm的注册码、Key
    关于【bootstrap modal 模态框弹出瞬间消失的问题】
    python 常见算法
    scrapy 爬虫基础
    python中的小知识点
    python 数据结构简介
    前端插件定制--表头和表内容
  • 原文地址:https://www.cnblogs.com/fangyan5218/p/10618252.html
Copyright © 2011-2022 走看看