我们知道进程间是存在独立性的,有自己的独立地址空间,所以说将进程通信起来首先要让他们看到一份公共资源,才有可能进行通信。
1、进程间通信的目的
传输数据
资源共享
通知事件
进程控制
2、进程间通信的分类(常把进程间通信叫做IPC)
管道
消息队列
共享内存
信号量
1、理解管道
管道式Unix中最古老的通信形式。
管道是一个进程连接到另一个进程的数据流
管道分为匿名管道和命名管道,所以就分开来看:
(左边是子进程,右边是父进程)将父进程的读写操作打开,通过父进程创建子进程,子进程的读写操作也打开了,父进程写文件子进程就不能写文件所以,关闭父进程的读端,关闭子进程的写端,就能实现对同一个文件读、写操作,此时文件就起到一个传输作用,将父进程的输入作为子进程的输出。
2、具体实现代码
介绍创建管道的函数
#include
int pipe(int fd[2]); fd:文件描述符操作,fd[0]表示读端。fd[1]表示写端
1 #include
2 #include
3 #include
4 #include
5 #include
6 int main(){
7 int fd[2];
8 int ret=pipe(fd);
9 if(ret<0){
10 //失败的原因1、内存不足2、文件描述符达到上限
11 perror("pipe error\n");
12 return 1;
13 }
14
15 pid_t pid=fork();
16 if(pid==0){
17 //child
18 close(fd[0]);
19 const char* msg="hello!i am child\n";
20 while(1){
21 sleep(1);
22 write(fd[1],msg,strlen(msg));
23 }
24 }else if(pid>0){
25 //father
26 char buf[1024]={0};
27 while(1){
28 ssize_t read_size=read(fd[0],buf,sizeof(buf)-1);
29 if(read_size<0){
31 perror("read error\n");
32 }
33 printf("receive:%s\n",buf);
34 }
35 }
36 return 0;
37 }
通过结果我们可以看到,父进程每隔一秒输出一句receive:hello!i am child。程序中控制的是子进程每隔一秒往管道中写一条消息,所以管道具有同步机制
管道读写规则
1、当没有数据可读时:
O_NONBLOCK disable:read调用阻塞,直到有人写数据
O_NONBLOCK enablle:调用返回-1,errno是EAGAIN
2、当管道满了的时候:
O_NONBLOCK disable:write调用阻塞,直到有人读走数据
O_NONBLOCK enablle:调用返回-1,errno是EAGAIN
3、如果管道写端对应文件描述符被关闭,则read返回0;
在这里我们让read返回等于0时打印pipe done
4、如果管道读端对应的文件描述符被关闭,write操作会产生信号,使得write进程退出
5、当要写入的数据量不大于PIPE_BUF时,系统将保证写入的原子性
6、当要写入的数据量大于PIPE_BUF时,系统将不保证写入的原子性
参考代码:
1 #include
2 #include
3 #include
4 #include
5 #include
6 int main(){
7 int fd[2];
8 int ret=pipe(fd);
9 if(ret<0){
10
11 perror("pipe error\n");
12 return 1;
13 }
14
15 pid_t pid=fork();
16 if(pid==0){
17 //child->w
18 close(fd[0]);
19 int count;
20 const char* msg="hello!i am child\n";
21 while(1){
22 write(fd[1],msg,strlen(msg));
23 printf("count:%d\n",count++);
24 sleep(1);
25 if(count>10){
26 close(fd[1]);
27 break;
28 }
29 }
30 }else if(pid>0){
31 //father->r
32 close(fd[1]);
33 char buf[1024]={0};
34 while(1){
35 ssize_t read_size=read(fd[0],buf,sizeof(buf)-1);
36 if(read_size>0){
37 printf("receive:%s\n",buf);
38 }else if(read_size==0){
39 printf("pipe done\n");
40 break;
41 }
42 }
43 }
44 return 0;
匿名管道的特点
1、只能用于具有亲缘关系的进程。
2、管道是半双工的,只能向一个方向流动; 如果需要双向通信则需要两个管道。
3、生命周期随进程(进程退出文件关闭,管道也是文件)。
4、内核对管道操作进行同步与互斥(写完了就不写了,读完了就不读了)。
当我们只往管道中写数据但是不去读就会发现管道写满后子进程就会卡住count值不变。这就验证了他们的互斥机制。
当我们控制了父进程写一个信息后睡眠十秒,而子进程一直在读,但是实际上子进程将父进程写入的信息读完就要阻塞式等待,这就验证了他们的同步机制。
5、管道面向字节流(假如发送10k大小的文件,可以一次读1k,也可以一次读10k)。
这分别是两个进程,通过函数创建管道后,一个往管道中写入数据,一个则执行后就会显示,与匿名管道不同的是,此函数创建管道后会产生文件。
例题1:利用管道模拟实现服务器-客户端之间通信
代码:
serverpipe.c文件:
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8
9 int main(){
10 if(mkfifo("mypipe",0644)<0){
11 perror(" mkfifo error\n");
12 }
13 int rfd=open("mypipe",O_RDONLY);
14 if(rfd<0){
15 perror("open error\n");
16 }
17 char buf[1024]={0};
18 while(1){
19 printf("please wait...\n");
20 ssize_t read_size=read(rfd,buf,sizeof(buf)-1);
21 if(read_size>0){
22 printf("client say:%s\n",buf);
23 }else if(read_size==0){
24 printf("client quit,exit \n");
25 exit(EXIT_SUCCESS);
26 }
27 else{
28 perror("read\n");
29 }
30 }
31 close(rfd);
32 return 0;
33 }
clientpipe.c文件:
2 #include
3 #include
4 #include
5
6 int main(){
7 int wfd=open("mypipe",O_WRONLY);
8 if(wfd<0){
9 perror("open error\n");
10 }
11 char buf[1024]={0};
12 while(1){
13 printf("please enter:");
14 fflush(stdout);
15 while(read(0,buf,sizeof(buf)-1)>0){
16 write(wfd,buf,strlen(buf));
17 }
18 perror("read error");
19 }
20 close(wfd);
21 return 0;
22 }
例2:用命名管道实现文件拷贝
file-pipe.c文件:
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 int main(){
10 if(mkfifo("pipe",0644)<0){
11 perror("mkfifo error\n");
12 return 1;
13 }
14 int infd=open("abc",O_RDONLY|O_CREAT);
15 if(infd<0){
16 exit(1);
17 }
18 int outfd=open("pipe",O_WRONLY);
19 char buf[1024]={0};
20 int read_size;
21 while((read_size=read(infd,buf,sizeof(buf)-1))>0){
22 write(outfd,buf,read_size);
23 }
24 close(infd);
25 close(outfd);
26 return 0;
27 }
pipe-file.c文件:
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7
8 int main(){
9 int outfd=open("abc.bak",O_WRONLY|O_CREAT|O_TRUNC,0644);
10 if(outfd<0){
11 perror("open error\n");
12 return 1;
13 }
14 int infd=open("pipe",O_RDONLY);
15 if(infd<0){
16 perror("open error\n");
17 return 2;
18 }
19 char buf[1024]={0};
20 int read_size=0;
21 while(( read_size=read(infd,buf,sizeof(buf)))>0){
22 write(outfd,buf,read_size);
23 }
24 close(infd);
25 close(outfd);
26 unlink("pipe");
27 return 0;
28 }