一、基础概念:
linux下进程间通信:也叫做 IPC
常见的IPC方法:
1.管道:有名管道(用于非血缘关系进程)和匿名管道;
2.信号:
3.共享内存:也叫共享映射,用于非血缘关系进程
4.消息队列
5.套接字
早期进程间通信还会使用文件,虽然每个进程地址空间独立,但是1G大小的内核空间是共享的,所以两个进程共享一个文件,就达到了进程通信的目的。
为什么要进程间通信:
因为每个进程都有自己独立的地址空间,进程中的数据都是进程私有的。
二、管道的概念:
1.管道的特质:
1.本质上是一个伪文件(实际上是内核的一小块缓冲区),利用缓冲区实现的伪文件
2.由2个文件描述符引用这个伪文件,一个表示读端,一个表示写端
3.规定,数据从写端流入,从读端流出
所以说,管道的同步是自带的,文件在磁盘上上存在,只不过数据是在内存上
所以查看管道文件的大为0
2.什么是伪文件:
linux下的文件种类:
占用磁盘空间:-文件 d目录 l符号链接
不占磁盘空间:s套接字 b块设备 c字符设备 p管道
不占用磁盘空间的文件,但是在磁盘上有记录 就叫做伪文件
3.管道实现原理:
内核使用一个环形队列机制实现充当一个缓冲区,默认大小为4K
king@ubuntu:~$ ulimit -al
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7703
max locked memory (kbytes, -l) 16384
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8 //8个扇区,一个扇区512B
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7703
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
既然是一个队列,就有队列的特点:FIFO,现进先出
4.使用管道的注意事项(局限性):
1.数据自己读不能自己写
2.数据一旦被读走,数据便不在管道中存在,不可反复读取
3.由于管道采用半双工的通信方式,数据只能在一个方向上流动
4.管道只能在公共祖先的进程间使用管道,也就是只能在本机上通信
5.通信方式:
单工通信:不管什么时刻,只能固定一头读,固定一头写
半双工通信:要么一头读,一头写,不能一头又读又写
全双工通信:两头既可以同时读写
三、管道的使用:
1.无名管道(匿名管道):只能用于父子进程之间
#include<unistd.h>
int pipe(int pipefd[2]);
fd[0]:读端
fd[1]:写端
返回值成功返回0 失败返回-1
实现一个管道,完成父子进程间通信
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int fd[2]; //定义一个文件描述符数组,保存管道两端的文件描述符
int ret = pipe(fd);
if( ret == -1 )
{
perror();
}
pid_t pid = fork();
}
。。。。。。。。。
剩下的代码我先不写,我们来观察一个现象,我们通过pipe()创建了一个管道,并且得到了两个文件描述符,fd[0]和fd[1],当然相当于父进程中两个,子进程中也有两个。
所以需要进行以下的操作: 我们把程序补充完整:
保证管道的单向性,写端关闭读端,读端关闭写端。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int fd[2]; //定义一个文件描述符数组,保存管道两端的文件描述符
int ret = pipe(fd);
if( ret == -1 )
{
perror("pipe failed");
}
pid_t pid = fork();
if( pid == 0 ) //子进程读数据,所以关闭写端
{
close(fd[1]);
char buff[1024] = {0};
ret = read(fd[0],buff,sizeof(buff));
write(1,buff,ret);
}
else if(pid > 0)//父进程进行写数据,所以关闭读端
{
close(fd[0]);
write(fd[1],"hello world\n",strlen("hello world\n"));
}
return 0;
}
执行结果:
hello world
读时注意事项:
1.管道有数据
read返回实际读到的数据个数
2.管道无数据
判断写端是否关闭,read返回0
写端开着,认为不久的将来会给管道中写数据,所以读端一直阻塞着
写时注意事项:
1.读端全关闭
进程异常终止,SIGPIPE信号
2.有读端打开
管道未满,写数据,write返回写入字节数
管道写满,阻塞,直到有空间腾出来
2.命名管道:fifo 无血缘关系的进程间可以使用
命名管道的读端:
命名管道的写端:
这个代码是以前写的,需要注意的是,命名管道创建好以后,进程之间通信时要使用同一个目录下的fifo文件,否则无法实现。也支持多读端多写端