五十一、进程间通信——System V IPC 之进程信号量
51.1 进程信号量
51.1.1 信号量
- 本质上就是共享资源的数目,用来控制对共享资源的访问
- 用于进程间的互斥和同步
- 每种共享资源对应一个信号量,为了便于大量共享资源的操作引入了信号量集,可对所有信号量一次性操作。对信号量集中所有操作可以要求全部成功,也可以部分成功
- 二元信号量(信号灯)值为 0 和 1
- 对信号量做 PV 操作2
51.1.2 信号量集属性
51.1.3 创建信号量集
- 函数参数:
- key:用户指定的信号量集键值
- nsems:信号量集中信号量个数
- semflg:IPC_CREAT,IPC_EXCL 等权限组合
- 返回值:成功,返回信号量集 ID,出错,返回 -1
51.1.4 信号量集控制
- 函数参数:
- semid:信号量集 ID
- semnum:0 表示对所有信号量操作,信号量编号从 0 开始
- cmd:控制命令,通过 cmd 参数设定对信号量集要执行的操作
- IPC_STAT:获取信号量集的属性 ---> buf
- IPC_SET:设置信号量集的属性 ---> buf
- IPC_RMID:删除信号量集 ---> buf
- GETVAL:返回信号量的值 ---> val
- SETVAL:设置 semnum 信号量的值 ---> val
- GETALL:获取所有信号量的值 ---> arryr
- SETALL:设置所有信号量的初始值 ---> array
- arg:即 ... ,semun 联合体变量
- val:放置获取或设置信号量集中某个信号量的值
- buf:信号量集属性指针
- array:放置获取或设置信号量集中所有信号量的值
51.1.5 信号量集操作
- 函数参数:
- semid:信号集 ID
- sops:sembuf 结构体数组指针
- sem_num:信号集中信号量的编号
- sem_op:正数为 V 操作,负数为 P 操作,0 可用于对共享资源是否已用完的测试
- sem_flg:SEM_UNDO 标识,表示在进程结束时,相应的操作将被取消。如果设置了该标志,那么在进程没有释放共享资源就退出时,内核将代为释放
- nsops:第二个参数中结构体数组的长度
- 返回值:成功返回 0;出错返回 -1
- 其他说明:
- 用于信号量集中信号量的加和减操作(PV 操作)
- 可用于进程间的互斥或同步
51.2 信号量例子
51.2.1 PV 操作
(1)PV模块
sem_pv.h
#ifndef INCLUDE_SEM_PV_H_
#define INCLUDE_SEM_PV_H_ #include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <malloc.h> union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}; /** 初始化 semnums 个信号灯/信号量值(value) */
extern int sem_I(int semnums, int value); /** 对信号量集(semid)中的信号灯(semnum)作 P() */
extern void sem_P(int semid, int semnum, int value); /** 对信号集(semid) 中的信号灯(semnum)作V(value)操作 */
extern void sem_V(int semid, int semnum, int value); /** 销毁信号量集(semid) */
extern void sem_D(int semid); #endif /* INCLUDE_SEM_PV_H_ */
sem_pv.c
#include "sem_pv.h" /** 初始化 semnums 个信号灯/信号量值(value) */
int sem_I(int semnums, int value)
{
/** 创建信号量集 */
int semid;
/** 创建信号量集 */
semid = semget(IPC_PRIVATE, semnums, IPC_CREAT | IPC_EXCL | );
if(semid < ){
return -;
} union semun un;
unsigned short *array = (unsigned short *)calloc(semnums, sizeof(unsigned short));
int i;
for(i = ; i < semnums; i++){
array[i] = value;
}
un.array = array; /**
* 初始化信号量集中所有信号灯的初值
* 0: 表示要初始化所有的信号灯
*/
if(semctl(semid, , SETALL, un) < ){
perror("semctl error");
return -;
}
free(array);
return semid;
} /** 对信号量集(semid)中的信号灯(semnum)作 P() */
void sem_P(int semid, int semnum, int value)
{
assert(value >= ); /** 定义 sembuf 类型的结构体数组,放置若干个结构体变量,对应要操作的信号量、P或V操作 */
struct sembuf ops[] = {{semnum, -value, SEM_UNDO}};
if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < ){
perror("semop error");
}
} /** 对信号集(semid) 中的信号灯(semnum)作V(value)操作 */
void sem_V(int semid, int semnum, int value)
{
assert(value >= ); /** 定义 sembuf 类型的结构体数组,放置若干个结构体变量,对应要操作的信号量、P或V操作 */
struct sembuf ops[] = {{semnum, value, SEM_UNDO}};
if(semop(semid, ops, sizeof(ops)/sizeof(struct sembuf)) < ){
perror("semop error");
}
} /** 销毁信号量集(semid) */
void sem_D(int semid)
{
if(semctl(semid, , IPC_RMID, NULL) < ){
perror("semctl error");
}
}
编译:
gcc -o obj/sem_pv.o -Iinclude -c src/sem_pv.c
(2)互斥操作
atm_account.h
#ifndef INCLUDE_ATM_ACCOUNT_H_
#define INCLUDE_ATM_ACCOUNT_H_ #include <malloc.h>
#include <assert.h>
#include <string.h> typedef struct {
int code;
double balance;
int semid; ///< 在共享资源上绑定一个信号量集
}atm_account; /** 取款 */
extern double atm_account_withdraw(atm_account *a, double amt); /** 存款 */
extern double atm_account_deposit(atm_account *a, double amt); /** 查看账户余额度 */
extern double amt_account_balanceGet(atm_account *a); #endif /* INCLUDE_ATM_ACCOUNT_H_ */
atm_account.c
#include "sem_pv.h"
#include "atm_account.h" /** 取款 */
double atm_account_withdraw(atm_account *a, double amt)
{
assert(a != NULL); /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */
sem_P(a->semid, , );
if(amt < || amt > a->balance){
/** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
sem_V(a->semid, , );
return 0.0;
} double balance = a->balance;
sleep();
balance -= amt;
a->balance = balance;
/** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
sem_V(a->semid, , );
return amt;
} /** 存款 */
double atm_account_deposit(atm_account *a, double amt)
{
assert(a != NULL); /** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */
sem_P(a->semid, , );
if(amt < ){
/** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
sem_V(a->semid, , );
return 0.0;
}
double balance = a->balance;
sleep();
balance += amt;
a->balance = balance;
/** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
sem_V(a->semid, , ); return amt;
} /** 查看账户余额度 */
double amt_account_balanceGet(atm_account *a)
{
assert(a != NULL);
/** 对信号量集 semid 中的0号信号量/信号灯作 P(1) 操作 */
sem_P(a->semid, , );
double balance = a->balance;
/** 对信号量集 semid 中的0号信号量/信号灯作 V(1) 操作 */
sem_V(a->semid, , );
return balance;
}
测试代码:atm_account_test.c
#include "atm_account.h"
#include "sem_pv.h"
#include <unistd.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h> int main(void)
{
/** 在共享内存中创建银行账户 */
int shmid;
if((shmid = shmget(IPC_PRIVATE, sizeof(atm_account), IPC_CREAT | IPC_EXCL | )) < ){
perror("shmget error");
return ;
} /** 进行共享内存映射(a 为映射的地址) */
atm_account *a = (atm_account *)shmat(shmid, , );
if(a == (atm_account *)-){
perror("shmat error");
return ;
}
a->code = ;
a->balance = ; /** 创建信号量集并初始化(1 个信号量/信号灯,初值为 1) */
a->semid = sem_I(, );
if(a->semid < ){
perror("sem_I(1, 1) error");
return ;
}
printf("balance: %f\n", a->balance); pid_t pid;
if((pid = fork()) < ){
perror("fork error");
return ;
}
else if(pid > ){
/** 父进程执行取款操作 */
double amt = atm_account_withdraw(a, );
printf("pid %d withdraw %f form code %d\n", getpid(), amt, a->code);
wait(); /** 对共享内存的操作要在解除映射之前 */
printf("balance: %f\n", a->balance); sem_D(a->semid); ///< 销毁信号量集
shmdt(a); ///< 解除共享内存的映射
shmctl(shmid, IPC_RMID, NULL);///< 释放共享内存
}
else {
/** 子进程进行取款操作 */
double amt = atm_account_withdraw(a, );
printf("pid %d withdraw %f form code %d\n", getpid(), amt, a->code); shmdt(a); ///< 解除共享内存的映射
} return ;
}
编译运行如下:
51.2.2 PV操作--读者写者案例
目的:利用进程信号量的 PV操作实现进程间的同步问题
共享内存中读写数据(读者和写者问题)
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h> /** 读者和写者的共享资源 */
typedef struct {
int val;
int semid;
}Storage; void init(Storage *s)
{
assert(s != NULL); /** 创建信号量集(包含 2 个信号量) */
if((s->semid = semget(IPC_PRIVATE, , IPC_CREAT | IPC_EXCL | )) < ){
perror("semget error");
exit();
} /** 对信号量集中的所有信号量初始化 */
union semun{
int val;
struct semid_ds *ds;
unsigned short *array;
};
union semun un;
/** 2 个信号量的初值设置为 0 */
unsigned short array[] = {, };
un.array = array;
if(semctl(s->semid, , SETALL, un) < ){
perror("semctl error");
exit();
}
} void destroy(Storage *s)
{
assert(s != NULL);
if(semctl(s->semid, , IPC_RMID, NULL) < ){
perror("semctl error");
exit();
}
} void writer(Storage *s, int val)
{
/** 写入数据到 Storage */
s->val = val;
printf("%d write %d\n", getpid(), val); /** 设置信号量 0 号作 V(1) 操作 */
struct sembuf ops_v[] = {{, , SEM_UNDO}};
/** 设置信号量 1 号作 P(1) 操作 */
struct sembuf ops_p[] = {{, -, SEM_UNDO}}; /** V(s1) */
if(semop(s->semid, ops_v, ) < ){
perror("semop error");
} /** P(s2) */
if(semop(s->semid, ops_p, ) < ){
perror("semop error");
}
} void reader(Storage *s)
{
assert(s != NULL); /** 设置信号量 0 号作 P(1) 操作 */
struct sembuf ops_p[] = {{, -, SEM_UNDO}};
/** 设置信号量 1 号作 V(1) 操作 */
struct sembuf ops_v[] = {{, , SEM_UNDO}};
/** P(s1) */
if(semop(s->semid, ops_p, ) < ){
perror("semop error");
}
/** 从 Storage 中读取数据 */
printf("%d read %d\n", getpid(), s->val);
/** V(s2) */
if(semop(s->semid, ops_v, ) < ){
perror("semop error");
}
} int main(void)
{
/** 将共享资源 Storage 创建在共享内存中 */
int shmid;
if((shmid = shmget(IPC_PRIVATE, sizeof(Storage), IPC_CREAT | IPC_EXCL | )) < ){
perror("shmget error");
exit();
} /** 父进程进行共享内存映射 */
Storage *s = (Storage *)shmat(shmid, , );
if(s == (Storage *)-){
perror("shmat error");
exit();
} /** 创建信号量并初始化 */
init(s); pid_t pid;
pid = fork();
if(pid < ){
perror("fork error");
exit();
}
else if(pid > ){
int i = ;
for(;i <= ; i++){
writer(s, i);
}
wait();
destroy(s);
shmdt(s);
shmctl(shmid, IPC_RMID, NULL);
}
else{
int i = ;
for(;i <= ; i++){
reader(s);
}
shmdt(s);
}
}
五十一、进程间通信——System V IPC 之进程信号量的更多相关文章
- 四十九、进程间通信——System V IPC 之消息队列
49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...
- 五十、进程间通信——System V IPC 之共享内存
50.1 共享内存 50.1.1 共享内存的概念 共享内存区域是被多个进程共享的一部分物理内存 多个进程都可把该共享内存映射到自己的虚拟内存空间.所有用户空间的进程若要操作共享内存,都要将其映射到自己 ...
- 从并发处理谈PHP进程间通信(二)System V IPC
.container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...
- UNIX 进程间通讯(IPC)概念(Posix,System V IPC)
IPC(Inter-Process Communication,进程间通讯)可以有三种信息共享方式(随文件系统,随内核,随共享内存).(当然这里虽然说是进程间通讯,其实也是可以和线程相通的). 相对 ...
- System V IPC 之共享内存
IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行系列操作的一组机制: 通过信号量与其他进程进行同步 向其他进程发送消息或者从其他进程接收消息 ...
- System V IPC 之信号量
本文继<System V IPC 之共享内存>之后接着介绍 System V IPC 的信号量编程.在开始正式的内容前让我们先概要的了解一下 Linux 中信号量的分类. 信号量的分类 在 ...
- System V IPC 之消息队列
消息队列和共享内存.信号量一样,同属 System V IPC 通信机制.消息队列是一系列连续排列的消息,保存在内核中,通过消息队列的引用标识符来访问.使用消息队列的好处是对每个消息指定了特定消息类型 ...
- pause、jobs、setitimer(2)、system v ipc(day12)
一.pause()的使用 #include <unistd.h> int pause(void); 功能:等待信号的到来 返回值: - 错误 errno被设置 只有在信号处理函数执行完毕的 ...
- Linux 系统编程 学习:04-进程间通信2:System V IPC(1)
Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...
随机推荐
- 《生命》第五集:Birds (鸟类)
看了前四集之后意犹未尽,今天终于有时间来看第五集了. 本集讲的是鸟类,一个在恐龙开始繁荣的时代才开始有的物种. 鸟类和其他动物最不同的地方,就是羽毛,能隔热,保暖,最重要的是:能帮助他们飞行. 在秘鲁 ...
- nginx 反向代理 负载均衡
nginx反向代理 用户(浏览器) 请求网站资源 -> 直接定位到django后台(所有的请求压力,都直接给了后台) django默认对并发性 很差,并且处理网页的静态资源,效率很差 10万个并 ...
- Oracle Metric sequence load elapsed time
Oracle Metric sequence load elapsed time The sequence load elapsed time Oracle metric is the amount ...
- Python爬虫-爬取豆瓣电影Top250
#!usr/bin/env python3 # -*- coding:utf-8-*- import requests from bs4 import BeautifulSoup import re ...
- go笔记-值传递、引用传递
eg: func sliceModify(slice []int) { // slice[0] = 88 slice = append(slice, ) } func main() { slice : ...
- Lepus搭建企业级数据库全方位监控系统
前言 Lepus(天兔)数据库企业监控系统是一套由专业DBA针对互联网企业开发的一款专业.强大的企业数据库监控管理系统,企业通过Lepus可以对数据库的实时健康和各种性能指标进行全方位的监控.目前已经 ...
- 玩转3D Swiper美女性感秀之思路分析总结
前言 继一次的3D魔方之后,这次利用CSS3的transform.translate.rotate.preserve-3d等结合JS的requestAnimationFrame.class带你一起玩转 ...
- Gruntfile.js模板
module.exports = function(grunt) { // 配置项 var AppConfig = { name: 'app', //源文件目录 src: 'app/src', //生 ...
- Django之ORM
- Python进阶2---树的遍历和堆排序
二叉树的遍历 堆排序Heap Sort 堆排序的过程 完整过程: #打印完整的二叉树 import math #打印完全二叉树,此函数非必要只是为了显示便利! def print_tree(lst): ...