一、概念
  简单来讲,信号量是一个用来描述临界资源的资源个数的计数器。
  信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件、外部设备等)来实现进程间通信,

他本身更只是一种外部资源的标识。信号量在此过程中负责数据操作的同步、互斥等功能。
  当请求 一个使用信号量来表示 的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求;等于0,无资源可用,

进程会进入睡眠状态直至资源可用。

  当进程不再使用一个使用信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减都是原子操作,这是由于信号量的主要作用

是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性。
  

原子操作:
   所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另

一个线程)。共享数据(全局变量或堆变量)的自增(++)操作在多线程环境下会出现错误是因为这个操作(一条c语句)被编译为汇编代码后不

止一条指令,因此在执行的时候可能执行了一半就被调度系统打断,去执行别的代码。

  我们把单指令的操作称为原子的(Atomic),因为无论如何,单条指令的执行是不会被打断的。

  总而言之,原子操作的状态是要么不做,要么做并且完成,没有做了但没有完成这种状态。

二、为什么要使用信号量

  为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,他可以通过生成并使用令牌来授权,在任意时刻只能

由一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占的执行。而信号量就可以提供这样的一  种访问机制让一个临界区

域同一时间只有一个线程访问它,也就是说信号量使用来协调进程对共享资源的访问的。其中共享内存的使用就要用到信号量。

三、信号量的工作原理:

  我们这里讲的是二元信号量:

  二元信号量(Binary Semaphore)是最简单的一种锁,它只用两种状态:占用与非占用。它适合只能被唯一一个线程访问的资源。当二元信号量

处于非占用状态时,第一个试图获取该二元信号量的线程会获得该锁,并将二元信号量置为占用状态,此后其他的所有试图获取该二元信号量的线程将会

等待,知道该锁被释放。

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

P(sv):若sv值大于0,就给它减1,;若他的值为0,就挂起该进程的执行

V(sv)如果有其他进程因等待sv而被挂起,就让他恢复运行;如果没有进程因等待而被挂起,那就给他们加1。

举个例子:
就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,他将得到信号量,并可以进入临界区,使sv减1,。而第二个进程将被阻止进入临界

区,因为当它试图执行P(sv)时,sv为0,他会被挂起以等待第一个进程离开临界区并执行V(sv)释放信号量,这时第二个进程就可以恢复运行。

四、相关函数

1、创建信号量集(每次创建一个多多个信号量)

头文件:#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

函数原型:int semget(key_t key,int nsems,int semflg);

返回值:调用成功返回IPC标识符,调用失败返回-1;

参数:

key:所创建或打开信号量集的键值,也可以有函数 ftok 生成(不同于fork)

nsems:表示创建的信号量集中信号量的个数,该参数只在创建信号量集时有效

semflg: IPC_CREAT:如果IPC存在,则打开它,若不存在,则创建IPC资源。

IPC_EXCL:不能单独使用,没有意义;当与IPC_CREAT搭配使用时表示:若IPC不存在则创建IPC资源,若存在,则产生错 误。二者一 起使用用来保证所得的对象是新建的,而不是打开已有的对象。

2、执行在信号量集上的控制操作

头文件:#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

函数原型:int semctl(int semid,int semnum,int cmd, …);

返回值:调用成功返回一个正数,调用失败返回-1。

参数:semid:信号量集IPC标识符,因为信号量一般是作为一个信号量集使用的,而不是一个单独的信号量。所以在信号量集的操作中,不但要知道IPC关键字值,也要知道信号量集中的具体的信号量。

semnum:操作信号在信号量集中的编号

cmd:系统设置了三种操作:

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

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

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

3、操作信号量

头文件:#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

函数原型:int semop(int semid,struct sembuf * sops,unsigned nsops);

返回值:调用成功返回0,调用失败返回-1。

参数:semid:信号量集标识码

nsops:信号结构的数量,恒大于或等于1.

sops:指向寻出信号操作结构的数组指针,信号操作结构的原型如下:

struct sembuf

{

unsigned short sem_num;  //操作信号在信号量集中的编号,第一个编号为0.

short sem_op;

short sem_flg;   //信号操作标志,可能的选择有两种:

IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息;

SEM_UNDO:程序结束时(无论正不正常结束),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。

};

五、代码实现:

