system V共享
创建共享内存&通信流程
- 现在物理内存中开辟一段空间
- 各个进程间通过页表结构将物理内存映射到自己的虚拟地址空间当中的共享区
- 戈各个进程间的通信时通过修改自己的虚拟地址空间当中的共享区的地址来完成的
特性:不同的进程对共享空间内存区进行读的时候,并不会抹除物理内存当中的值
创建共享内存&使用共享内存的接口
- 创建共享内存:int shmget(key_t key, size. _t size, int shmflag);
①key:共享内存的标识符
②size:共享内存的大小
③shmflag:IPC_ CREAT:如果想获取的共享内存不存在,则创建共享内存。如果存在则返回共享内存操作句柄.
IPC_EXCL | IPC_CREAT:如果想获取的共享内存存在,则报错
④按位或上权限,可以使用8进制的数字来进行参数的按位或
⑤返回值:成功返回共享内存的操作句柄 - 将进程附加到共享内存上:void * shmat(int shmid, const void * shmaddr, int shmflag);
①shmid:共享内存的操作句柄
②shmaddr:程序员去指定映射到共享区的哪一个地址 ,程序员一般都不会去选择,设置为NULL ,由操作系统来指定将内存映射到哪一个地址上。
③shmflag:0:可读可写
IPC_ RDONLY:只读
④返回值:返回映射到共享区的哪一个地址上,程序员就可以操作这个地址来对物理内存区域进行读写操作。 - 从共享内存当中分离进程:int shmdt(const void * shmaddr);
shmaddr:共享区当中映射的物理内存的首地址,shmat的返回值,成功返回1,失败返回0 - 共享内存的销毁:int shmctl(int shmid, int cmd, struct shmid. _ds * buf);
销毁:IPC_RMID:删除共享内存,标记共享内存为删除状态 - 获取共享内存信息
IPC_STAT:获取共享内存状态,需要搭配struct shmid. ds
buf:作为一个出参,用来获取共享内存状态信息,传入一个struct shmid _ds结构体对象的地址
操作:
- 写数据到共享内存中:
#include <stdio.h> //writeshm
#include <sys/shm.h>
#include <unistd.h>
#define shm_key 0x99999999
int main() {
int shmid = shmget(shm_key, 1024, IPC_CREAT | 0664);
if(shmid < 0) {
perror("shmget");
return 0;
}
void* lp = shmat(shmid, NULL, 0);
if(!lp) {
perror("shmat");
return 0;
}
int i = 0;
while(1) {
sprintf((char*)lp, "%s-%d", "linux", i);
i++;
sleep(1);
}
//shmdt(lp); 删除
//shmctl(shmid, IPC_RMID, NULL)
return 0;
}
- 从共享内存中读取数据:
#include <stdio.h> //readshm
#include <sys/shm.h>
#include <unistd.h>
#define shm_key 0x99999999
int main() {
int shmid = shmget(shm_key, 1024, IPC_CREAT | 0664);
if(shmid < 0) {
perror("shmget");
return 0;
}
struct shmid_ds buf;
shmctl(shmid, IPC_STAT, &buf);
printf("shm_size:%ld\n", buf.shm_segsz); //共享内存大小
void* lp = shmat(shmid, NULL, 0);
if(!lp) {
perror("shmat");
return 0;
}
while(1) {
printf("readshm read [%s]\n", (char*)lp); //0 ~ ∞
sleep(1);
}
shmdt(lp);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
消息队列
先进先出的特性,使用链表进行实现,队列当中的每一个元素都有一个类型,这个类型其实使用整数来区分,具有优先级特色的队列。
int msgget(key_t key, int msgflag); 创建一个新的或打开一个已经存在的消息队列
①key:消息队列的标识符
②msgflag:IPC_CREAT:用来创建一个消息队列
IPC_EXCL:查询由key指定的消息队列释放存在
IPC_NOWAIT:之后的消息队列操作都为非阻塞
IPC_CREAT|IPC_EXCL :按位或上权限
int msgsnd(int msgid, const void* msggp, size_t msgsz,int msgflag); 追加一条新消息到消息队列的系统调用语法
msgflag:0:当队列满的时候,则阻塞
IPC_NOWAIT:当队列满的时候,不阻塞,函数返回
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 从消息队列中取出消息
magtype:0 :什么类型都可以接收
>0:则返回队列当中消息类型为msgtype的第一个消息
<0:则返回队列当中消息类型小于等于msgtype绝对值的消息,如果这样的消息比较多,则返回类型最小的那一个
int msgctl(int msgid, int cmd, struct msgid_ds* buf); 控制消息队列的行为
cmd:IPC_STAT:取出系统保存的消息队列的msqid_ds 数据,并将其存入参数buf 指向的msqid_ds结构
中。
IPC_SET:设定消息队列的msqid_ds数据中的msg_perm成员。设定的值由buf指向的msqid_ds结构给出。
IPC_EMID:将队列从系统内核中删除。
IPC_RMID:删除,设置消息队列的属性
信号量:实现进程控制,同步与互斥的,system V
资源计数器+PCB等待队列,当进程需要访问一各临界资源的时候,先获取信号量(-1),然后再访问资源,访问完成资源之后,释放资源,对信号量进行+1操作;
如果实现互斥:
- 访问:当进程需要访问一个临界资源的时候,对信号量当中的资源计数器进行预减;
①信号量计数器当中的值等于0,意味着预减之前资源计数器当中的值为1,表示可以去访问临界资源,对计数器正常进行减1操作,访问临界资源。
②信号量计数器当中的值小于0,意味着预减之前资源计数器当中的值为0,表示不可以访问临界资源,江金城放到PCB等待队列当中。 - 释放:如果当前占用临界资源的进程,对临界资源访问完成之后,释放资源,则需要对信号量当中的资源计数器进行+1操作,唤醒PCB等待队列当中的进程
如果实现同步----保证对临界资源访问的合理性
1.初始化信号量:按照资源的个数来初始化信号量当中的计数器。
2.访问:直接对资源计数器进行-1操作,判断计数器的值。
>=0:访问临界资源。
<0:将PCB放到等待队列当中去。
3.释放当临界资源被占用的进程进行释放的时候,同时需要对信号量当中的资源计数器进行+1操作,唤醒PCB等待队列当中的进程来访问临界资源。
>0:之前资源计数器的值肯定>=0,表示PCB等待队列当中没有进程等待,不需要唤醒PCB等待队列当中的进程
<=0:之前资源计数器的值肯定<0,示PCB等待队列当中有进程等待,需要唤醒PCB等待队列当中的进程