无名管道
unix最古老的管道:
- 半双工(只能进行一个方向的读写)
- 在内存缓存区中(所以不属于文件系统),大小由系统决定,使用ulimit -a查看
- 数据先入先出且按字节(所以没有格式)传出,读取后就抛弃
- 只能在具有公共祖先(3代以内)的进程中使用
创建
#include <unistd.h>
int pipe(int filefd[2]);
- filefd也是用来接受返回的,接受两个文件描述符
- 创建的管道使用文件描述符进行操作
- 管道pipefd[0]用于读,pipefdp[1]用于写;
- 失败返回-1
操作
与文件操作一样,都是调用write、read,只是由于只能先进先出,所以不能使用lseek()等函数
读写控制
阻塞只与管道有关,而不是与write和read函数相关,管道的读写都有计数器,close相应管道后就会对应-1
读管道(阻塞):
- 管道中有数据,read返回实际读取的字节数
- 无数据
- 写端计数为0,则直接返回0,且不会修改buffer;
- 仍然有写端,则阻塞
写管道:
- 读端为0,进程异常终止
- 仍有读端
- 管道已满,write阻塞
- 管道未满,write将数据写入,并返回实际写入的字节数
读管道(非阻塞)
-
写端没有关闭,管道没有数据,直接返回-1
-
设置方法:
cint flags = fcntl(fd[0],F_GETFL); flag |= O_NONBLOCK; fcntl(fd[0],F_SETFL,flags);
有名管道FIFO
- 就是一个特殊文件,内容放置于内存,程序能及时写入读取
- 对通信进程不做要求
创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
- mode就是设置权限,和chmod一样,如644
- shell中创建:mkfifo 文件名
操作
- 首先打开该管道(文件)获得文件描述符
- 后续与文件操作一样,只是不能使用lseek
读写控制
- 以只读/只写形式打开文件,会阻塞等待另一个进程只写/只读形式打开
- 和无名管道一致
存储映射(内存映射)
- 本质上是将文件指定区域映射到程序的内存中,程序对此部分内存的操作就是对文件的操作,减少系统调用write、read,进而减少了模态切换而造成的开销
- 如果两个进程同时对一个文件同块区域进行了存储映射,那这两个进程就实现了通信
- 每次都需要一个文件会很麻烦,系统提供了
匿名存储映射
,不需要提供文件,但是只能用于父子进程,因为此时两者都具有指向该内存空间的指针
创建
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
- addr 指定内存指针,一般设置NULL让系统自行分配
- length 设置内存、文件映射区域大小,注意文件分配的内存要>=length
- prot 设置该区域的访问权限PROT_WRITE|PROT_READ
- flags 设置该区域数据被修改后程序的行为,MAP_SHARED|MAP_ANONYMOUS
- fd 文件描述符,匿名映射可以不设置
- offset 设置文件从哪开始映射到内存中
- 如果成功则返回指向该区域的内存指针,否则返回MAP_FAILED
操作
- 首先打开文件,获得文件描述符(匿名不需要)
- 调用
c
void *addr = mmap(NULL,512,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
- 关闭文件(匿名不需要)
- 写存储映射区
- 断开存储映射munmap(addr,1024)
补充
- 何时写入磁盘?
- flag为MAP_SHARED,由系统决定何时写回
- munmap调用
- 程序退出
- 系统内存不足
- 强制写回:
int msync(void addr[.length], size_t length, int flags);
其中flags为NS_ASYNC
- 能否感知文件内容变化
- flag为MAP_SHARED时,能直接感知到其他进程对文件内容的更新(注意其他进程得写回磁盘);
- 写入的是什么?
- 将映射内存的全部内容直接覆盖原本文件中的指定区域
- 如果文件分配的磁盘大小<length会怎么样
- 出现Bus error (core dumped)
- 解决方法:创建文件并分配 length 字节的空间
truncate -s length大小 文件名