一、信号量

1.1 基本介绍

信号量是用来处理进程间同步和互斥问题的一种进程间通信机制,包括一个称为信号量的变量和在该信号量下等待资源的进程等待队列,以及对信号量进行的两个原子操作(PV操作),其中,信号量对应于某一种资源,取一个非负的整形值。信号量值(通常用sem_id表示)指的是当前可用的该资源的数量,若等于0则意味着目前没有可用的资源。

1.1.1 同步与互斥

同步关系:指在某些时刻为完成某种任务而使得多个进程协调工作而产生的制约关系,也称为直接制约关系(如管道通信中,必须先写后读);
互斥关系:指当一个进程访问某临界资源时,另一个想要访问该临界资源的进程必须等待,直到当前访问临界资源的进程访问结束,释放该资源后,另一个进程才能去访问临界资源,也称为间接制约关系;(通俗地说就是在只有一个洗手间多人等待时,当此时洗手间里的人使用完出来后,才能轮到下一个人)
同步是一种更为复杂的互斥,而互斥是一种特殊的同步
进程间的同步与互斥关系存在的根源在于临界资源

1.1.2 临界资源

临界资源:在同一时刻只允许有限个(通常只有一个)进程可以访问(读)或修改(写)的资源,通常包括硬件资源(处理器、内存、存储器及其他外围设备等)和软件资源(共享代码段、共享结构和变量等)
临界区:访问临界资源的代码(临界区本身也叫临界资源)

1.1.3 PV操作

P操作:如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码);如果没有可用的资源(信号量值=0,)则此操作所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程);
V操作:如果在该信号量值的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待它,则释放一个资源(即信号量值加1);

1.1.4 使用信号量的基本步骤

①创建信号量或获得在系统中已存在的信号量,调用semget()函数(不同进程通过使用同一个信号量键值来获得同一个信号量)
②初始化信号量,调用semctl()函数的SETVAL操作(当使用互斥信号量时,通常将信号量初始化为1)
③进行信号量的PV操作,调用semop()函数(是实现进程间的同步和互斥的核心工作部分)
④在系统中删除不需要的信号量,调用semctl()函数的IPC_RMID操作

1.2 函数介绍

1.2.1 semget()

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget( key_t key, int nsems, int semflg);

①函数功能:创建或获取信号量
②函数参数
第一个参数→key是所创建或打开的信号量集的键值,即fork()的返回值;
第二个参数→nsems是指定需要的信号量数目,几乎总是1;
第三个参数→semflg是一组标志,当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作
③函数返回值:成功,返回信号量集的标识符;失败,返回-1;

1.2.2 semctl()

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semun, int cmd, ...);

①函数功能:初始化信号集或删除信号集
②函数参数
第一个参数→semid为semget()返回的信号量键值;
第二个参数→semun为操作信号在信号集中的编号,第一个信号为0;
第三个参数→cmd是在semid指定的信号量集合上执行此命令,命令如下:

SETVAL:设置信号量集中的一个单独的信号量的值,此时需要传入第四个参数;
IPC_RMID:从系统中删除该信号量集合;
IPC_SEAT:对此集合取semid_ds结构,并存放在由arg.buf指向的结构中;

第四个参数→可选,如使用该参数,其类型为semun,是多个特定命令参数的联合,该联合不在任何系统头文件中定义,需要自行在代码中定义

union semun
{int val;struct semid_ds  * buf;unsigned  short  *array;struct  seminfo  *__buf;
};

③函数返回值:成功,返回一个正数;失败,返回-1;

1.2.3 semop()

#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid,struct sembuf *sops,unsigned nsops);

①函数功能:操作一个或一组信号,或叫PV操作
②函数参数
第一个参数→semid为semget()返回的信号量键值;
第二个参数→sops是一个指针,指向一个信号量操作数组,信号量操作由结构体sembuf结构表示如下:

