文章目录
共享内存:
最快的进程间通信方式,比起管道,少了从用户态缓冲区拷贝数据到内核态缓冲区和从内核态缓冲区拷贝数据到用户态缓冲区的步骤
原理:
在物理内存中开辟一块内存空间,通过页表映射到进程的虚拟地址空间.进程可以直接通过虚拟地址空间来访问到这块内存,进行操作
如果多个进程同时映射到这块物理内存,就可以实现相互通信.通过虚拟地址空间修改内存中的数据,其他进程也会随之改变
int shmget(key_t key, size_t size, int shmflg);
key: 共享内存在系统中的标识符
size: 共享内存的大小
shmflg: IPC_CREAT 共享内存如果存在就打开,如果不存在就创建
IPC_EXCL 共享内存如果存在就报错返回
mode: 共享内存的操作权限
返回值: 共享内存的操作句柄---正整数 失败的话返回-1
void *shmat(int shmid, const void *shmaddr, int shmflg);
第一次创建完共享内存时,它还不能被任何进程访问,shmat()函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间
头文件是#include<sys/shm.h>
shmid: 创建共享内存返回的句柄
shmaddr: 共享内存映射在虚拟地址空间的首地址位置---使用时通常置NULL
shmflg: SHM_RDONLY,共享内存只读---使用时置0表示可读可写
返回值: 成功的话返回映射的首地址 失败返回(void*)-1
int shmdt(const void *shmaddr);
用于将共享内存从当前进程中分离.注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用
参数shmaddr是shmat()函数返回的地址指针,调用成功时返回0,失败时返回-1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid: 共享内存的操作句柄
cmd: 即将进行的操作
IPC_STAT: 把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖
shmid_ds的值
IPC_SET: 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID: 删除共享内存段
buf: 用于获取和设置共享内存的信息
共享内存删除:
共享内存在删除的时候会先判断映射数是否为0,如果是,就直接删除,如果不是,说明还有其他进程正在使用,那么共享内存不能立即被删除.但是会拒绝后续进程的映射链接,等映射数为0时,删除共享内存
代码示例:
shm_read.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<error.h>
#include<stdlib.h>
#include<string.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
int main()
{
//int shmget(key_t key, size_t size, int shmflg);
int shmID=shmget(IPC_KEY,32,IPC_CREAT|0664);
if(shmID<0)
{
perror("shmget error");
return -1;
}
//void *shmat(int shmid, const void *shmaddr, int shmflg);
void *shm_start=shmat(shmID,NULL,0);
if(shm_start==(void*)-1)
{
perror("shmat error");
return -1;
}
int i = 0;
while(1)
{
printf("%s\n", shm_start);
sleep(1);
}
//int shmdt(const void *shmaddr);
shmdt(shm_start);
//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shctl(shmID,IPC_RMID,NULL);
return 0;
}
int sprintf( char *buffer, const char *format [, argument] … );
函数功能: 把格式化的数据写入某个字符串
返回值: 字符串长度(strlen)
buffer: char型指针,指向将要写入的字符串的缓冲区
format: 格式化字符串
[argument]...: 可选参数,可以是任何类型的数据
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<error.h>
#include<stdlib.h>
#include<string.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
int main()
{
//int shmget(key_t key, size_t size, int shmflg);
int shmID=shmget(IPC_KEY,32,IPC_CREAT|0664);
if(shmID<0)
{
perror("shmget error");
return -1;
}
//void *shmat(int shmid, const void *shmaddr, int shmflg);
void *shm_start=shmat(shmID,NULL,0);
if(shm_start==(void*)-1)
{
perror("shmat error");
return -1;
}
int i = 0;
while(1)
{
sprintf(shm_start, "%s-%d", "陆子涵我爱你~", i++);
sleep(1);
}
//int shmdt(const void *shmaddr);
shmdt(shm_start);
//int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shctl(shmID,IPC_RMID,NULL);
return 0;
}
信号量的P/V操作:
代码示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
union semun{
int val;
struct semid_ds *buf;
unsigned short *arr;
struct seminfo *_buf;
};
int cerateSemSet(int nums);
int initSem(int semid, int nums, int initVal);
int P(int semid, int who);
int V(int semid, int who);
int destroySemSet(int semid);
#include "comm.h"
static int commSemSet(int nums, int flags)
{
key_t _key = ftok(PATHNAME, PROJ_ID);
if (_key < 0)
{
perror("ftok error\n");
return -1;
}
int semid = semget(_key, nums, flags);
if (semid < 0)
{
perror("semget error\n");
return -1;
}
return semid;
}
int createSemSet(int nums)
{
return commSemSet(nums, IPC_CREAT | IPC_EXCL | 0666);
}
int getSemSet(int nums)
{
return commSemSet(nums, IPC_CREAT);
}
int initSem(int semid, int nums, int initVal)
{
union semun _un;
_un.val = initVal;
if (semctl(semid, nums, SETVAL, _un) < 0)
{
perror("semctl error\n");
return -1;
}
return 0;
}
static int commPV(int semid, int who, int op)
{
struct sembuf _sf;
_sf.sem_num = who;
_sf.sem_op = op;
_sf.sem_flg = 0;
if (semop(semid, &_sf, 1) < 0)
{
perror("semop error\n");
return -1;
}
return 0;
}
int P(int semid, int who)
{
return commPV(semid, who, -1);
}
int V(int semid, int who)
{
return commPV(semid, who, 1);
}
int destroySemSet(int semid)
{
if (semctl(semid, 0, IPC_RMID) < 0)
{
perror("semctl error\n");
return -1;
}
}
#include "comm.h"
int main()
{
int semid = createSemSet(1);
initSem(semid, 0, 1);
pid_t id = fork();
if (id == 0)
{
int _semid = getSemSet(0);
while (1)
{
P(_semid, 0);
printf("A");
fflush(stdout);
usleep(100000);
printf("A");
fflush(stdout);
usleep(100000);
V(_semid, 0);
}
}
else
{
while (1)
{
P(semid, 0);
printf("B");
fflush(stdout);
usleep(100000);
printf("B");
fflush(stdout);
usleep(100000);
V(semid, 0);
}
wait(NULL);
}
destroySemSet(semid);
return 0;
}
封装动态库、静态库:
常用命令:
ipcs 查看当前系统的进程间通信方式 -m查看共享内存 -s查看信号量 -q查看消息队列
ipcrm(删除进程间通信方式) -m(因为都有操作句柄所以需要指定删除的是哪种IPC) 0(操作句柄-shmid)