详解管道和共享内存
1.管道
什么是管道
- 管道是Unix中最古老的进程间通信的形式。
- 我们把从一个进程链接到另一个进程的一个数据流称为一个“管道”
- 管道有:匿名管道和命名管道
1.1 匿名管道
1.1.1匿名管道的创建
匿名管道是一个特殊的文件,在文件IO中创建或打开一个文件用open函数来实现,但是open函数不能创建匿名管道文件,只能用pipe函数来创建
#include<unistd.h>
int pipe(int fd[2]);
功能:创建无名管道文件
参数:fd[2] 有两个成员fd[0]和fd[1],它俩都是文件描述符,
其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返会-1
1.1.2 匿名管道实现进程间通信原理
1.1.3 管道的读写规则
1.如果写端不写入且不关闭文件描述符,读端可能会长时间阻塞。
2.当我们在实际进行写入时,如果写入条件不满足,写入端就要进行阻塞。
3.如果写端关闭文件描述符,读端在读取完文件后会停留在结尾代表结束。
4**.如果读端关闭,写端进程会被系统关闭。**
1.1.4 匿名管道的特点
- 只能用于具有亲缘关系的进程之间通信。
- 管道是创建在内存中的,进程结束,空间释放,管道就不存在了。(生命周期在进程)
- 管道中的东西,读完了就删除了
- 管道是半双工的,数据只能向一个方向流动;要双方通信时,则需要建立两个管道。
1.2 命名管道
1.2.1 命名管道的创建
- 1.从命令行创建 ————.$ mkfifo filename
- 2.从程序里创建———— int mkfifo(const char*filename,mode_t mode);
- 参数:filename 是要创建的管道文件名;mode是创建文件的权限,生成的文件权限是经过mode&(~umask)运算后的结果
- 返回值:成功返回0,出错返回-1.
注意:mkfifo 是用来创建管道文件的节点,没有在内核中创建管道。只有通过open函数打开这个文件时才会在内核空间创建管道。
1.2.2 用命名管道实现server&client通信
//serve.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
umask(0);
int mf=mkfifo("fifo",0664);
if(mf<0)
{
perror("mkfifo error\n");
}
size_t rf=open("fifo",O_RDONLY);
if(rf<0)
{
perror("open error\n");
}
char buf[1024];
while(1)
{
ssize_t rp=read(rf,buf,sizeof(buf)-1);
if(rp>0){
buf[rp]=0;
printf("client say: %s",buf);
}
else if(rp==0){
printf("client quit!\n");
break;
}
else{
perror("rp error!\n");
}
}
close(rf);
return 0;
}
//###################################################
//client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int wf=open("fifo",O_WRONLY);
if(wf<0){
perror("wf error!\n");
}
char buf[1024];
while(1)
{
printf("please enter:");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0){
buf[s]=0;
write(wf,buf,s);
}
else{
perror("read 0 error!\n");
}
}
close(wf);
return 0;
}
结果展示:
2. 共享内存
2.1 基本介绍
共享内存在内存中是一块缓存,类似于用户空间的数组或malloc函数分配的空间一样。
2.2 共享内存示意图
2.3 相关函数介绍
2.3.1 shmget函数
功能:用来创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);
参数:
key为IPC_PRIVATE或ftok的返回值;
size:共享内存大小;
shmflg:权限位和打开方式。
返回值:
成功返回一个非负整数,即该共享内存段的标识符,即IPC的ID号,出错返回-1.
2.3.1.1 ftok 函数
函数形式:
key_t ftok(const char *pathname,int proj_id);
功能: 创建非0的key值。
参数: pathname 路径加文件名,proj_id非0的数。
返回值: 返回一个key,失败返回-1.
2.3.2 shmat 函数
函数形式:
void*shmat(int shmid, const void*shmaddr, int shmflg);
功能: 将共享内存段连接到进程地址空间
参数:
shmid 是共享内存的标识
shmaddr:是指定映射到用户空间的首地址,如果shmaddr 被设置为NULL,则映射到用户空间的首地址由系统自动分配;
shmflg: 若是SHM_RDONLY则共享内存为只读,若是0表示共享内存可读可写。
返回值: 成功返回一个指针指向共享内存第一节;失败返回-1.
2.3.3 shmdt 函数
函数形式:
int shmdt(const void *shmaddr)
功能:将共享内存段与当前进程脱离
参数:shmaddr:由shmat返回的指针
返回值: 成功返回0,失败-1
2.3.4 shmctl 函数
函数形式:
int shmctl (int shmid,int cmd,struct shmid_ds *buf)
;
功能:删除或查看共享内存
参数:shmid: 是要操作的共享内存标识符
cmd: 三种形式,IPC_STAT 为获取共享内存属性,读取的内容放在参数buf中;IPC_SET 设置共享内存属性,要设置的内容放在buf中;IPC_RMID 为删除共享内存,此时参数buf可能设置为NULL。
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构(属性缓存)。
返回值:成功0,失败-1.
例如:删除共享内存:shmctl(shmid,IPC_RMID,NULL);
补充:命令操作
- 共享内存查看命令:ipcs -m
- 共享内存删除命令: ipcrm -m shmid