(三)Linux系统编程之进程间通信

1.什么是IPC

a.进程间通信
i.InterProcess Communication

2.进程间通信常用的4种方式
a.管道-简单
b.信号-系统开销小
c.共享映射区 -(有无血缘关系的进程间通信都可以)
d.本地套接字 - 稳定

由于每个进程都有各自不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不见,所以进程之间要减缓数据必须通过内核,在内核中开辟一块缓冲区。内核提供这种机制称之为进程间通信。

一、管道(匿名)

在这里插入图片描述
1.管道的概念
本质:
内核的缓冲区(内核里的一个空间)
伪文件 - 不占用磁盘空间(在硬盘上没有)
【因为它的操作跟文件IO操作一样,所以称之为伪文件】

特点:
两部分:
读端,写端,对应两个文件描述符
数据写端流入,读端流出。
数据是单向流动的–半双工

当操作管道的进程被销毁之后,管道自动释放了。

管道默认是阻塞的。【后端对管道操作就是对两个文件描述符的操作,而对文件描述符的操作就是文件的IO操作】

阻塞包括读和写。

①如果一个管道的写端一直在写,而读端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只写不读再次调用write会导致管道堵塞;

②如果一个管道的读端一直在读,而写端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只读不写再次调用read会导致管道堵塞;

③而当他们的引用计数等于0时,只写不读会导致写端的进程收到一个SIGPIPE信号,导致进程终止,只写不读会导致read返回0,就像读到⽂件末尾⼀样。

文件的读写不是read和write的行为,而是对文件描述符对应的文件的行为。

2.管道的原理
内部实现方式:队列
环形队列【通过算法实现,本质是线性队列】
特点:先进先出

缓冲区的大小
默认4K
大小会根据实际情况做适当的调整,不会无限放大

3.管道的局限性
队列:
数据只能读取一次,不能重复读取。【读取就把数据弹出去了】

半双工:就是数据传输的方向是单向的

匿名管道:
适用于有血缘关系的进程

4.创建匿名管道
int pipe(int fd[2]);
fd-传出参数
fd[0]-读端
fd[1]-写端

5.父子进程使用管道通信
单个进程能否使用管道完成读写操作?
可以

父子进程间通信是否需要sleep函数?
父 写 --写的慢
子 读 – 读的快
不需要

父进程fork出子进程,子进程也有两个文件描述符指向同一管道。
父进程关闭fd[0],子进程关闭fd[1],即父进程关闭管道读端,子进程关闭管道写端(因为管道只支持单向通信)。父进程就可以往管道里写,子进程可以从管道里读。

在这里插入图片描述

6.兄弟进程使用管道通信
父进程关闭读和写
父亲 - 资源回收

7.管道的读写行为

读操作
read(fd)

(1)有数据
read(fd) - 正常读,返回读出的字节数

(2)无数据
1>写端全部关闭

  • read解除阻塞,返回0
  • 相当于读文件读到了尾部

2>没有全部被关闭

  • read阻塞

写操作

(1)读端全部关闭
管道破裂,进程被终止
内核会给当前进程发信号SIGPIPE【终止进程】

(2)读端没全部关闭
1>缓冲区写满了

  • write阻塞

2>缓冲区没有满

  • write继续写

如何设置非阻塞?
默认读写两端都阻塞
设置读端为非阻塞pipe(fd)

fcntl - 变参函数
a)复制文件描述符 - dup
b)修改文件属性 - open的时候对应flag属性

设置方法:

//获取原来的flags
int flags=fcntl(fd[0],F_GETFL);

//设置新的flags
flags |= O_NONBLOCK;
//或者
//flags= flags|O_NONBLOCK;
fcntl(fd[0],F_SETFL,flags);

8.查看管道缓冲区大小
命令
ulimit -a
【还可查看最大可支持的文件描述符的个数、栈空间的大小】

函数
fpathconf
【可查看很多属性:Linux下路径最长可写多长、文件描述符有多少个字符】

long size = fpathconf(fd[0],_PC_PIPE_BUF);

二、fifo

first in first out
在这里插入图片描述

1.特点

  • 有名管道
  • 在磁盘上有这样一个文件 ls -l ->p
  • 伪文件,在磁盘大小永远为0
  • 在内核中有一个对应的缓冲区
  • 半双工的通信方式

2.使用场景

没有血缘关系的进程间通信

3.创建方式

命令:mkfifo 管道名
在这里插入图片描述
函数:mkfifo

4.fifo文件可以使用IO函数进行操作

  • open/close
  • read/write
  • 不能执行lseek操作

5.进程间通信
a.fifo文件 — myfifo

  • 两个不相干的进程 A(a.c) B(b.c)
  • a.c —> read
    int fd = open("myfifo",O_RDONLY);
     read(fd,buf,sizeof(buf));
     close(fd);

b.b.c — write

int fd1 = open("myfifo",O_WRONLY);
write(fd1,"hello,world",11);
close(fd1);

三、内存映射区

在这里插入图片描述
1.mmap - 创建内存映射

作用:将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。

函数原型:

void *mmap(
	void* adrr,			//映射区首地址,传NULL,内核
	
	size_t length,  	//映射区的大小
	//100byte -4k 都是4k的整数倍
	//不能为0
	//一般文件多大,length就指定多大
	
	int prot,			//映射区权限
	//此处的权限要大于等于fd【文件描述符,open】的权限!!!
	//PROT_READ -- 映射区必须要有读权限<重点>
	//PROT_WRITE 
	//PROT_READ | PROT_WRITE
	
	int flags,			//标志位参数
	//MAP_SHARED 修改了内存数据会同步到磁盘
	//MAP_PRIVATE 修改了内存数据不会同步到磁盘
	
	int fd,				//文件描述符
	//要映射的文件对应fd
	//通过 open() 得到对应的fd
	
	off_t offset		//映射文件的偏移量
	//作用:映射的时候后文件指针的偏移量
	//必须是4k的整数倍
	//通常为0
);

返回值:
映射区的首地址 – 调用成功
调用失败:MAP_FAILED

munmap - 释放内存映射区

函数原型:int munmap(void *addr,size_t length);

addr - - mmap的返回值,映射区的首地址
length - - mmap的第二个参数,映射区的长度

问题:

  • 如果对mmap的返回值(ptr)做++操作(ptr++),munmap是否能够成功?
    不能。

  • 如果open时O_RDONLY,mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
    mmap调用失败
    open文件指定的权限应该大于等于mmap第三个参数prot指定的权限

  • 如果文件偏移量为1000会怎样?
    必须是4096(4k)的整数倍

  • 如果不检测mmap的返回值会怎样?
    无法知道错误情况

  • mmap什么情况下会调用失败?
    第二个参数length=0
    第三个参数必须指定PROT_READ
    fd对应的打开权限必须大于等于prot权限
    偏移量:必须时4096的整数倍

  • 可以open的时候指定O_CREAT一个新的文件来创建映射区吗?(就是创建一个映射区对应的文件不存在,先用O_CREAT指定open创建出来)

    可以,需要做文件拓展
    【文件拓展】
    lseek
    truncate(path,length)

  • mmap后关闭文件描述符,对mmap映射有没有影响?【close(fd),然后打印返回的首地址的取值,验证该问题】
    没有影响,只要映射区创建成功之后,就可以读取数据

  • 对ptr越界操作会怎样?

猜你喜欢

转载自blog.csdn.net/CarmenIsOK/article/details/89607828