信号量是一个计数器,用于为多个进程提供对共享数据对象的访问。

在信号量上只有三种操作可以进行,初始化、递增和增加,这三种操作都是原子操作。递减操作可以用于阻塞一个进程,增加操作用于解除阻塞一个进程。

为了获得共享资源,需要测试信号量,若信号量为正,则进程可以使用该资源,这时信号量值减一。否则信号量值为0,进程进入休眠状态。当进程不再使用由一个信号量控制的共享资源时,信号量值加一。如果有正在休眠的进程,则唤醒它们。常用的信号量的形式为二元信号量。即是原子性的。

信号量的功能:负责数据操作的互斥、同步等功能。本质上是一种数据操作锁。

我们为什么要使用信号量呢?为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使⽤用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来协调进程对共享资源的访问的。其中共享内存的使用就要用到信号量。

信号量只能进行两种操作等待和发送信号,PV操作,即P(sv)和V(sv),P申请资源,则将可用资源数-1,V释放资源,则将可用资源数+1。

内核为每个信号量集合维护着一个semid_ds结构:

struct semid_ds
{struct ipc_perm sem_perm;unsigned short sem_nsems; //该集合的信号量数目time_t sem_otime;time_t sem_ctime;
};

每个信号量都有一个无名的结构:

  unsigned short  semval;   /* semaphore value */unsigned short  semzcnt;  /* # waiting for zero */unsigned short  semncnt;  /* # waiting for increase */pid_t           sempid;   /* process that did last op */

当我们想使用信号量时,首先要通过调用函数semget来获得一个信号量ID:

#include<sys/sem.h>
int semget(key_t key,int nsems,int flag);
//成功,返回信号量ID,出错返回-1

semctl函数包含了多种信号量操作

int semctl(int semid,int semnum,int cmd,.../* union semun arg */);
//第四个参数是可选的,取决于请求的命令,如果使用该参数,则其类型是semun,它是多个命令特定的联合。
union semun
{int val;       //for SETVAL;struct semid_ds *buf;     //for IPC_STAT and IPC_SET;unsigned short *array;    //for GETALL and SETALL
};

我们通常使用的:IPC_RMID:从系统中删除该信号量集合

SETVAL:设置成员semnum的semval值,该值由arg.val指定

函数semop自动执行信号量集合上的操作数组。

int semop(int semid,struct sembuf semoparray[],size_t nops);
//成功,返回0,失败,返回-1;semoparray是一个指针,指向由sembuf结构表示的信号量操作数组,nops规定该数组中操作的数量
struct sembuf
{unsigned short sem_num;   //0,1,2,3,....short sem_op;  //(负值,0,正值)-1,0,1short sem_flg;  //IPC_NOWAIT,SEM_UNDO
};

对集合中每个成员的操作由相应的sem_op值规定。此值可以为负值,0,正值。最易于处理的是为正值,说明需要释放资源,则sem_op的值会加到信号量值上,如果指定了undo标志,则从此信号量调整之后的值上减去sem_op;如果sem_op是负值,说明需要申请资源,则信号量会减去sem_op的绝对值,如果指定undo标志,则sem_op的绝对值也加到信号量的调整值上。sem_op为0,表示调用进程希望等待到该信号量值为0。

对于信号量调整,如果在进程终止时,它占用了经由信号量分配的资源,那么就会成为一个问题。无论何时只要为信号量操作指定了SEM_UNDO标志,然后分配资源(sem_op< 0),那么内核就会记住该特定信号量,分配给调用进程多少资源。对每个操作都指定SEM_UNDO,以处理在未释放资源条件下进程终止的情况。

信号量主要解决互斥与同步问题,下面举个栗子:(实现父子进程输出成对AA或BB)

