文章目录
(一)共享内存
(1)共享内存实现进程间通信的原理:
- 共享内存在物理内存上开辟的空间
- 多个进程可以将自己的虚拟空间地址映射到该块物理空间上
- 多个进程都可以访问共享内存中的数据
- 共享内存上的改动可以被其他进程看到
- 并未实现同步机制,需要其他机制同步对共享内存的访问
(2)共享内存逻辑结构图
(二)共享内存相关函数
使用sys/shm.h
(1)创建(获得)共享内存
int shmget(key_t key, size_t size, int shmflg);
- key_t :键值用于标识使用同一个共享内存段;(IPC_PRIVATE创建只属于进程的共享内存,但不是真正的私有)
- size : 共享内存的容量(字节)
- shmflg : IPC_CREAT(创建生效 / 获得忽略)(9个位的权限标志,与文件权限mode类似)
- 返回值:成功返回共享内存标识符(非负数),失败-1
(2)共享内存和进程的连接(at)
void* shmat(int shm_id, const void* shm_addr, int shmflg);
- shm_id : 共享内存标识符
- shm_addr : 指定共享内存连接到当前进程的地址位置(通常是空指针NULL,表示让系统自己分配共享内存的地址)
- shmflg : 一般是0,SHM_RND(与shm_addr联合使用控制共享内存连接地址)、SHM_RDONLY(连接的内存只读)(一组位标志)
- 返回值:成功返回指向共享内存第一个字节的指针,失败-1
注意:
- 共享内存的读写权限:由创建者、它的访问权限、当前进程属主决定(类似于文件权限)
- 例外:shmflg & SHM_RDONLY为真时,此时的共享内存即使可写入,都不能写入;
(3)共享内存的控制(ctl)
int shmctl(int shm_id, int command, struct shmid_ds* buf);
- shm_id : shmget的返回值,共享内存标识符
- command : 采取的操作
命令 | 说明 |
---|---|
IPC_STAT | 把shmid_ds结构中的数据设置成共享内存的当前关联值 |
IPC_SET | 若进程有足够的权限,就把共享内存的当前关联值设置成shmid_ds结构中给定的值 |
IPC_RMID | 删除共享内存段 |
- buff :是一个指针,指向包含共享内存模式和访问权限的结构
- 返回值:成功返回0,失败-1
注意:删除一个正在处于连接状态的共享内存段后,通常还能够使用,直到最后一个进程脱离该共享内存段的连接(最好不要这样做!!哒咩)
(4)共享内存的分离(dt)
将共享内存从当前进程中分离;
int shmdt(const void* shm_addr);
- shm_addr是shmat()返回的指针
- 返回值:成功0,失败-1
注意:
共享内存分离,但未删除,只是分离的进程变得不可用
(三)练习巩固
(1)(未进程同步)简单的使用共享内存
- a.c
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <string.h>
//向共享内存中写入数据
void WriteToShm()
{
//创建共享内存
int shmid = shmget((key_t)8888, 128, IPC_CREAT | 0600);
if(shmid == -1)
{
perror("shmget err");
return;
}
//连接共享内存
char* shm = shmat(shmid, NULL, 0);
if(shm == NULL)
{
perror("shmat shm err");
return;
}
//向共享内存中写入数据
printf("input data : \n");
while(1)
{
char buff[128] = {
0};
fgets(buff, 127, stdin);
if(strncmp(buff, "end", 3) == 0)
{
break;
}
strncpy(shm, buff, strlen(buff) - 1);
}
//脱离连接
shmdt(shm);
}
int main()
{
WriteToShm();
return 0;
}
- b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
//从共享内存中读取数据
void ReadFromShm()
{
//获得共享内存
int shmid = shmget((key_t)8888, 128, IPC_CREAT | 0600);
if(shmid == -1)
{
perror("shmget jion err");
return;
}
//连接共享内存
char* shm = shmat(shmid, NULL, 0);
if(shm == NULL)
{
perror("shmat err");
return;
}
while(1)
{
printf("%s\n", shm);
sleep(2);
}
}
int main()
{
ReadFromShm();
return 0;
}
- 结果:
- 使用ipcs -m查看共享内存
- 结论:
- 从结果可知,a程序写入共享内存中的数据,b程序从共享内存中读取时(不管共享内存中的数据有没有更新,都会一直打印),违背了我们a输入一次,b程序打印一次
(2)使用信号量同步后的进程a , b
思路:
-
当a进程键盘输入写入共享内存期间,b进程不能读取共享内存,即要被阻塞
-
当b进程读取共享内存的数据,进行输出,这个期间 ,a进程不能再共享内存中写入数据,否则就会覆盖共享内存中的数据
-
使用2个信号量:初始值s1 = 1, s2 = 0 ;
-
a进程每次进行输入之前对s1进行p操作,b进程每次读取输出之前对s2 进行p操作,此时就会阻塞,需要等待a进程写入数据完毕后对s2进行v操作后,b进程才能对s2进行p操作;
-
当b进程在读取输出期间,a进程试图对s1进行p操作,此时阻塞,需等待b进程对s1进行v操作;
-
sem.h
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <string.h>
//两个信号量
#define SEMSIZE 2
union semun
{
int val;
};
//创建(获得)信号量集 并初始化信号量的值
void sem_init();
//p操作
void sem_p(int index);
//v操作
void sem_v(int index);
//销毁信号量集
void sem_destory();
- sem.c
#include "sem.h"
//默认的信号量集的ID
static int semid = -1;
//创建(获得)信号量集 并初始化信号量的值
void sem_init()
{
semid = semget((key_t)1234, SEMSIZE, IPC_CREAT | IPC_EXCL | 0600);
//创建失败,获得
if(semid == -1)
{
semid = semget((key_t)1234, SEMSIZE, IPC_CREAT);
//获取失败
if(semid == -1)
{
perror("semget err");
return;
}
}
//创建成功,并初始化
else
{
union semun tmp[SEMSIZE];
tmp[0].val = 1; //信号量的值
tmp[1].val = 0;
for(int index = 0; index < SEMSIZE; index++)
{
if(semctl(semid, index, SETVAL, tmp[index]) == -1)
{
perror("semctl init err");
return;
}
}
}
}
//p操作
void sem_p(int index)
{
if(index < 0 || index > SEMSIZE)
{
perror("index out of line");
return;
}
struct sembuf buf;
buf.sem_num = index;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
if(semop(semid, &buf, 1) == -1)
{
perror("semop p err");
return;
}
}
//v操作
void sem_v(int index)
{
if(index < 0 || index > SEMSIZE)
{
perror("index out of line");
return;
}
struct sembuf buf;
buf.sem_num = index;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
if(semop(semid, &buf, 1) == -1)
{
perror("semop v err");
return;
}
}
//销毁信号量集
void sem_destory()
{
if(semctl(semid, 0, IPC_RMID) == -1)
{
perror("sem_destory err");
return;
}
}
- a.c
#include <sys/shm.h>
#include "sem.h"
//向共享内存中写入数据
void WriteToShm()
{
//初始化信号量集
sem_init();
//创建共享内存
int shmid = shmget((key_t)8888, 128, IPC_CREAT | 0600);
if(shmid == -1)
{
perror("shmget err");
return;
}
//连接共享内存
char* shm = shmat(shmid, NULL, 0);
if(shm == NULL)
{
perror("shmat shm err");
return;
}
//向共享内存中写入数据
printf("input data : \n");
while(1)
{
sem_p(0);
char buff[128] = {
0};
memset(shm, 0, 128);
fgets(buff, 127, stdin);
if(strncmp(buff, "end", 3) == 0)
{
strcpy(shm, buff);
sem_v(1);
break;
}
strncpy(shm, buff, strlen(buff) - 1);
sem_v(1);
}
//脱离连接
shmdt(shm);
}
int main()
{
WriteToShm();
return 0;
}
- b.c
#include <sys/shm.h>
#include "sem.h"
//从共享内存中读取数据
void ReadFromShm()
{
//获得信号量集
sem_init();
//获得共享内存
int shmid = shmget((key_t)8888, 128, IPC_CREAT | 0600);
if(shmid == -1)
{
perror("shmget jion err");
return;
}
//连接共享内存
char* shm = shmat(shmid, NULL, 0);
if(shm == NULL)
{
perror("shmat err");
return;
}
while(1)
{
sem_p(1);
if(strncmp(shm, "end", 3) == 0)
{
break;
}
printf("%s\n", shm);
sem_v(0);
}
//脱离共享内存
shmdt(shm);
//销毁共享内存
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("destory shm err");
return;
}
//销毁信号量集
sleep(2);
sem_destory();
}
int main()
{
ReadFromShm();
return 0;
}
结果: