> 文章列表 > 【Linux】System V IPC-匿名管道

【Linux】System V IPC-匿名管道

【Linux】System V IPC-匿名管道

管道&共享内存&消息队列

  • 进程间通信
  • 管道
    • 匿名管道
    • 匿名管道的创建
    • 匿名管道的特性
    • 匿名管道的阻塞属性

进程间通信

对于进程间通信的方式,大致可以分为四种:管道,共享内存,信号量,消息队列和网络

而前三种都是在同一台计算机当中的不同的进程之间进行通信的,而网络可以在计算机之间进行通信。
为什么要进行进程间通信呢?

对于同一台计算机而言,创建出来的多个进程,它们之间要进行通信,例如当前计算机运行着服务端的进程,我们需要对这个服务进程进行守护,回顾守护进程的方式,其中有很重要的一点就是,服务端进程需要每秒将时间发送给守护进程,当守护进程发现时间没有被修改时,就需要先杀死当前进程,然后创建子进程,利用进程程序替换将子进程替换为被守护进程的代码。

其中关键的一个环节就是,如何让守护进程读到当前进程发送的时间信息。
由于各个进程是相互独立的,它们的数据也是独立的,那么两个进程需要通信就不容易实现了,这里实际上就应用到了进程间通信的方式。

管道

管道实际上就是内核中的一块缓冲区,这块缓冲区是内核维护的,它的生命周期跟随进程,由进程创建,当进程结束时,这块缓冲区也被释放了。

而管道可以被分为两种,匿名管道和命名管道。

匿名管道

匿名管道顾名思义,它是没有名字的,意味着进程是无法通过名字将其找到

匿名管道的特点是需要有亲缘性的进程之间才能进行通信,例如:父子进程,祖孙进程,兄弟进程等
满足了亲缘性之后,以父子进程举例,还需要有这样的顺序
1.父进程先创建管道
2.父进程再创建子进程

匿名管道的创建

如果创建管道和创建子进程的顺序写错了,那么,父子进程通过管道打开的文件描述符指向的空间是不同的
【Linux】System V IPC-匿名管道

那么如果创建顺序是对的,就会指向相同的一块空间
【Linux】System V IPC-匿名管道
只有父子进程同时指向同一个管道的读写两端,才能实现匿名管道的通信。

究其原因:由于父进程创建子进程时,子进程会拷贝父进程的PCB,如果父进程先进行了管道的创建,那么子进程同样也会拷贝管道的地址,也就是拷贝父进程中的管道文件描述符,使得父子进程的管道文件描述符指向的是同一个管道。如果父进程先创建子进程,然后创建管道,那么子进程拷贝父进程的PCB时,父进程并没有管道文件描述符,因此,子进程创建的管道文件描述符和父进程的就不同了。

匿名管道的写法是int pipe(int pipefd[2]);

其中pipefd是由pipe函数给其赋值的,
pipefd[0]是读端,pipefd[1]是写端,
返回值为-1,创建失败,返回0创建成功

注意,其中的pipefd[0]和pipefd[1]本质上都是文件描述符。

我们可以通过/proc/进程号/fd 查看当前进程打开了几个文件

int fd[2];
int res = pipe(fd);
if(res < 0)
{perror("pipe");exit(-1);
}
//为了防止进程退出,从而导致匿名管道的销毁,需要使用sleep让进程停下来
while(1)
{sleep(1);
}

将上述进程运行起来,查看其进程号,使用/proc/进程号/fd 查看当前打开的文件描述符
【Linux】System V IPC-匿名管道

0, 1 , 2 我们知道是标准输入,标准输出,标准错误,它们是随着进程一起被创建的,而3和4实际上就是通过pipe函数打开的管道的读写两端。
红色区域实际上是在闪烁,而闪烁的意思是当前指向的空间是属于操作系统内核的空间。

当然闪烁还有一种情况,就是在使用软链接时,如果将当前文件删除,只留下软链接文件,那么软链接文件指向的区域空间已经被释放了

已经知道了匿名管道是内核的一块缓冲区,并且创建管道后会有两个文件描述符,那么

既要有这样的亲缘关系,并且有这样的创建管道和创建子进程的顺序吗,才能够成功使用管道来进行通信。

我们先写这样一个代码,父进程写管道,子进程读管道并且打印出来。
【Linux】System V IPC-匿名管道
运行结果
【Linux】System V IPC-匿名管道
可以看到,子进程确实从管道中读到父进程写入管道的内容。

匿名管道的特性

实际上,管道有一些特性

1.管道是半双工通信,数据只能从写端流向读端
2.没有标识符,只能在亲缘关系的进程间实现通信
3.管道的大小为64k
4.管道提供字节流服务,意味着前后两次写入的数据之间没有间隔,而在读时,是将数据直接取走,而非拷贝
5.pipe_size大小为4096字节,当单词读写的数据大小小于4096字节时,操作系统会保证其读写的原子性(读写被不间断全部执行)

匿名管道的阻塞属性

匿名管道创建文件描述符时,文件描述符的初始属性为阻塞属性

当write一直写,不去读时,管道写满后write会阻塞等待
当read一直读,不去写时,管道为空后read会阻塞等待

使用fcntl函数可以修改为非阻塞属性

设置方式:
1.通过F_GETFL获取文件描述符的属性信息
2.通过F_SETFL设置文件描述符的属性信息,原属性信息 | O_NONBLOCK

读端设置非阻塞 写端设置非阻塞
写端关闭:一直读,读端read返回0 读端关闭:进程异常终止,SIGPIPE,退出码为141
写端不关闭:一直读, 读端read返回-1 读端不关闭: 一直写,写端write返回-1

特别注意的就是:当写端为非阻塞,并且读端关闭时,管道会破裂,进程结束