- 进程间通信方式
- 管道
- 共享内存
- 消息队列
- 信号量
- 本地套接字等等都能作为我们进程间通信的方法
- 操作系统提供进程间通信方式的原因
因为对于我们进程来说,每一个进程都是相互独立的,具有独立性,如果我们需要两个不同的进程相互通信,就需要我们操作系统提供一个公共的媒介,这个媒介就是进程间通信方式 - 进程间通信是干什么的?
通过进程间通信我们可以对不同进程进行数据传输,数据共享,进程控制,时间通知,不同的进程间通信方式不同我们的功能也有所差异
管道
- 管道的本质
管道其实就是内核中的一块缓冲区,我们通过对这块缓冲区的操作来达到我们的进程间通信的目的
对缓冲区的操作其实就是对我们的I/O操作,但我们的管道是双向选择半双工方式工作,因为在同一个时刻我们只能对我们的一端只进行读或者写操作,不能对我们的一端又读又写。我们管道又分为匿名管道和有名管道。
匿名管道
匿名管道只能用于有血缘关系的进程中的通信,因为匿名管道创建缓冲区的时候没有名字,对于非血缘的进程找不到这款缓冲区,所有我们就不能再非血缘进程中进行同行。
#include <unistd.h>
int pipe(int pipefd[2]);
pipefd是传入参数,得到的是我们操作的读写文件描述符,
返回值:成功返回0,失败返回-1
pipe()是在内核中建立一块缓冲区并且利用传入参数返回这个缓冲区的文件描述符。
例子:使用匿名管道实现ps | grep命令
1. #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <errno.h>
int main()
7 {
8 int fd[2];
9
10 int ret = pipe(fd);
11 if (ret < 0) {
12 perror("pipe error");
13 return -1;
14 }
15 int pid1 = fork();
16 if (pid1 == 0) {
17 //child1-- ps -ef
18 close(fd[0]);
19 dup2(fd[1], 1);
20 execlp("ps", "ps", "-ef", NULL);
21 exit(0);
22 }
23 int pid2 = fork();
24 if (pid2 == 0) {
25 //child2-- grep pipe
26 close(fd[1]);
27 dup2(fd[0], 0);
28 execlp("grep", "grep", "bash", NULL);
29 exit(0);
30 }
31 close(fd[0]);
32 close(fd[1]);
33 waitpid(pid1, NULL, 0);
34 printf("child1 exit--------\n");
35 waitpid(pid2, NULL, 0);
36 printf("child2 exit--------\n");
37 return 0;
38 }
exit(0);
22 }
23 int pid2 = fork();
24 if (pid2 == 0) {
25 //child2-- grep pipe
26 close(fd[1]);
27 dup2(fd[0], 0);
28 execlp("grep", "grep", "bash", NULL);
29 exit(0);
30 }
31 close(fd[0]);
32 close(fd[1]);
33 waitpid(pid1, NULL, 0);
34 printf("child1 exit--------\n");
35 waitpid(pid2, NULL, 0);
36 printf("child2 exit--------\n");
37 return 0;
38 }
有名管道
为我们的管道创建一个管道文件,这个管道文件就是管道的名字,因为有了名字,所以在我们的有名管道中我们可以在任意的进程间都可以实现我们的进程间通信。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
pathname:管道文件名称
mode:管道文件的权限
其实原理还是和匿名管道是一样的,在内核中创建一块缓冲区使得我们操作缓冲区的时候我们可以进行进程间的通信,但是此时的缓冲区变得有名字了,这时候不再是只有血缘关系的进程才能通信,现在是所有的进程都能进行通信,只要能找到我们对应的缓冲区文件fifio.
实现我们有名管道进程间的通信
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main(){
char* filename = "pipe.fifo";
int ret = mkfifo(filename,664);
if(ret == -1){
if(errno != EEXIST){
perror("mkfifo error\n");
return -1;
}
}
//创建fifo成功
int fd = open(filename,O_WRONLY);
if(fd == -1){
perror("open error\n");
return -1;
}
printf("打开文件成功\n");
while(1){
char buf[1024];
scanf("%s",buf);
write(fd,buf,strlen(buf));
}
close(fd);
return 0;
}
此时我们想我们的缓冲区中写入数据,
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int main(){
char * filename = "./pipe.fifo";
int ret = mkfifo(filename,664);
if(ret == -1){
if(errno != EEXIST){
perror("mkfifo error\n");
return -1;
}
}
printf("打开fifo文件成功\n");
int fd = open(filename,O_RDONLY);
if(fd == -1){
perror("read error\n");
return -1;
}
while(1){
sleep(1);
char* buf[1024];
ret = read(fd,buf,sizeof(buf));
if(ret == 0){
printf("写入端已经关闭\n");
exit(0);
}
if(ret == -1){
perror("read error\n");
return -1;
}
printf("buf:[%s\n]",buf);
}
close(fd);
return 0;
}
读取我们write进程写入到我们的缓冲区的内容。
管道特性
对于我们的有名管道,如果管道文件没有被写的方式打开,则以只读的方式打开会阻塞;如果没有被读的方式打开,则以只写打开会阻塞
对于匿名管道来说只有读写特性,有名管道的读写特性和匿名管道是一样的。
- 管道中没有数据的时候,read()会阻塞,直到读取到数据。
- 管道中若数据满了,write会阻塞知道有空闲空间
- 所有读端被关闭,write会触发异常
- 所有写端被关闭,read读完数据不会阻塞,而是返回0
- 匿名管道只能用于血缘关系的进程通信,有名管道可以用于任意的进程通信
- 管道读写特性+有名管道的打开特性
- 管道是半双工通信,但是双向选择
- 管道的生命周期随着进程
- 管道提供流式服务
- 管道自带同步与互斥功能(体现在没有数据则读会阻塞,放了数据之后唤醒我们的对象阻塞,对方读数据(同步);数据往管道中没放完别人不能去操作(互斥))
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)