一、进程之间的通信。--- 共享内存。
1、什么是共享内存?机制如何?
共享内存也是属于IPC对象,所以在使用之前,必须为共享内存申请key值。
共享内存由于存在于运行内存上,运行内存在linux下所有的进程都可以访问到,结论就是任意的两个进程都可以通过共享内存进行通信。
实现步骤: key值 -> ID号 -> 共享内存地址 -> 数据交换 -> 撤销映射。
2、 共享内存函数接口?
1)先申请key值。
key = ftok(".",10);
2)根据申请到的key值去申请共享内存的ID号。 -> shmget() -> man 2 shmget
功能: allocates a System V shared memory segment
//允许在系统中申请一块共享内存 -> 你的住房证明
头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
原型:
int shmget(key_t key, size_t size, int shmflg);
参数:
key: 共享内存的key值。
size:共享内存的总字节数,必须是PAGE_SIZE的倍数 #define PAGE_SIZE 1024
shmflg:IPC_CREAT|0666 -> 不存在则创建。
返回值:
成功:共享内存的ID号
失败:-1
3)根据共享内存ID号去运行内存上申请对应的空间。 -> shmat() -> man 2 shmat
头文件:
#include <sys/types.h>
#include <sys/shm.h>
原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid:共享内存的ID号。
shmaddr:NULL -> 系统自动分配空间给你 99.99999%
不为NULL-> 用户自己选择地址 0.000001%
shmflg:普通属性 0
返回值:
成功:共享内存起始地址。
失败:(void *)-1
4)撤销映射。 -> shmdt() -> man 2 shmdt
头文件:
#include <sys/types.h>
#include <sys/shm.h>
原型:
int shmdt(const void *shmaddr);
参数:
shmaddr: 共享内存起始地址。
返回值:
成功:0
失败:-1
5)删除共享内存IPC对象。 -> shmctl() -> man 2 shmctl
头文件:
#include <sys/ipc.h>
#include <sys/shm.h>
原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shmid:共享内存的ID号。
cmd:IPC_RMID -> 删除共享内存IPC对象
buf:如果是删除,填NULL就行了。
返回值:
成功:0
失败:-1
3、 案例 -- 使用共享内存通信机制,实现jack.c发送数据给rose.c。
rose.c每隔2S就打印一次共享内存的数据。
发送端:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[])
{
//1、 申请共享内存的key值。
key_t key = ftok(".",10);
//2、 根据key值去申请共享内存ID号。
int shmid = shmget(key,2048,IPC_CREAT|0666);
//3、 根据ID号去运行内存中申请对应的空间。
char *p = (char *)shmat(shmid,NULL,0);
//4、 不断将数据写入到共享内存上。
while(1)
{
fgets(p,2048,stdin);
//5、 如果把"quit"放在共享内存上,则进程退出。
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
return 0;
}
接收端:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
//1、 申请共享内存的key值。
key_t key = ftok(".",10);
//2、 根据key值去申请共享内存ID号。
int shmid = shmget(key,2048,IPC_CREAT|0666);
//3、 根据ID号去运行内存中申请对应的空间。
char *p = (char *)shmat(shmid,NULL,0);
//4、 不断地打印共享内存上的数据出来
while(1)
{
printf("from shm:%s",p);
sleep(1);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
//5. 撤销映射,删除IPC对象
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
结果:
接受端每隔2S就会打印一次共享内存上的数据,即使数据没有修改,也会打印,不会阻塞。
最终目标:
jack -> 输入一次数据 -> 发送一次 -> rose -> 接收一次数据 -> 打印一次
二、处理同步互斥的方式。 --- 信号量。
1、 什么是信号量?
信号量也是属于IPC对象,所以使用信号量之前必须先申请key值。
信号量不是用于进程之间的通信,只是作用与进程之间的通信。
2、学习信号量的函数接口?
1)申请key值。
key = ftok(".",10);
2)根据key值来申请信号量ID号。 -> semget() -> man 2 semget
功能: get a System V semaphore set identifier
//获取信号量的ID号
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
原型:
int semget(key_t key, int nsems, int semflg);
参数:
key: key值。
nsems: 信号量中元素的个数。
semflg:IPC_CREAT|0666
返回值:
成功:信号量的ID号
失败:-1
3)设置信号量元素的起始值/删除信号量的IPC对象。 -> semctl() -> man 2 semctl
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
原型:
int semctl(int semid, int semnum, int cmd, ...);
参数:
semid:信号量的ID号
semnum:需要操作的成员的下标 空间:0 数据:1
cmd: SETVAL -> 用于设置信号元素的起始值
IPC_RMID -> 删除信号量的ID号
...: 如果cmd填SETVAL,那么最后一个参数就是起始值
如果cmd填IPC_RMID,最后一个参数就会忽略。
例如:我想设置空间为1,数据为0,那么怎么做?
semctl(semid,0,SETVAL,1); -> 设置空间起始值为1
semctl(semid,1,SETVAL,0); -> 设置数据起始值为0
如果想删除信号量ID号。
semctl(semid,0,IPC_RMID);
4)如何实现信号量的P/V操作? (p操作(减1操作): 1变成0, v操作(加1操作): 0变成1)
semop() -> man 2 semop
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
原型:
int semop(int semid, struct sembuf *sops, size_t nsops);
参数:
semid:信号量的ID号
sops:进行P/V操作的结构体
struct sembuf{
unsigned short sem_num; 需要操作的成员的下标 空间:0 数据:1
short sem_op; P操作/V操作 p操作:-1 v操作:1
short sem_flg; 普通属性,填0。
}
nsops: 信号量操作结构体的个数: -> 1
返回值:
成功:0
失败:-1
3、案例:
我们要将信号量加入到刚才的代码中,处理同步互斥。
参考: shm+sem/