Linux 学习笔记16 信号量Semaphore

信号量概念

  • 信号量(或信号灯)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。
  • 信号量是控制进程(或线程)同步(谁先执行,谁后执行)的一种方式。如:有时候我们需要保护一段代码,使它每次只能被一个执行进程运行。(如:抢火车票,每次只能一个用户买,买完再轮到下一个用户)。此时就需要二进制开关(加解锁)。

信号量分类:

  • System V 信号量,在内核中维护,可用于进程或线程间的同步,常用于进程的同步。
  • Posix 有名信号量,一种来源于 POSIX 技术规范的实时扩展方案(POSIX Realtime Extension),可用于进程或线程间的同步,常用于线程。(不常用)
  • Posix 基于内存的信号量,存放在共享内存区中,可用于进程或线程间的同步

获取共享资源(如共享内存shm,代码)进程

操作步骤
step1:测试控制该资源的信号量(类比火车进站,是否是绿灯)
step2:若信号量的值为正,则进程可以使用该资源。进程信号量值减 1(红灯), 表示它使用了一个 资源单位。此进程使用完共享资源后对应的信号量会加 1。以便其他进程使用
step3:若信号量的值为 0,则进程进入休息状态(等待), 直至信号量值大于 0。进程被唤醒,返回step1。

system V IPC机制 信号量函数学习

  • int semget(key_t key, int nsems, int semflg); //创建一个信号量"集合"  ,名字:semget - get a System V semaphore set identifier 获取一个system V的信号量集合ID
    
  • int semop(int semid, struct sembuf *sops, size_t nsops);//semop接口是一个原子操作
    
  • int semctl(int semid, int semnum, int cmd, ...)
    
  • semget参数解析:
    semget(key_t key, int nsems, int semflg);

    • key:类似于shm的key值,key 是唯一标识一个信号量的关键字,如果为 IPC_PRIVATE(值为 0,创建一 个只有创建者进程才可以访问的信号量,通常用于父子进程之间;非 0 值的 key(可以通过ftok 函数获得)表示创建一个可以被多个进程共享的信号量
    • nsems:信号量的个数
    • semflg:信号量权限,可设置为IPC_CREATE|0600
      示例代码如下:
#include <func.h>int main(int argc,char * argv[]){int sems_id;//semset idsems_id=semget(1000,1,IPC_CREAT|0600);//一旦创建,数目不可修改,修改要使用接口semctlERROR_CHECK(sems_id,-1,"semget");return 0;                                                                                               }

执行效果如下:

注意,信号量第一次被设定之后,不能再更改信号量的权限、参数等,只能使用 semctl 修改

  •  semctl(int semid, int semnum, int cmd, ...)参数解析
    
    • semid:信号量ID

    • semnum:参数 semnum 为集合中信号量的编号,当要用到成组的信号量时,从 0 开始。一 般取值为0,表示这是第一个也是唯一的一个信号量

    • cmd:参数 cmd 为执行的操作。
      常用cmd为:IPC_RMID(立即删除信号集,唤醒所有被阻塞的进程)
      GETVAL(根据 semnum 返回信号量的值,从 0 开始,第一个信号量编号为 0)
      SETVAL(根据 semnum 设定信号的值,从 0 开始,第一个信号量编号为 0)
      GETALL(获取所有信号量的值,第二个参数为 0,将所有信号的值存入 semnum.array 中)、SETALL(将所有 semnum.array 的值设定到信号集中,第二个参数为 0)等
      代码如下:

#include <func.h>int main(int argc,char * argv[]){int sems_id;//semset idsems_id=semget(1000,1,IPC_CREAT|0600);//一旦创建,数目不可修改,修改要使用接口semctlERROR_CHECK(sems_id,-1,"semget");int ret=semctl(sems_id,0,SETVAL,1);//0代表只有一个信号量,1代表将信号量的值初始化为1                     ERROR_CHECK(ret,-1,"semctl");return 0;}

setval之后看不出变化,此时采用getval 查看信号量的变化,代码如下:

#include <func.h>int main(int argc,char * argv[]){int sems_id;//semset idsems_id=semget(1000,1,IPC_CREAT|0600);//一旦创建,数目不可修改,修改要使用接口semctlERROR_CHECK(sems_id,-1,"semget");int ret=semctl(sems_id,0,SETVAL,1);//0代表只有一个信号量,1代表将0号信号量的值初始化为1ERROR_CHECK(ret,-1,"semctl");ret=semctl(sems_id,0,GETVAL);//GETVAL取0号信号量的值ERROR_CHECK(ret,-1,"semctl");printf("value=%d\n",ret);//打印setval设置的0号信号量的值                                                return 0;}

执行效果如下:

删除信号量集合代码如下:

#include <func.h>//删除信号量集合
? int main(int argc,char * argv[]){int sems_id;//semset idsems_id=semget(1000,1,IPC_CREAT|0600);//一旦创建,数目不可修改,修改要使用接口semctlERROR_CHECK(sems_id,-1,"semget");int ret=semctl(sems_id,0,IPC_RMID);//0代表只有一个信号量,1代表信号量的值初始化为1ERROR_CHECK(ret,-1,"semctl");return 0;}

执行效果如下:

  • PV操作
  • semop参数解析:
  •  semop(int semid, struct sembuf *sops, size_t nsops);//semop接口是一个原子操作
    
    • semid:由 semget 设定的semID

    • sops :是指向一个结构体数组的指针,即一个结构体数组的起始地址

    • nsops:结构体的数量,一般都设置为1

  • sops的结构体数组指针
  •  struct sembuf{unsigned short sem_num;  /* semaphore number */ 信号量ID,第一个信号量ID为 0short          sem_op;   /* semaphore operation */ 信号量操作 p代表-1操作,v代表+1操作short          sem_flg;  /* operation flags */ 通常设为:SEM_UNDO}
    

例:两个进程同时对共享内存(shm)进行写操作,使用PV操作,代码如下:

#include <func.h>#define N 10000000//父子进程同时对共享内存做加法(写)//测试是否能够成功等于20000000,pv操作                                                                       int main(int argc,char * argv[]){int shmid;//存储创建的共享内存idshmid=shmget(1000,4096,IPC_CREAT|0600);ERROR_CHECK(shmid,-1,"shmget");printf("shmid=%d\n",shmid);int *p=(int*)shmat(shmid,NULL,0);ERROR_CHECK(p,(int *)-1,"shmat");p[0]=0;//初始化共享内存值为0int i;int sems_id;//设置信号量sems_id=semget(1000,1,IPC_CREAT|0600);//一旦创建,数目不可修改,修改要使用接口semctlERROR_CHECK(sems_id,-1,"semget");int ret=semctl(sems_id,0,SETVAL,1);//0代表只有一个信号量,1代表信号量的值初始化为1ERROR_CHECK(ret,-1,"semctl");struct sembuf sopp,sopv;//p对信号量减1操作,v加1操作sopp.sem_num=0;sopp.sem_op=-1;//等待信号量变为可用,p操作sopp.sem_flg=SEM_UNDO;//一般都写SEM_UNDO,防止程序崩溃//SEM_UNDO能够使程序崩溃时进程还原成原来的状态sopv.sem_num=0;//0号信号量sopv.sem_op=1;//发送信号通知信号量可用,v操作sopv.sem_flg=SEM_UNDO;if(!fork()){//子进程写for(i=0;i<N;i++){semop(sems_id,&sopp,1);//p操作,加锁p[0]=p[0]+1;semop(sems_id,&sopv,1);//v操作,解锁}exit(0);}else{//父进程写for(i=0;i<N;i++){semop(sems_id,&sopp,1);p[0]=p[0]+1;semop(sems_id,&sopv,1);}wait(NULL);printf("ret=%d\n",p[0]);}return 0;}

执行效果如下:

  • 多个信号量操作
  • 获取多个信号量,代码如下:
#include <func.h>//一个信号量集合中创建多个信号量//int main(int argc,char * argv[]){int sems_id;sems_id=semget(1000,2,IPC_CREAT|0600);ERROR_CHECK(sems_id,-1,"semet");return 0;                                                                                               }

执行效果如下:

  • 设置多个信号量 SETALL 代码如下:
#include <func.h>int main(int argc,char * argv[]){int sems_id;//semset idsems_id=semget(1000,1,IPC_CREAT|0600);//一旦创建,数目不可修改,修改要使用接口semctlERROR_CHECK(sems_id,-1,"semget");unsigned short arr[2]={2,5};//两个信号量的值设置为非负数                                                int ret=semctl(sems_id,0,SETALL,arr);//当设置setall时,第二个参数就不起作用,因此一般填0ERROR_CHECK(ret,-1,"semctl");bzero(arr,sizeof(arr));//清空arr,采用bzero接口ret=semctl(sems_id,0,GETALL,arr);//获取刚设置的信号量的值ERROR_CHECK(ret,-1,"semctl");printf("arr[0]=%d,arr[1]=%d\n",arr[0],arr[1]);//打印设置的信号量的值return 0;}

执行效果如下:

Linux 学习笔记16 信号量相关推荐

  1. Linux学习笔记16——磁盘的分区、格式化、检验与挂载

    目录 一.磁盘的分区.格式化.检验与挂载 1,观察磁盘分区状态 lsblk 列出系统上的所有磁盘列表 blkid 列出设备的 UUID 等参数 parted 列出磁盘的分区表类型与分区信息 2,磁盘分 ...

  2. Linux学习笔记 16(存储设备管理)

    (1) 列出Linux服务器的文件系统的磁盘空间占用情况 (2) 将系统内的所有特殊文件格式及名称都列出来 (3) 以易读容量格式的方式,显示/boot下可用磁盘容量 (4) 将当前各个分区中可用的i ...

  3. linux学习笔记16

    LVM 讲解 步骤: 1.fdisk /dev/sdb         //给一个磁盘分区 2.创建三个新分区 3.t 改变分区类型为 8e       //在分区创建完成后 " t &qu ...

  4. (零)我为什么要写Linux学习笔记?

    我对Linux有强烈的好奇心,兴趣:写学习笔记会让我有成就感:我记忆力实在不好.好吧,其实真正原因是:如果我能把自己学到的知识简明扼要的讲出来,写出来,那便证明我真的学懂了我想学的知识.没时间怎么办? ...

  5. 20155303狄惟佳预备作业三Linux学习笔记

    20155303狄惟佳预备作业三Linux学习笔记 初次接触Ubuntu系统以及Linux内核,了解了其产生的历史,从感性来讲,深深吸引我的是其中蕴含的珍贵的开源精神,以及Stallman等人对&qu ...

  6. Linux学习笔记-文件权限与路径

    Linux学习笔记-文件与目录 目前从电子信息科学与技术转到了计算机专业,因此想趁着大四比较闲的时候补一些计算机的知识.我想说:你好,生活[斜眼笑]!愿生活温柔以待!哈哈,这是我写的第一篇博客,谨以此 ...

  7. LINUX学习笔记共五部分

    LINUX学习笔记1--LINUX系统命令 1.         界面切换: a)         进入字符界面:按住ctrl+alt+F1: 1.         首先需要进入用户登入,输入用户名和 ...

  8. Linux学习笔记12——配置ftp、squid、Tomcat、Samba、MySQL主从

    Linux学习笔记12 Linux学习笔记12 配置FTP服务 配置pure-ftpd 开机启动 上传下载文件 配置vsftpd CentOS 70安装配置Vsftp服务器 搭好vsftp之后出现55 ...

  9. Linux学习笔记10

    Linux学习笔记10 Linux学习笔记10 正则表达式 源码包约定目录 Shell脚本约定目录 Shell脚本的创建与执行 date命令 同步时间 Shell脚本预设变量 与用户交互 数学计算 S ...

最新文章

  1. ARM Linux内核Input输入子系统浅解
  2. Bech32编码 (4)地址验证示例
  3. Function types cannot have argument labels 错误解决方案
  4. python的socket连接不上_Python套接字只允许一个连接,但在新的连接上断开,而不是拒绝...
  5. C#Semaphore信号量用法
  6. php使用curl发送请求时 添加header失效
  7. mac+免密登陆+linux,mac ssh免密登录Linux
  8. 关于服务器耗电量的计算
  9. Git命令:git常用命令
  10. oracle存储过程id递增,oracle存储过程——按id更新相关信息
  11. 显示分类对应的文章数目,为空的设置为0,按文章数目排序
  12. tcping命令详解
  13. CSDN获得积分的方法
  14. 使用Python做饼图
  15. 系统更换数据库oracle到mysql问题汇总
  16. python中莱布尼茨计算圆周率_python圆周率盘算(带进度条)
  17. 软件体系结构(三)名词解释
  18. GPGPU-SIM(原码阅读)(流多处理器部分完成)
  19. FluentData
  20. Java通过Jolt调用Tuxedo服务

热门文章

  1. 提升学习 Prompt 总结
  2. catia二维图坐标如何表示_CATIA二维工程制图中孔系尺寸及点坐标标注方法
  3. 【Eclipse下载与安装教程】
  4. kafka-生产者消息发送流程
  5. Unity2020版本使用Kinect的修复(附粒子人像互动demo)
  6. vue项目,解决IE浏览器报Promise未定义的错误
  7. 适配器模式(Adapter)
  8. 什么叫最少拍计算机控制系统,最少拍无波纹计算机控制系统的一种综合方法.doc...
  9. 音符和频率的对应关系计算
  10. 《豪杰音乐工作室》技巧拾遗