1.为什么要进行进程间的通信呢?通信的目的
1.
实现进程间的数据传递
2.
实现进程间的数据共享
3.
实现进程间的事件的通知等等
2、Linux中进程间通信的发展
3、Linux中进程间通信的方式
早期的进程间通信方式:
•
有名管道、无名管道、信号
----------
同一台主机上进程间的通信
System V IPC
对象
•
共享内存、消息队列、信号量
------
同一台主机上进程间的通信
BSD
:
socket
------
不同主机上进程间的通信
(
网络编程中要研究
)
3-1、无名管道
3-1-1、特点
《
1
》只能用于具有亲缘关系的进程间的通信 : 父子进程、兄弟进程
《
2
》半双工的通信模式,具有固定的读端和写端, 一端进行读操作,一端进行写操作
《
3
》管道可以看成是一种特殊的文件,对于它的读写可以使用文件
IO
如
read
、
write
、
close
函数。
注意: 无名管道不属于任何文件系统,存在于内核中
《
4
》无名管道在应用层角度来说,是有
2
个文件描述符来标识他,
fd
[
0
]
表示读端
fd
[
1
]
表示写端
管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符
fd
[
0
]
和
fd
[
1
]
。其
中
fd
[
0
]
固定用于读管道,而
fd
[
1
]
固定用于写管道
![](https://i-blog.csdnimg.cn/direct/7e334de58bcc4e8cafb46e7c5a1f97bc.png)
3-1-2、创建无名管道
#include <unistd.h>
int
pipe
(
int
fd
[
2
])
/*
参数
1
:保存
2
个文件描述符的数组
返回值:成功
0
, 失败
-1
*/
close
(
fd
);
//
关闭
3-1-3、使用--亲缘关系进程间的通信
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
//创建管道
int fd[2]= {0};
int res = pipe(fd);
if(res<0)
{
perror("pipe error");
return -1;
}
//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid ==0) //子进程 fd[0] fd[1]
{
//关闭读端
close(fd[0]);
char buf_write[256] = {0};
//给管道中写数据
fgets(buf_write, sizeof(buf_write), stdin);
write(fd[1], buf_write, sizeof(buf_write));
close(fd[1]);
}
else if(pid>0) //父进程 fd[0] fd[1]
{
//关闭写端
close(fd[1]);
char buf_read[256] = {0};
//从管道中读取数据
read(fd[0], buf_read, sizeof(buf_read));
printf("read data = %s\n", buf_read);
//关闭读端
close(fd[0]);
}
return 0;
}
3-1-4、测试一下无名管道的容量: 64KB
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd[2] = {0};
int res = pipe(fd);
if(res<0)
{
perror("pipe error");
return -1;
}
//给管道中写数据
char buf[1024]={0};
int count = 1;
while(1)
{
write(fd[1], buf, 1024);
printf("写入了%dKB数据\n", count);
count++;
}
close(fd[0]);
close(fd[1]);
return 0;
}
3-1-5、无名管道使用时需要注意的点
当管道中无数据时,读操作会阻塞
向管道中写入数据时,
linux
将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入
数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的
SIGPIPE
信号
(
通常
Broken pipe
错误
)
。
在系统中是不允许程序向没有读端的管道中写入数据! 不然会造成管道破裂!
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
//创建管道
int fd[2]= {0};
int res = pipe(fd);
if(res<0)
{
perror("pipe error");
return -1;
}
//关闭读端
close(fd[0]);
//创建子进程
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid ==0) //子进程 fd[0] fd[1]
3-2、有名管道
3-2-1、为什么会引入有名管道呢
{
//关闭读端
close(fd[0]);
char buf_write[256] = {0};
//给管道中写数据
fgets(buf_write, sizeof(buf_write), stdin);
write(fd[1], buf_write, sizeof(buf_write));
close(fd[1]);
exit(0);
}
else if(pid>0) //父进程 fd[0] fd[1]
{
int status=0;
wait(&status);
if(WIFEXITED(status))
{
printf("子进程正常退出\n");
}
else if(WIFSIGNALED(status))
{
printf("子进程是通信信号退出,信号的值为%d\n", WTERMSIG(status));
}
//关闭写端
close(fd[1]);
char buf_read[256] = {0};
//从管道中读取数据
read(fd[0], buf_read, sizeof(buf_read));
printf("read data = %s\n", buf_read);
//关闭读端
close(fd[0]);
}
return 0;
}
3-2、有名管道
对无名管道的优化,无名管道只实现有亲缘关系的通信,没有亲缘则需要有名通道
3-2-2、特点
《
1
》无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围
《
2
》有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见
《
3
》进程通过文件
IO
来操作有名管道,
open\read\write\close
《
4
》有名管道遵循先进先出规则, 从头开始读,写入的追加在尾巴处!
《
5
》不支持如
lseek
()
操作
《
6
》有名管道本质上就是一个文件
3-2-3、创建有名管道
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename,mode_t mode);
/*
参数1: 有名管道的名称
参数2:权限 8进制的数表示 0777 0664
返回值: 成功0 失败 -1
*/
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
int main()
{
int res = mkfifo("./fifo_1", 0777);
if(res <0)
{
perror("mkfifo error");
return -1;
}
printf("mkfifo ok\n");
return 0;
}
3-2-4、使用有名管道实现一下非亲缘关系的进程间的通信
//写数据
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
//创建有名管道
int res = mkfifo("./fifo_2", 0664);
if(res<0 && errno!=EEXIST)//保证管道存在的情况下不会报错!
{
perror("mkfifo error");
return -1;
}
//打开有名管道文件
int fd = open("./fifo_2", O_WRONLY);
if(fd<0)
{
perror("open error");
return -1;
}
//写入数据
char buf_write[256] = {0};
while(1)
{
memset(buf_write, 0, sizeof(buf_write));
fgets(buf_write, sizeof(buf_write), stdin);
write(fd, buf_write, sizeof(buf_write));
}
close(fd);
return 0;
}
//文件2
//读数据
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
//创建有名管道
int res = mkfifo("./fifo_2", 0664);
if(res<0 && errno!=EEXIST)
{
perror("mkfifo error");
return -1;
}
//打开有名管道文件
int fd = open("./fifo_2", O_RDONLY);
if(fd<0)
{
perror("open error");
return -1;
}
//读数据
char buf_read[256] = {0};
while(1)
{
memset(buf_read, 0, sizeof(buf_read));
read(fd, buf_read, sizeof(buf_read));
printf("read data =%s\n", buf_read);
}
close(fd);
return 0;
}