每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到。所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2 在从内核缓冲区把数据读走。
管道是一种最基本的IPC机制,由pipe函数创建:
调用pippe函数时在内核中开辟一块缓冲去(称为管道)用于通信,它有一个读端,一个写端,然后通过fileds参数传出给用户程序两个文件描述符,fileds[0]指向管道的读端,fileds[1]指向管道的写端,所以管道在用户程序看起来就像一个打开的文件,通过read 或者write;向这个文件读写数据实际是在读写内核缓冲区。
管道是实现进程间的通信需要经过以下几步:
1).创建一个管道
2).创建两个进程
3).分别关闭一个进程的读端,另一个的写端
4).实现通信
程序具体如下:
(一)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int _pipe_fd[2]={-1,-1};
int _pipe=pipe(_pipe_fd);
if(_pipe<0){
perror("pipe");
exit(1);
}
int _pid=fork();
if(_pid<0){
perror("fork");
exit(2);
}else if(_pid==0){//child
close(_pipe_fd[0]);
char buf[1024]="hello world\n";
fflush(stdout);
int count =10;
while(count--){
char buf[1024]="hello world\n";
}
sleep(5);
}
else{//father
close(_pipe_fd[1]);
char buf[1024];
while(1){
memset(buf,‘\0‘,sizeof(buf));
ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));
if(_size<0){
perror("read");
exit(3);
}else{
printf("%s",buf);
}
}
}
}
return 0;
}
运行结果为:
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
因为读端一直循环,一直在读,而写端并没有在写,所以读端最终会阻塞。
(二)如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端读数据,那么管道剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int _pipe_fd[2]={-1,-1};
int _pipe=pipe(_pipe_fd);
if(_pipe<0){
perror("pipe");
exit(1);
}
int _pid=fork();
if(_pid<0){
perror("fork");
exit(2);
}else if(_pid==0){//child
close(_pipe_fd[0]);
//char buf[1024]="hello world\n";
//fflush(stdout);
int count =10;
while(count--){
char buf[1024]="hello world\n";
write(_pipe_fd[1],buf,strlen(buf));
sleep(2);
}
close(_pipe_fd[1]);
}
else{//father
close(_pipe_fd[1]);
char buf[1024];
int j=0;
while(j++ < 15){
memset(buf,‘\0‘,sizeof(buf));
ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));
if(_size<0){
perror("read");
exit(3);
}else{
printf("%s",buf);
}
}
int status=0;
if(waitpid(_pid, &status,0)==_pid){
printf("wait sucess,sigcode\n:%d",status & 0xFF);
break;
}
}
return 0;
}
程序运行结果为:
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
wait sucess,sigcode:0
(三)
如果有指向管道写端的文件描述符没关闭,而持有管道写端的进程也没有向管道写数据,这时有进程从管道读端读数据,那么管道中剩余的数据被读取后,再次read会被阻塞,直到管道中有数据可读了才读取数据并返回。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int _pipe_fd[2]={-1,-1};
int _pipe=pipe(_pipe_fd);
if(_pipe<0){
perror("pipe");
exit(1);
}
int _pid=fork();
if(_pid<0){
perror("fork");
exit(2);
}else if(_pid==0){//child
close(_pipe_fd[0]);
int count =10;
while(count--){
char buf[1024]="hello world\n";
fflush(stdout);
if(count>5)
{
write(_pipe_fd[1],buf,strlen(buf));
}
sleep(2);
}
close(_pipe_fd[1]);
}
else{//father
close(_pipe_fd[1]);
char buf[1024];
int j=0;
while(j++ < 15){
memset(buf,‘\0‘,sizeof(buf));
ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));
if(_size<0){
perror("read");
exit(3);
}else{
printf("%s",buf);
}
}
int status=0;
if(waitpid(_pid, &status,0)== _pid){
printf("wait sucess,sigcode:%d\n",status & 0xFF);
}
}
return 0;
}
程序运行结果:
hello world
hello world
hello world
hello world
wait sucess,sigcode:0
(四)如果所有指向管道读端的文件描述符都关闭了,这时有进程向管道的写端write ,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int _pipe_fd[2]={-1,-1};
int _pipe=pipe(_pipe_fd);
if(_pipe<0){
perror("pipe");
exit(1);
}
int _pid=fork();
if(_pid<0){
perror("fork");
exit(2);
}else if(_pid==0){//child
close(_pipe_fd[0]);
int count =10;
while(count--){
char buf[1024]="hello world\n";
fflush(stdout);
if(count>5)
{
write(_pipe_fd[1],buf,strlen(buf));
}
sleep(2);
}
}
else{//father
close(_pipe_fd[1]);
char buf[1024];
int j=0;
while(j++ < 3){
memset(buf,‘\0‘,sizeof(buf));
ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));
if(_size<0){
perror("read");
exit(3);
}else{
printf("%s",buf);
}
}
close(_pipe_fd[0]);
sleep(10);
int status=0;
if(waitpid(_pid, &status,0)== _pid){
printf("wait sucess,sigcode:%d\n",status & 0xFF);
}
}
return 0;
}
程序运行结果:
hello world
hello world
hello world
wait sucess,sigcode:13
(五)如果有指向管道读端的文件描述符没关闭,而持有管道读端的进程也没有向管道读数据,这时有进程从管道写端写数据,那么管道被写满后,再次write会被阻塞,直到管道中有空位置了才写入并返回。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int _pipe_fd[2]={-1,-1};
int _pipe=pipe(_pipe_fd);
if(_pipe<0){
perror("pipe");
exit(1);
}
int _pid=fork();
if(_pid<0){
perror("fork");
exit(2);
}else if(_pid==0){//child
close(_pipe_fd[0]);
int count =10;
while(1){
char buf[1024]="hello world\n";
fflush(stdout);
write(_pipe_fd[1],buf,strlen(buf));
sleep(2);
}
}
else{//father
close(_pipe_fd[1]);
char buf[1024]
int status=0;
if(waitpid(_pid, &status,0)== _pid){
printf("wait sucess,sigcode:%d\n",status & 0xFF);
}
sleep(10);
}
return 0;
}
程序运行结果便是一直被阻塞。
由此可见管道的特点为:
1):主要用于父子进程间的通信,或者是有血缘关系的进程。
2):是单向通信的,且内部有保护机制。
3):其生命周期随进程。