每个进程各自有不同的地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程间要交换数据必须通过内核,在内核中开辟 一块缓冲区,进程1把数据从 用户空间拷到内核缓 冲区,进程2再从内核缓冲区把数据读 走,内核提供的这种机制称为进程间通信。
管道(匿名管道)是一种最基本的IPC机制,由pipe函数创建。
1. 父进程调 用pipe开辟管道,得到两个文件描述符指向管道的两端。
2. 父进程调 用fork创建 子进程,那么子进程也有两个文件描述符指向同 一管道。
3. 父进程关闭管道读端, 子进程关闭管道写端。父进程可以往管道写, 子进程可以管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
匿名管道实现进程间通信有以下四种情况:
1.指向管道写端的文件描述符未关闭,持有管道写端的进程也未向管道中写入数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才会读取数据并返回。
代码:
结果:
2.如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0), 而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到 文件末尾 一样。
代码:
结果:
3.所有指向管道读端的文件描述符都关闭了,只是有进程向管道的写端write,那么该进程会受到信号SIGPIPE,通常会导致进程异常终止。
代码:
结果:
4.指向管道读端的文件描述符未关闭,而持有管道读端的进程也没有从管道中读取数据,这时有进程向管道写端写数据,那么当管道被写满时,再次write会阻塞,直到管道中有空位置了才再写入数据并返回。(类似情况1)
源代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
int main()
{
int mypipefd[2];
int fd = pipe(mypipefd); //创建管道
if(fd < 0) //管道创建失败
{
perror("pipe");
return 2;
}
pid_t id = fork(); //创建子进程
if(id < 0)
{
perror("fork");
return 2;
}
else if(id == 0)
{
close(mypipefd[0]); //关闭子进程的读功能
int i = 0;
char *msg = NULL;
while(i < 10)
{
msg = "hello bit";
write(mypipefd[1],msg,strlen(msg)+1); //子进程写
sleep(1);
i++;
}
close(mypipefd[1]); //发送完毕子进程关闭写功能
exit(1);
}
else
{
close(mypipefd[1]); //父进程关闭写功能
char _msg[128];
int j = 0;
while(j < 10)
{
ssize_t _s = read(mypipefd[0],_msg,sizeof(_msg)-1);//父进程读
if(_s > 0)
{
_msg[_s] = '\0';
printf("child->father: %s\n",_msg);
}
else if(_s == 0) //读到了文件结尾(指向管道写端的文件描述符关闭)
//仍然有进程从管道的读端读数据,管道中剩余数据被读完则返回0
{
printf("pipe write is close\n");
break;
}
j++;
}
close(mypipefd[0]); //读完毕父进程关闭读功能
int status = 0;
pid_t ret = waitpid(id,&status,0);
if(ret < 0)
{
perror("wait");
}
else
{
printf("exit code: %d, signal: %d\n",(status>>8)&0xff, status&0xff);
}
}
}