//comm.h
#ifndef _COMM_H_
#define _COMM_H_#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>#define PATHNAME "."
#define PROJ_ID 0X6666
union semun {int  val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  };int CreateSem(int nums);
int  GetSem(int nums);
int DestroySem(int semid);
int initSem(int semid,int nums,int initval);
int SemP(int semid,int who);
int SemV(int semid,int who);#endif  //_COMM_H_
#include"comm.h"static int CommSemSet(int nsems,int flags)
{key_t key = ftok(PATHNAME,PROJ_ID);if(key < 0){//printf("%d\n", key);perror("ftok");return -1;}int semid = semget(key,nsems,flags);if(semid < 0){perror("semget");return -2;}return semid;
}int  CreateSemSet(int nums)
{return CommSemSet(nums,IPC_CREAT|IPC_EXCL|0666);
}int GetSem(int nums)
{return CommSemSet(nums,IPC_CREAT);
}
int DestroySem(int semid)
{if(semctl(semid,0,IPC_RMID) < 0){perror("semctl");return -1;}return 0;
}int initSem(int semid,int nums,int initval)
{union semun _un;_un.val =  initval;if(semctl(semid,nums,SETVAL,_un) < 0){perror("semctl");return -1;}return 0;
}static int CommPV(int semid,int who,int op)
{struct sembuf _sem;_sem.sem_num = who;_sem.sem_op = op;_sem.sem_flg = 0;if(semop(semid,&_sem,1) < 0){perror("semop");return -1;}   return 0;
}
int SemP(int semid,int who)
{return CommPV(semid,who,-1);
}
int SemV(int semid,int who)
{return CommPV(semid,who,1);
}
//sem.c
#include"comm.h"int main()
{int semid = CreateSemSet(1);initSem(semid,0,1); pid_t id = fork();if(id == 0)//child{int _semid = GetSem(0);while(1){SemP(_semid,0);printf("A");fflush(stdout);usleep(123456);printf("A");fflush(stdout);usleep(345678); SemV(_semid,0);}}else{while(1){SemP(semid,0);printf("B");fflush(stdout);usleep(234567);printf("B");fflush(stdout);usleep(456789);//  usleep(121212);SemV(semid,0);}wait(NULL);}DestroySem(semid);printf("sem quit!\n");return 0;
}
//Makefile
sem:sem.c comm.cgcc -o $@ $^
.PHONY:clean
clean:rm -f sem

运行结果:

[Linux]信号量相关推荐

  1. Linux信号量 sem_t简介

    简介请移步: https://blog.csdn.net/qq_19923217/article/details/82902442 https://blog.csdn.net/evsqiezi/art ...

  2. linux申请信号量,linux 信号量

    https://www.jianshu.com/p/6e72ff770244 无名信号量 只适合用于一个进程的不同线程 #include #include #include #include #inc ...

  3. 最全面的 linux 信号量解析

    一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为 0,说明它被占用,测试的线程 ...

  4. linux文件信号量删除,linux信号量_閑の洎茬

    1.1 创建信号量 int semget( key_t key,   //标识信号量的关键字,有三种方法:1.使用IPC--PRIVATE让系统产生, // 2.挑选一个随机数,3.使用ftok从文件 ...

  5. 最全面的linux信号量解析

    信号量 一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程) 所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明 它被占用, ...

  6. linux 信号量semget,51CTO博客-专业IT技术博客创作平台-技术成就梦想

    semget() 可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集: 系统调用:semget(); 原型:intsemget(key_t key,int nsems ...

  7. linux 信号量锁 内核,Linux内核中锁机制之信号量、读写信号量

    在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...

  8. Linux信号量之用户态信号量(Posix信号量->无名信号量)

    相关API: 1.初始化信号量 int sem_init(sem_t* sem,int pshared,unsigned int value); //pshared为信号量最多由几个进程共享.Linu ...

  9. Linux信号量之内核信号量

    一.内核信号量 Linux内核的信号量在概念和原理上与用户态的System V的IPC机制信号量是一样的,但是它绝不可能在内核之外使用,它是一种睡眠锁. 如果有一个任务想要获得已经被占用的信号量时,信 ...

  10. linux信号量简介

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

最新文章

  1. HIbernate的优缺点
  2. [Unity3d]多个摄像机叠加效果
  3. python字典获取关联值_【Python实战12】使用字典关联数据
  4. CVPR2020最新实例分割论文
  5. 一篇文章为何能引起公众对联想那么多的争议?
  6. 蒟蒻の背包dp学习总结
  7. vc 判断哪个按键 被按下 消息 按键 状态
  8. 三、vue3--生命周期、Hook函数、 toRef和toRefs、其他的组合式API
  9. 南充计算机职业学校有哪些专业,南充旅游计算机职业中专学校2020年招生简介...
  10. 使用腾讯云 SCF 云函数压缩 COS 对象存储文件
  11. 室内定位技术及机场方案建议
  12. 大数据分析——Matplotlib入门教程
  13. 在中国怎么使用tiktok
  14. 什么是蜘蛛池的搜索留痕技术
  15. 关于51/STC单片机中断优先级的调整
  16. TM1638模块驱动(stm32,可以显示led灯,数码管,按键操作)(包含全部功能)
  17. 论文阅读笔记 1.《Open Flow: Enabling Innovation in Campus Networks》(2022.12.22)
  18. 增值类短信业务图文简介
  19. TOJ 3778.Sheldon's Friendship II
  20. informatica 初级操作流程

热门文章

  1. HDU1166-敌兵布阵
  2. [Swift]LeetCode1020. 飞地的数量 | Number of Enclaves
  3. 使用Gitolite搭建Gitserver
  4. HDU 5691 Sitting in Line 状压dp
  5. Codeforces Round #277 (Div. 2) 题解
  6. 使用ACE_SOCK_SEQPACK_Association获取socket连接的本地及远程IP端口信息
  7. lisp 揭 院长_HISLISPACSRIS EMR系统简介
  8. java 自定义报表_灵活数据分析 | 自定义数据分析_集力数据系统平台_Java报表系统软件...
  9. thinkphp mysql 更新_THINKPHP5修改数据库数据出现“缺少更新条件”的错误
  10. html生成自定义表格,自定义js的表格插件