进程间通信方式----管道通信
2017-12-07 20:46
239 查看
管道通信
介绍
一、管道的定义
管道(Pipe)及有名管道(Named Pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
二、管道的通信方式
管道的通信方式分为无名管道和有名管道,无名管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制。
三、管道的特点
管道是Linux支持的最初Unix IPC 形式之一,具有以下特点:1、管道是半双工的,数据只能向一个方向流动;需要双方通信,需要建立两个管道。
2、只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
3、单独构成一种独立的文件系统。
4、数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。
操作
一、建立无名管道
表头文件:#include<unistd.h>定义函数:int pipe(int filedes[2])
函数说明:pipe()会建立管道,并将文件描述符由参数filedes数组返回。filedes[0]为管道里的读取端, filedes[1]为管道的输入端。
返回值:成功返回0,否则-1;
例子:#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
int main()
{
pid_t pid;
int filedes[2];
char buffer[80];
pipe(filedes);
pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(pid > 0)
{
/*父进程*/
char s[]="hello!\n";
write(filedes[1],s,sizeof(s));
}
else
{
/*子进程*/
read(filedes[0],buffer,80);
printf("%s",buffer);
}
return 0;
}
执行:hello!
管道用于不同进程间通信。通常先创建一个管道,再通过 fork 函数创建一个子进程,该子进程会继承父进程所创建的管道。
注意:必须在系统调用 fork() 前调用 pipe () , 否则子进程将不会继承文件描述符。
二、读写无名管道
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
int main()
{
int pipe_fd[2];
pid_t pid;
char r_buf[100];
char w_buf[4];
char *p_wbuf;
int r_num;
int cmd;
memset(r_buf,0,sizeof(r_buf));
memset(w_buf,0,sizeof(w_buf));
p_wbuf = w_buf;
if(pipe(pipe_fd)<0)
{
printf("pipe create error!\n");
return -1;
}
if((pid = fork())==0)
{
printf("\n");
close(pipe_fd[1]);
sleep(3); //确保父进程关闭写端
r_num = read(pipe_fd[0],r_buf,100);
printf("read num is %d the data read from the pipe is %d\n",r_num,atoi(r_buf));
close(pipe_fd[0]);
exit(0);
}
else if(pid>0)
{
close(pipe_fd[0]); //read
strcpy(w_buf,"6666");
if(write(pipe_fd[1],w_buf,4)!=-1)
{
printf("parent write over!\n");
}
close(pipe_fd[1]);
printf("parent close fd[1] over!\n");
sleep(10);
执行:parent write over!
parent close fd[1] over!
read num is 4 the data read from the pipe is 6666
附加结论:管道写段关闭后,写入的数据将一直存在,知道读出为止
向管道中写入数据:向管道中写入数据时,Linux 将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走缓冲区中的数据,那么写操作将一直阻塞。
注意:只有在管道的读端存在时,向管道写入数据才有意义。
对管道的写规则的验证1:写端对读端存在的依赖性。
例子:#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int pipe_fd[2];
pid_t pid;
char r_buf[4];
char* w_buf;
int writenum;
int cmd;
memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0)
{
printf("pipe create error!\n");
return -1;
}
if((pid = fork())==0)
{
close(pipe_fd[0]);
close(pipe_fd[1]);
sleep(10);
exit(0);
}
else if(pid>0)
{
sleep(1);
close(pipe_fd[0]);
w_buf="111";
if((writenum = write(pipe_fd[1],w_buf,4))==-1)
printf("write to pipe error!\n");
else
printf("the bytes write to pipe is %d \n",writenum);
close(pipe_fd[1]);
}
return 0;
}
执行: broken pipe
原因:该管道及其所有 fork() 产物的读端已经被关闭。
对管道的写规则的验证2:Linux不保证写管道的原子性验证
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<errno.h>
#include<string.h>
int main(int argc,char **argv)
{
int pipe_fd[2];
pid_t pid;
char r_buf[4096];
char w_buf[4096*2];
int writenum;
int rnum;
memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0)
{
printf("pipe create error!\n");
return -1;
}
if((pid = fork())==0)
{
close(pipe_fd[1]);
while(1)
{
sleep(1);
rnum = read(pipe_fd[0],r_buf,1000);
printf("child:readnum is %d \n",rnum);
}
close(pipe_fd[0]);
exit(0);
}
else if(pid > 0)
{
close(pipe_fd[0]);
memset(r_buf,0,sizeof(r_buf));
if((writenum=write(pipe_fd[1],w_buf,1024))==-1)
printf("write to pipe error!\n");
else
printf("the bytes write to pipe is %d \n",writenum);
writenum=write(pipe_fd[1],w_buf,4096);
close(pipe_fd[1]);
}
return 0;
}
了解即可,运行后会陷入死循环(或在 while(1)处添加限制条件)
三、无名管道应用实例
1.实例一:用于shell管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入。
2.实例二:用于具有亲缘关系的进程间通信
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
int main()
{
int pipe_fd[2];
pid_t pid;
char r_buf[4];
char* w_buf[256];
int childexit=0;
int i;
int cmd;
memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0)
{
printf("create pipe error!\n");
return -1;
}
if((pid = fork())==0)
{
printf("\n");
close(pipe_fd[1]);
sleep(2);
while(!childexit)
{
read(pipe_fd[0],r_buf,4);
cmd = atoi(r_buf);
if(cmd == 0)
{
printf("child:receive command from parent over\nnow childprocess exit\n");
childexit=1;
}
else if(handle_cmd(cmd)!=0)
return ;
sleep(1);
}
close(pipe_fd[0]);
exit(0);
}
else if(pid > 0)
{
close(pipe_fd[0]);
w_buf[0]="003";
w_buf[1]="005";
w_buf[2]="777";
w_buf[3]="000";
for(i=0;i<4;i++)
write(pipe_fd[1],w_buf[i],4);
close(pipe_fd[1]);
}
return 0;
}
int handle_cmd(int cmd)
{
if((cmd<0)||(cmd)>256)
{
printf("child:invalid command\n");
return -1;
}
printf("child:th cmd from parent is %d\n",cmd);
return 0;
}
执行:
child:th cmd from parent is 3
child:th cmd from parent is 5
child:invalid command
创建有名管道
表头文件:#include <sys/types.h>#include <sys/stat.h>
定义函数:int mkfifo(const char *pathname, mode_t mode);
函数说明:mkfifo() 会依参数 pathname 建立特殊的 FIFO 文件,该文件必须不存在,而参数 mode为该文件的权限(mode%~umask),因此 umask 值也会影响到 FIFO 文件的权限。 mkfifo() 建立的 FIFO 文件其他进程都可以用读写一般文件的方式存取。当使用 open() 来打开 FIFO文件时,O_NONBLOCK 旗标会有影响;
(1)当使用O_NONBLOCK 旗标时,打开 FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开 FIFO 文件来读取,则写入的操作会返回 ENXIO 错误代码。
(2)没有使用O_NONBLOCK 旗标时,打开 FIFO 来读取的操作会等到其他进程 FIFO 文件来写入才正常返回。同样的,打开 FIFO 文件来写入的操作会等到其他进程打开 FIFO 文件来读取后才正常返回
例如:#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include <fcntl.h>
#define FIFO "/home/self/2"
int main()
{
char buffer[80];
int fd;
unlink(FIFO);
mkfifo(FIFO,0666);
if(fork()>0)
{
char s[]="hello!\n";
fd = open(FIFO,O_WRONLY);
write(fd,s,sizeof(s));
close(fd);
}
else
{
fd = open(FIFO,O_RDONLY);
read(fd,buffer,80);;
printf("%s",buffer);
close(fd);
}
return 0;
}
执行:hello!
读写有名管道
1、从 FIFO 中读取数据约定:如果一个进程为了从 FIFO 中读取数据而阻塞打开 FIFO ,那么称该进程内的读操作作为设置了阻塞标志的读操作。
(1)如果有进程写打开 FIFO,且当前 FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。
(2)对于设置了阻塞标志的读操作来说,造成阻塞的原因有两种:当前 FIFO 内有数据,但其他进程在读这些数据;另外就是 FIFO 内没有数据。
(3)读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其他将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO 中没有数据也一样(此时,读操作返回0);
(4)如果没有进程打开 FIFO,则设置阻塞标志的读操作会阻塞。
2、向 FIFO 中写入数据
(1)对于设置了阻塞标志的写操作:
1)当要写入的数据量不大于 PIPE_BUF 时,Linux将保证写入的原子性。
2)当要写入的数据量大于PIPE_BUF 时,Linux 将不再保证写入的原子性。
(2)对于没有设置阻塞标志的写操作:
1)当要写入的数据量大于 PIPE_BUF 时,Linux将不再保证写入的原子性。
2)当要写入的数据量不大于PIPE_BUF 时,Linux将保证写入的原子性。
3、对 FIFO 读写规则的验证
相关文章推荐
- 进程学习:进程间通信(传统通信方式)1.无名管道
- 线程间的通信、同步方式与进程间通信方式
- linux进程间通信方式之匿名管道http://blog.csdn.net/guoyang1007/article/details/4720984
- Linux 下进程间通信机制(一) 通信方式
- 进程间通信-管道通信
- linux下进程间通信方式之(1)-管道及fork()函数
- Linux进程间的通信方式之管道
- Linux进程间通信(三)---管道通信之有名管道及其基础实验
- Linux进程间通信方式之管道(pipe)
- 进程间通信的方式——信号、管道、消息队列、共享内存
- 线程间的通信、同步方式与进程间通信方式
- 进程间通信之命名管道FIFO通信
- 进程学习:进程间通信(传统通信方式)3.信号
- Linux进程间通信(二)---管道通信之无名管道及其基础实验
- Linux进程间通信(九)---综合实验之有名管道通信实验
- 进程间通信 3 -- 使用管道进行通信
- 进程间通信方式之管道--无名管道
- Linux编程练习 --进程间通信2--两个管道实现双向通信
- 1.6 进程间通信方式之有名管道
- 5、linux进程间通信之管道通信