共享内存
共享内存是最快的进程通信方式。
因为不同于消息队列和管道,需要调用内核函数切换运行环境,而是直接在自己进程的虚拟地址空间里操作。
- 创建或打开共享内存
int shmget(key_t key, int size, int flag);
key:共享内存字段名
size:共享内存大小
flag:和创建文件的mode相同
返回值:返回共享内存标识码
- 将共享内存映射到进程地址空间
void *shmat(int shmid, const void *shmaddr, int shmflag)
shmid:共享内存标识
shmaddr:指定连接地址,一般为NULL
shmflag:一般为0
返回值:返回映射到虚拟地址空间的起始位置
- 取消内存映射
int shmdt(const void *shmaddr)
shmid:shmat返回的地址
- 删除共享内存
shmctl(shmid, IPC_RMID, 0)
在命令行删除
ipcrm -M key 删除共享内存
ipcs -m 查看共享内存
信号量集
信号量的本质是计数器
struct semaphore{
int value;
struct task_struct queue;//需求该信号控制的资源的队列
}
p(struct semaphore s){
s.value--;
if(value < 0){
进程阻塞,加入等待队列
}
}
v(struct semaphore s){
s.value++;
if(s.value <=0 ){
//说明曾经有至少一个进程在等
唤醒某个等待的进程
}
}
生产者消费者模型
死锁:多个进程访问互斥资源,每个进程都拿到一部分资源,如果要继续往下运行,就要拿到更多资源,而更多的资源由其他进程占有,每个进程在执行完毕之前不会释放已占有资源,形成环路等待。
必要条件
- 资源排他性
- 请求保持
- 环路等待
- 不可剥夺
银行家算法:https://blog.csdn.net/hanzheng6602/article/details/78765673
哲学家进餐:
Linux SystemV 信号量
//自定义的信号量
union semnum{
int val;
}
union semnu su;
//获取信号量集
int semget(key_t key, int nsems, int flag);
nsems:信号量个数
flag:IPC_CREATE || 0
//控制
int semctl(int id, int semnum, int cmd, ...);
cmd:SETVAL设置初值,有第四个参数su || GETVAL获得初值
/*pv操作*/
int semop(int id, struct sembuf *sb, int len);
//sb参数是对信号量的操作定义
struct sembuf{
int sem_num;
int sem_op;//v是1, p是-1
int sem_flag;
}
pv操作例
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main(){
//获得信号量集标识
int id = semget(1234, 1, IPC_CREATE|0644);
if (-1 == id)
perror();
struct sembuf sb[1] = {{0,-1,0}};
//执行一次p操作
semop(id, sb, 1);
}
利用信号量实现两个进程的互斥
任务:父进程打印XX,子进程打印OO,在XX或者OO是一个整体,在没有打印完成对的X或者O不允许另一个进程打印。
解决:将打印XX上锁,用一个互斥信号量。
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <stdio.h>
int id;
void p(void)
{
struct sembuf sb[1] = {{0,-1,0}};
semop(id, sb, 1);
}
void v(void)
{
struct sembuf sb[1] = {{0,1,0}};
semop(id, sb, 1);
}
void print(char c)
{
int i;
for(i=0; i<10; i++){
p();
printf("%c", c);
fflush(stdout);
sleep(rand()%3);
printf("%c", c);
fflush(stdout);
v();
sleep(rand()%3);
}
}
union semnu{int val;};
int main()
{
//创建有一个信号量的信号量集
id = semget(1234, 1, IPC_CREAT|0600);
union semnu su;
su.val = 1;
//给0号信号量赋初值
semctl(id, 0, SETVAL, su);
pid_t pid = fork();
if(0 == pid){
//子进程打印
print('O');
}
else{
print('X');
}
}
利用信号量保证共享内存的同步问题
pg1程序往共享内存不断写入数字,pg2不断读出数字。要保证pg2每次只读出最新写入的数字。
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
union semnu{int val;};
int main()
{
int shmid = shmget(1234, sizeof(int), IPC_CREAT|0600);
int semid = semget(1234, 1, IPC_CREAT|0600);
union semnu su;
su.val = 0;
semctl(semid, 0, SETVAL, su);
int *p = (int*)shmat(shmid, NULL, 0);
int num = 1;
struct sembuf sb[1] = {{0, 1, 0}};
while(1){
sleep(1);
*p = num++;
semop(semid, sb, 1);
}
}
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main()
{
int shmid = shmget(1234, 0, 0);
int semid = semget(1234, 1, 0);
int *p = (int*)shmat(shmid, NULL, 0);
struct sembuf sb[1] = {{0, -1, 0}};
while(1){
semop(semid, sb, 1);
printf("%d\n", *p);
}
}
执行结果
[root@localhost 6_3]# ./pg2
3
3
3
4
5
6
7
8
pg1先执行了3秒,所以现在信号量的值被v增加到3,pg2后执行直接先读取了3次然后两个进程同步起来。