LINUX中的IPC
用于多个进程间进行通讯的手段
多个进程间通讯的手段有三种(IPC)
共享内存
信号量
消息队列
IPC通信的唯一性
我们现在所使用的操作系统,多用户多任务的操作系统
多个任务间的通讯可能会用到多个的 IPC
在操作系统上可能会同时存在多个 IPC
在使用 IPC 进行通讯的时候 就需要找到合适的那个 IPC
要想能够找到最合适的那个 IPC 就需要有一个唯一识别 IPC 的东西
这个能够唯一识别 IPC 的东西 就叫做键值
可以类比成进程号
键值的创建
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
原函数
key_t ftok(const char *pathname, int proj_id);
函数的参数
const char *pathname 参考文件 建议写绝对路径
int proj_id 参考 id 0-255
函数的返回值
成功返回 键值
失败返回 -1
PS:ftok 的两个参数只要一样 在任意的文件夹下执行 ftok 的函数
得到的键值都是一样的
共享内存
共享内存就是一块共享的内存,共享内存是全局变量的一个加强版
全局变量的特性
作用域
整个工程
生命周期
随着工程的结束而结束
共享内存的特性
作用域
整个操作系统
生命周期
跟随操作系统
查看共享内存
ipcs -m
进程启动的时候要做的事情
进程在启动的时候会做两件事
1) 打开三个标准的文件描述符
0 标准输入
1 标准输出
2 标准错误
2) 操作系统会虚拟出一块 4G 大小的内存空间
0-3G 用户空间
3-4G 内核空间
虚拟出的这块空间进程是可以直接的使用的
共享内存的创建
函数头文件
#include <sys/ipc.h>
#include <sys/shm.h>
函数原型
int shmget(key_t key, size_t size, int shmflg);
函数的参数
key_t key, 键值
size_t size, 要创建的共享内存的大小
int shmflg IPC_CREAT|0644 如果共享内存不存在则创建,并赋予 0644 的权限,如果共享内存存在则打开共享内存
函数的返回值
成功返回 共享内存的 id
失败返回 -1
PS:
通过 shmget 这个函数创建的共享内存位于操作系统的内存空间上的
但是操作系统的内存空间不允许我们直接的访问
进程虚拟出来的内存空间 进程是可以直接访问的
假如虚拟出来的内存空间跟创建的内存空间有一定的关系
就可以间接的访问到共享内存
这个建立关系的过程就叫做内存的映射
内存的映射需要借助于系统提供的函数
共享内存的映射
函数的头文件
#include <sys/types.h>
#include <sys/shm.h>
函数的原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
函数的参数
int shmid, 共享内存的 id
const void *shmaddr, 要将共享内存映射的位置虚拟的内存空间的位置NULL 表示由系统自动分配一块可用的空间
int shmflg 0 一般为0
函数的返回值
成功返回 映射后的地址
失败返回 NULL
解除映射
函数的头文件
#include <sys/types.h>
#include <sys/shm.h>
函数的原型
int shmdt(const void *shmaddr);
函数的参数
const void *shmaddr:映射后的地址
函数的返回值
成功返回 0
失败返回 -1
删除共享内存
函数的头文件
#include <sys/ipc.h>
#include <sys/shm.h>
函数的原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数的参数
int shmid, 共享内存的 id
int cmd, 要执行的动作, IPC_RMID 删除共享内存
struct shmid_ds *buf NULL
函数的返回值
成功返回 0
失败返回 -1
PS:这个函数是一个多功能复用的一个函数
实例
写空间
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
key_t k;
int shmid;
char *p=NULL;
int main()
{
k = ftok("/",5);//创建键值
printf("k=%x\n",k);
shmid = shmget(k,10,IPC_CREAT|0644);//创建共享内存
if(shmid < 0)
{
perror("shmget");
return -1;
}
p = shmat(shmid,NULL,0);//内存的映射
if(p == NULL)
{
perror("shmat");
return -1;
}
//向内存写入数据
strcpy(p,"LQHAIXX\n");
//解除映射
shmdt(p);
return 0;
}
读空间
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
key_t k;
int shmid;
char *p=NULL;
int main()
{
//创建键值
k= ftok("/",5);
//打开共享内存
shmid = shmget(k,10,IPC_CREAT|0644);
//内存映射
p=shmat(shmid,NULL,0);
//读取数据
printf("p=%s\n",p);
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
信号量
信号量的本质就是一个数字
这个数字代表临界资源的数目
这个数字可以进行加也可以进行减
当这个数字加的时候表示 进程在释放资源
当这个数字减的时候 表示有进程在消耗资源
当这个数字减到 0 的时候 表示资源消耗完毕
如果还有进程来申请资源 进程就会阻塞
信号量的主要的作用保护临界资源
查看信号量相关的指令
ipcs -s
创建/打开信号量
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数的原型
int semget(key_t key, int nsems, int semflg);
函数的参数
key_t key, 键值
int nsems, 信号量的个数 一般写 1
int semflg IPC_CREAT|0644
函数的返回值
成功返回 信号量的 id
失败返回 -1
设置信号量的值/删除信号量
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数的原型
int semctl(int semid, int semnum, int cmd, ...);
函数的参数
int semid, 信号量的 id
int semnum, 信号量数组下标
int cmd, SETVAL 设置信号量的值
第四个参数直接写要给的值 IPC_RMID 删除信号量
函数的返回值
成功返回 0
失败返回 -1
信号量的消耗/释放
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数的原型
int semop(int semid, struct sembuf *sops, size_t nsops);
函数的参数
int semid, 信号量的 id
struct sembuf *sops, 执行的操作结构体
size_t nsops 要操作的信号量的个数
函数的返回值
成功返回 0
失败返回 -1
执行的操作结构体
struct sembuf{
unsigned short sem_num; 数组下标
short sem_op; +1 释放信号量
-1 消耗信号量
short sem_flg; 0 阻塞的消耗信号量
}
实例
sing1运行完才会运行sing2
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int i=0;
key_t k;
int semid;
struct sembuf mysem;
int main()
{
//创建键值
k=ftok("/",5);
//创建信号量
semid = semget(k,1,IPC_CREAT|0644);
//设置信号量的值
semctl(semid,0,SETVAL,1);
//消耗信号量
mysem.sem_num = 0;
mysem.sem_op =-1;
mysem.sem_flg = 0;
semop(semid,&mysem,1);
for(i=0;i<10;i++)
{
printf("i=%d\n",i);
sleep(1);
}
mysem.sem_op =1;
semop(semid,&mysem,1);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int i=0;
key_t k;
int semid;
struct sembuf mysem;
int main()
{
//创建键值
k=ftok("/",5);
//创建信号量
semid = semget(k,1,IPC_CREAT|0644);
//消耗信号量
mysem.sem_num = 0;
mysem.sem_op =-1;
mysem.sem_flg = 0;
semop(semid,&mysem,1);
for(i=0;i<10;i++)
{
printf("i=%d\n",i);
sleep(1);
}
mysem.sem_op =1;
semop(semid,&mysem,1);
return 0;
}
消息队列
消息队列相关基础知识
消息队列就是用来存放消息的队列
具有先进先出的特性
消息队列里的消息是可以进行分类
消息队列里的消息读取完成之后就消失了
创建消息队列
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数的原型
int msgget(key_t key, int msgflg);
函数的参数
key_t key, 键值
int msgflg IPC_CREAT|0644
函数的返回值
成功返回 消息队列的 id
失败返回 -1
往消息队列里发送消息
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数的原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数的参数
int msqid, 消息队列的 id
const void *msgp, 消息的结构体
size_t msgsz, 消息的大小 不包含类型
int msgflg 0 阻塞的发
函数的返回值
成功返回 0
失败返回 -1
消息结构体
struct msgbuf {
long mtype;
char mtext[80];
};
从消息队列里读取消息
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数的原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
函数的参数
int msqid, 消息队列的 ID
void *msgp, 读取到的消息存放的位置
size_t msgsz, 消息的大小
long msgtyp, 消息的类型
int msgflg 0 阻塞的收
函数的返回值
成功返回 成功读取的字节数
失败返回 -1
消息队列的删除
函数的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数的原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函数的参数
int msqid, 消息队列的 id
int cmd, 要执行的动作 IPC_RMID
struct msqid_ds *buf NULL
函数的返回值
成功返回 0
失败返回 -1
实例
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
key_t k;
int msgid;
struct msgbuf {
long mtype;
char name[10];
char passwd[10];
}mybuf;
int main()
{
//创建键值
k=ftok("/",5);
//创建消息队列
msgid = msgget(k, IPC_CREAT|0644);
//发送消息
mybuf.mtype = 1;
strcpy(mybuf.name,"LQHXXX");
strcpy(mybuf.passwd,"521125");
msgsnd(msgid,&mybuf,sizeof(mybuf)-sizeof(long),0);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
key_t k;
int msgid;
struct msgbuf {
long mtype;
char name[10];
char passwd[10];
}mybuf;
int main()
{
//创建键值
k=ftok("/",5);
//创建消息队列
msgid = msgget(k,IPC_CREAT|0644);
//从消息队列读取数据
msgrcv(msgid,&mybuf,sizeof(mybuf),1,0);
printf("name=%s,passwd=%s\n",mybuf.name,mybuf.passwd);
//删除消息队列
msgctl(msgid,IPC_RMID,NULL);
return 0;
}