struct sembuf
{unsigned short sem_um;//操作信号在信号集中的编号short  sem_op;//操作为负(P操作),其绝对值大于信号的现有值,操作将会阻塞,直到信号值≥ |sem_op|。通常用于获取资源的使用权;//操作为正(V操作),其值会加到现有的信号内值上。通常用于释放所控制资源的使用权;                                                          //操作为0:如sem_flg没有设置IPC_NOWAIT,则调用该操作的进程或线程将暂时睡眠,直到信号量的值为0;否则进程或线程会返回错误EAGAIN;short sem_flg;//信号操作标识符,有如下两种选择://IPC_NOWAIT:对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设置错误消息;//SEM_UNDO:程序结束时(正常退出或异常终止),保证信号会被重设为semop()调用前的值。避免程序在异常情况下结束时未解锁锁定的资源,造成资源被永远锁定,造成死锁。};

第三个参数→nsops为信号操作结构的数量,恒大于或等于1;
③函数返回值:成功,返回0;失败,返回-1;

1.3 信号量编程

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define  FTOK_PATH  "/dev/zero"
#define  FTOK_PROJID    0x22union semun
{int val;struct semid_ds  *buf;unsigned  short  *arry;
};union semun sem_union;
int semid;int semaphore_init(void);         //信号量初始化
int semaphore_p(int semid);         //父进程进行P操作
int semaphore_v(int semid);         //子进程进行V操作(优先子进程先运行)
void semaphore_term(int semid);int main(int argc,char **argv)
{pid_t pid;int i;int semid;if((semid = semaphore_init()) < 0){printf("semaphore initial failure:%s\n",strerror(errno));return -1;}if((pid = fork()) < 0){printf("create child process failure:%s\n",strerror(errno));return -2;}else if(0 == pid){printf("child process[%d] start running and do something now...\n",getpid());sleep(3);printf("child process done.\n");semaphore_v(semid);       //子进程进行V操作sleep(1);printf("child process exit now\n");exit(0);}printf("parent process P operator wait child process over.\n");semaphore_p(semid);       //子进程没进行v操作前,父进程在阻塞在此printf("parent process[%d] start running and do something now...\n",getppid());sleep(2);printf("parent process destroy semaphore and exit\n");semaphore_term(semid);return 0;
}
int semaphore_init(void)
{key_t   key;int  semid;union semun sem_union;sem_union.val = 0;       //将信号量的值设为0key = ftok(FTOK_PATH,FTOK_PROJID);      //获取IPC关键字keyif(key < 0){printf("ftok() get key failure:%s\n",strerror(errno));return -1;}printf("ftok() get key successfully!\n");semid = semget(key,1,IPC_CREAT|0644);        //创建或获取信号量,信号量不存在则创建if(semid < 0){printf("semget() get semid  failure:%s\n",strerror(errno));return -2;}if(semctl(semid,0,SETVAL,sem_union) < 0) //初始化信号集,设置信号量集中的一个单独的信号量的值,并使用该参数{printf("semctl() set initial value failure: %s\n", strerror(errno));     //设置初始化值失败return -3;}printf("semaphore get key_t[0x%x] and semid[%d]\n", key, semid); //key数据类型为16进制整数return semid;
}void semaphore_term(int semid)
{if(semctl(semid,0,IPC_RMID,sem_union) < 0){printf("semctl() delete semaphore ID failure:%s\n",strerror(errno));}return ;
}int semaphore_p(int semid)
{struct sembuf  _sembuf;_sembuf.sem_num = 0;       //第一个信号编号为0,最后一个为nsems-1_sembuf.sem_op  =  -1;      //操作为负操作(P操作),semid ≥ |sem_op| 才能继续运行,否则阻塞_sembuf.sem_flg  =  SEM_UNDO;    //信号操作标识,程序结束时(正常退出或异常终止),保证信号值会被重设为semop()调用前的值。避免程序在异常情况下结束时未解锁锁定的资源,造成资源被永远锁定。造成死锁。if(semop(semid,&_sembuf,1) < 0){printf("semop  P  operator   failure:%s\n",strerror(errno));return -1;}return 0;
}int semaphore_v(int semid)
{struct sembuf  _sembuf;_sembuf.sem_num = 0;       _sembuf.sem_op  =  1;      _sembuf.sem_flg  =  SEM_UNDO;  if(semop(semid,&_sembuf,1) < 0){printf("semop V  operator   failure:%s\n",strerror(errno));return -1;}return 0;
}

运行结果:

ftok() get key successfully!
semaphore get key_t[0x22050005] and semid[1]
parent process P operator wait child process over.
child process[24213] start running and do something now...
child process do something over...
parent process[24161] start running and do something now...
child process exit now
parent process destroy semaphore and exit

Linux之进程间通信③——信号量(Semaphore)相关推荐

  1. Linux环境进程间通信 信号量

    信号量与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制.相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志.除了用于访问控制外,还可用于进程 ...

  2. 【Linux】进程间通信-信号量详解及编程实例

    前面一篇文章线程同步之信号量同步 讲的是线程之间的信号量,这篇讲的更加具有通用性,能够实现进程之间的同步. 信号量概述 信号量定义: 它是一个特殊变量,只允许对它进行等待和发送信号这两种操作. P(信 ...

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

    0. 前言    进程是一个独立的资源管理单元,不同进程间的资源是独立的,不能在一个进程中访问另一个进程的用户空间和内存空间.但是,进程不是孤立的,不同进程之间需要信息的交互和状态的传递,因此需要进程 ...

  4. 进程间通信 [3] —— 信号SIGNAL、信号量SEMAPHORE

    原创首发于CSDN,转载请注明出处,谢谢! https://blog.csdn.net/weixin_46959681/article/details/114527183 文章目录 什么是信号 信号的 ...

  5. Linux IPC进程间通信(三):信号量

    系列文章: Linux IPC进程间通信(一):管道 Linux IPC进程间通信(二):共享内存 Linux IPC进程间通信(三):信号量 Linux IPC进程间通信(四):消息队列 文章目录 ...

  6. Linux 3.进程间通信(shmget shmat shmdt shmctl 共享内存、signal signaction sigqueue 信号、semget semctl semop 信号量)

    Linux 3.进程间通信(IPC) 共享内存 共享内存的接口指令 shmget 创建获取获取共享内存 shmat 映射:连接共享内存到当前进程的地址空间 shmdt 断开与共享内存的连接 shmct ...

  7. linux c 进程间通信

    进程间通信概述 进程间通信(InterProcess Communication,IPC)是指在不同进程之间传播或交换信息. Linux的进程间通信方法有管道(Pipe)和有名管道(FIFO).信号( ...

  8. Linux下进程间通信的六种机制详解

    linux下进程间通信的几种主要手段:        1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具 ...

  9. Linux下进程间通信概述

    1. Linux下进程间通信概述 P83-P84 将第一页和第二页合并起来讲了 引言:前面我们学习了一下进程,我们知道多,进程间的地址空间相对独立.进程与进程间不能像线程间通过全局变量通信. 如果想进 ...

最新文章

  1. Gartner2018新兴技术成熟度曲线:人机界线日益模糊!
  2. bash shell命令(1)
  3. HTML中有关表格的基本属性知识
  4. ExtJS中给Tree节点加click事件
  5. 408. Valid Word Abbreviation
  6. Java装饰器模式详解
  7. 专访OPPO Find X5产品经理:深耕自研芯片 以最高标准打造极致旗舰体验
  8. oracle 配置 ACL 使用数据库发送WebServic请求时需要
  9. 139团队(大型研发团队,大型敏捷开发团队,大型团队结构,敏捷绩效管理)...
  10. 云服务器网站logo,云服务器 logo
  11. 2、树莓派声卡设置和alsactl命令的使用
  12. VC dll依赖性查看工具depends
  13. 爬虫报错requests.exceptions.ProxyError:/Failed to establish a new connection: [WinError 10061]
  14. django后台搜索显示Related Field got invalid lookup: icontains
  15. Power BI与Tableau的对比与选择
  16. 如何在官网上下载Java JDK的历史版本
  17. Linux安装Jenkis后启动Unit jenkins.service entered failed state (一)
  18. 基于Cplex的分支定界
  19. 知了猴的营养成分和作用
  20. 山西财经大学计算机二级成绩,山西财经大学2018年各省市各批次录取分数线

热门文章

  1. 红米k30 android版本,Redmi K30至尊纪念版的三大升级,1999的安卓5G旗舰手机
  2. php文件怎么改为mp3,mgg格式怎么转换为mp3
  3. ArcGIS 制图流程 非常详细
  4. 奇舞学院学习笔记之JavaScript一页通
  5. 1223,又是新的一年
  6. 遇上好心的电影售票员
  7. python外星人入侵游戏代码_黄哥Python:猜数字游戏代码
  8. 电脑小白的装机必备小知识
  9. 奥运中国女排夺冠,12年后再登顶世界之巅!
  10. 欧洲文艺复兴时期,中国在干啥?