首页 > linux进程间通信:system V 信号量和共享内存实现进程间同步

linux进程间通信:system V 信号量和共享内存实现进程间同步

关于信号量和共享内存的相关描述已经在前几篇提到过;

  • 信号量:即内核维护的一个正整数,可以使用内核提供的p/v接口进行该正整数的+/-操作,它主要用来表示系统中可用资源的个数,协调各个进程有序访问资源,防止发生冲突
  • 共享内存:操作系统从运行中的进程拥有的3G用户空间中提供一段用户态可以直接访问的内存块,并且该内存区域可以被其他进程共享

信号量在共享内存中的使用基本流程如下图:

在这里插入图片描述

两个信号量,一个用于控制读端进程对共享内存的访问,一个用于控制写端进程对共享内存的访问

当写端进程写入时,写信号量sem1 进程p操作,读信号量sem0进行v操作

当读端进程写入时,读信号量sem0进行p操作,写信号量sem1进程v操作

以下代码通过信号量实现了每次仅有一个进程操作共享内存

写端sem_write.c

#include
#include 
#include 
#include #include 
#include 
#include 
#include union semnum { int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int sem_id;//初始化信号量
void sem_init(int sem_id, int nsignum, int sem_value) { union semnum sem_union;sem_union.val = sem_value;if (semctl(sem_id,nsignum,SETVAL,sem_union) == -1) { printf("semctl failed
");_exit(-1);}
}//对信号量进行p操作 -1
void sem_p(int sem_id, int nsignum) { struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = -1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) { printf("semop P failed 
");_exit(-1);}}
//对信号量进行v操作 +1
void sem_v(int sem_id, int nsignum) { struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = 1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) { printf("semop P failed 
");_exit(-1);}
}//打印信号量
void sem_print(int sem_id, int nsignum) { int sem_value;sem_value = semctl(sem_id, nsignum, GETVAL);printf("sem[%d] = %d 
",nsignum, sem_value);
}int main() { int shm_id;key_t shm_key = ftok("./",111);key_t sem_key = ftok("./",112);//创建共享内存,并将共享内存进行映射,获取映射后的共享内存地址shm_id = shmget(shm_key , 1028 , IPC_CREAT | 0666);char *shm_addr = shmat(shm_id, NULL , 0);sem_id = semget(sem_key,2,IPC_CREAT | 0666);//创建两个信号量if(sem_id == -1) { sem_id = semget(sem_key, 2 ,0666);}else { sem_init(sem_id,0,0); //初始化读端信号量sem_init(sem_id,1,1); //初始化写端信号量}while(1) { sem_p(sem_id,1); //由于接下来要写入共享内存,则对写端信号量进行p操作,-1fgets(shm_addr,1020,stdin);sem_v(sem_id,0);//由于读进程在阻塞读,则写完之后立即将读信号量进行v操作}return 0;
}

读端sem_read.c

#include
#include 
#include 
#include #include 
#include 
#include 
#include union semnum { int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int sem_id;void sem_init(int sem_id, int nsignum, int sem_value) { union semnum sem_union;sem_union.val = sem_value;if (semctl(sem_id,nsignum,SETVAL,sem_union) == -1) { printf("semctl failed
");_exit(-1);}
}void sem_p(int sem_id, int nsignum) { struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = -1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) { printf("semop P failed 
");_exit(-1);}}void sem_v(int sem_id, int nsignum) { struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = 1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) { printf("semop P failed 
");_exit(-1);}
}void sem_print(int sem_id, int nsignum) { int sem_value;sem_value = semctl(sem_id, nsignum, GETVAL);printf("sem[%d] = %d 
",nsignum, sem_value);
}int main() { int shm_id;key_t shm_key = ftok("./",111);key_t sem_key = ftok("./",112);shm_id = shmget(shm_key , 1028 , IPC_CREAT | 0666);char *shm_addr = shmat(shm_id, NULL , 0);sem_id = semget(sem_key,2,IPC_CREAT | 0666);if(sem_id == -1) { printf("semget failed
");_exit(-1);}else { sem_init(sem_id,0,0); //初始化读端信号量sem_init(sem_id,1,1); //初始化写端信号量}while(1) { //对读端信号量进行p操作,-1,接下来要从共享内存读sem_p(sem_id,0);printf("from shm:%s
",shm_addr);//读完之后将写端信号量进行v操作,+1,因为还有写端进程需要写入(p操作),防止信号量变为-1sem_v(sem_id,1);}return 0;
}

输入如下:

在这里插入图片描述

更多相关:

  • 文章目录概念描述编程接口注意事项编程案例信号量基本接口使用案例信号量父子进程间通信信号量实现 两进程之间通信 概念描述 英文:semaphore 简称SEM,主要用来进行进程间同步本质:内核维护的一个正整数,可对其进行各种+/-操作分类:systemV 信号量、POSIX 有名信号量、POSIX 无名信号量用途:用来标示系统...

  • 生产者和消费者模型: 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据两者不冲突的前提: 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个生产者向缓冲区中填数据前需要判断缓冲区是否满,满了则等待,直到有空间消费者从缓冲区中取数据前需要判断缓冲区是否为空,空了则等待,直到缓冲区有数据在某一时刻,缓冲区中只允许有一个操作...

  •     先吐为敬!   最近心血来潮研究nodejs如何完成微信支付功能,结果网上一搜索,一大堆“代码拷贝党”、“留一手”、“缺斤少两”、“不说人话”、“自己都没跑通还出来发blog”、“各种缺少依赖包”、“各种注释都没有”、“自己都不知道在写什么”的程序大神纷纷为了增加自己博客一个帖子的名额而发布了各种千奇百�...

  • 阅读ceph源码过程中需要明确当前操作是由哪个线程发出,此时需要根据线程id来确认线程名称 C++获取线程id是通过系统调用来直接获取 函数描述 头文件: 函数名称:syscall(SYS_gettid) 该函数直接返回了一个pid_t int类型的数字,即为当前线程id 此外函数pthread_s...

  • 面试题 分库分表之后,id 主键如何处理? 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来支持。所以这都是你实际生产环境中必须考虑的问题。 面试题剖析 基于数据库的实现方案 数据库自增 id 这个就是说你的...

  • ORM操作    单表、一对多表操作 1 from django.db import models 2 3 4 class UserGroup(models.Model): 5 title = models.CharField(max_length=32) 6 7 8 class UserInfo(m...

  • 建立如下表: 建表语句: class表创建语句 create table class(cid int not null auto_increment primary key, caption varchar(32) not null)engine=innodb default charset=utf8;student表创建语句 c...

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...