Linux - 进程间通信 - 信号量的更多相关文章

  1. Linux进程间通信--信号量

    信号量绝对不同于信号,一定要分清,关于信号,上一篇博客中已经说过,如有疑问,请移驾! 信号量 一.是什么   信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件 ...

  2. linux进程间通信-信号量(semaphore)

    一 为什么要使用信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问 代码的临界区域.临界区域是指执 ...

  3. Linux进程间通信—信号量

    二.信号量(semophore) 信号量是一种计数器,可以控制进程间多个线程或者多个进程对资源的同步访问,它常实现为一种锁机制.实质上,信号量是一个被保护的变量,并且只能通过初始化和两个标准的原子操作 ...

  4. Linux进程间通信(二):信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

    我们已经知道,我们可以通过信号来终止进程,也可以通过信号来在进程间进行通信,程序也可以通过指定信号的关联处理函数来改变信号的默认处理方式,也可以屏蔽某些信号,使其不能传递给进程.那么我们应该如何设定我 ...

  5. Linux进程间通信(五):信号量 semget()、semop()、semctl()

    这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信 -- 信号.下面 ...

  6. 【转载】Linux的进程间通信-信号量

    原文:Linux的进程间通信-信号量 Linux的进程间通信-信号量 版权声明: 本文章内容在非商业使用前提下可无需授权任意转载.发布. 转载.发布请务必注明作者和其微博.微信公众号地址,以便读者询问 ...

  7. Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)

    Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)

  8. Linux进程间通信IPC学习笔记之同步二(Posix 信号量)

    Linux进程间通信IPC学习笔记之同步二(Posix 信号量)

  9. Linux进程间通信——使用信号量

    这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信——使用信号.下面 ...

随机推荐

  1. 提交Sublime Text 插件到Package Control

    最近写了一个lua智能提示的插件LuaSmartTips.这个插件一直都是自己一个人在用,昨天突然想把插件提交到Package Control,如果其他的人有这样的需求就可以直接安装. Package ...

  2. Java面试09|多线程

    1.假如有Thread1.Thread2.Thread3.Thread4四条线程分别统计C.D.E.F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现? 把相互独立的计算任 ...

  3. swift -- 代理delegate

    1.声明协议 protocol SecondDelagate { func sendValue(text : String!) -> Void } 2.声明代理属性 var delegate : ...

  4. 2017 Android 面试题 [ 基础与细节 ]

    2017 Android 面试题 [ 基础与细节 ] 感谢@chuyao抛出的这些问题,平时业务代码写多了,很多基础的东西变得含糊不清了,这次裸辞出来找工作确实没有之前顺利,顺便求上海Android开 ...

  5. Apache Flume日志收集系统简介

    Apache Flume是一个分布式.可靠.可用的系统,用于从大量不同的源有效地收集.聚合.移动大量日志数据进行集中式数据存储. Flume简介 Flume的核心是Agent,Agent中包含Sour ...

  6. python 中的input()和raw_input()功能与使用区别

    在python中raw_input()和input()都是提示并获取用户输入的函数,然后将用户的输入数据存入变量中.但二者在处理返回数据类型上有差别. input()函数是raw_intput()和e ...

  7. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt".

    昨天遇到一个比较奇怪的问题,运行VS2010调试程序的时候,总是会报一个错,然后程序就挂掉了:无可用源….,弹出一个窗口提示:System.AccessViolationException: Atte ...

  8. web CSS的知识- 关于后代选择器,子选择器,兄弟选择器的使用

    1. 后代选择器官方解释:后代选择器可以选择作为某元素后代的元素.理解:选择某一标签的后代中,所有的此标签标记例:ul em {color:red;}就是选择,h1标签后代中中,所有的em.代码如下: ...

  9. 浅析Content Negotation在Nancy的实现和使用

    背景介绍 什么是Content Negotation呢?翻译成中文的话就是"内容协商".当然,如果不清楚HTTP规范(RFC 2616)的话,可以对这个翻译也是一头雾水. 先来看看 ...

  10. [LeetCode] Trapping Rain Water II 题解

    题意 题目 思路 我一开始想的时候只考虑到一个结点周围的边界的情况,并没有考虑到边界的高度其实影响到所有的结点盛水的高度. 我们可以发现,中间是否能够盛水取决于边界是否足够高于里面的高度,所以这必然是 ...