Linux进程间通信之管道
1.管道的分类:
按有无名称,管道主要分为有名管道和匿名管道
2.匿名管道
管道是一种进程之间进行单向通信的方式,由于其通信只是单方向的,所以起有以下缺点:
1.通信只是单方向的,通信太局限
2.其缓冲区大小是一定的,缓冲区满了之后就无法继续再写入数据。
3.通过管道传输的只能是无格式的字节流。
4.只能用于具有亲缘关系的进程之间,如父子进程,兄弟进程。
3.匿名管道的创建
管道创建使用函数pipe,其原型为:
#include<unistd.h>
intpipe(int pipefd[2]);
函数参数pipefd[2]为整形的数组,可作为一般的文件描述符使用,管道读端用pipefd[0]表示,管道写端用pipefd[1]表示,创建成功,则返回0,失败返回-1,管道创建后,就可以
作为一般的文件对待,对一般的I/O操作同样适用,如read,write等。
4.匿名管道读写数据
请看示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
void write_pipe(int fd) //管道读端
{
char *message="hello pipe test\n";
if(write(fd,message,strlen(message)+1)) //向管道读端写入数据,使用write就可以
printf("write success\n");
}
void read_pipe(int fd) //管道写端
{
char *message;
message=(char *)malloc(100*sizeof(char));
if(read(fd,message,100)) //从管道读端读取数据
{
printf("get pipe:%s\n",message);
}
else
{
printf("get message fail\n");
}
free(message);
}
int main()
{
int fd[2]; //定义一个int型的二维数组,作为pipe函数的参数
int stat_val;
pid_t pid;
if(pipe(fd)) //创建管道
{
printf("make a pipe fail\n");
return 0;
}
pid=fork(); //创建子进程
switch(pid)
{
case 0: //子进程用于读
close(fd[1]);
read_pipe(fd[0]);
exit(0);
case -1:
printf("fork a new process fial");
exit(0);
default: //父进程用于写
close(fd[0]);
write_pipe(fd[1]);
wait(&stat_val);
exit(0);
}
return 1;
}
执行结果:
writesuccess
getpipe:hello pipe test
5.有名管道
顾名思义,有名管道就是拥有名字的管道,当然,他与匿名管道的区别就是他拥有了名字,与此同时,他也克服了匿名管道的部分缺陷与不足,如它可以在任意两个进程之间进
行通信,当然也只能局限于单向的。
6.有名管道的创建
用于创建管道的函数有两个分别为:mknod和mkfifo,这两个函数原型分别为;
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
intmknod(const char *pathname, mode_t mode, dev_t dev);
说明:pathname指的是创建的有名管道的全文件路径名,mode为创建的有名管道的模式,既存权限,一般都为S_IFIFO|0666,dev指的是设备值,该值取决创建的文件的种
类,只有在创建设备文件时才用得到。函数调用成功反或0失败返回-1
#include<sys/types.h>
#include<sys/stat.h>
intmkfifo(const char *pathname, mode_t mode);
说明:pathname指的是创建的有名管道的全文件路径名,mode为创建有名管道的模式,既存权限,一般都为S_IFIFO|0666
使用mknod创建有名管道:
umask(0);
if(mknod(“fifo”,S_IFIFO|0666))
{
perror(“mkfifo error!”);
exit(0);
}
使用mkfifo创建有名管道:
umask(0);
if(mkfifo(“fifo”,S_IFIFO|0666))
{
perror(“mkfifo error !”);
exit(0);
}
说明:创建管道时,要特别注意,pathname必须是你有读写权限的路径,因为管道实质上就是一些特殊的文件,管道创建时自动创建,结束后自动删除。还有关于mode,必
须在前面使用umask修饰,表面上权限是由mode决定的,但真正权限是mode&~umask得到的。还有,有名管道在进行读写时,得像文件一样先打开,读写完毕后关闭。
7.有名管道应用示例:
//processread.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/types.h>
int main()
{
char buf[1024];
int fd;
umask(0);
fd=open("myfifo",O_RDONLY); //打开名为myfile的管道名,路径为当前路径
read(fd,buf,1024); <span style="white-space:pre"> </span>//读取管道里的内容
printf("read :%s\n",buf); //输出管道里的内容
close(fd);
exit(0);
}
//processwrite.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
int fd;
char buf[40]="hello world,this message send by fifo !";
umask(0);
if(mkfifo( "myfifo" , S_IFIFO|read :hello world,this message send by fifo ! 0666)==-1) /*有名创建管道*/
{
printf("create new fifo fail!\n");
exit(1);
}
if((fd=open("myfifo",O_WRONLY))==-1) //先打开管道文件
{
printf("open fail !\n");
exit(1);
}
write(fd,buf,strlen(buf)+1); <span style="white-space:pre"> </span>//向管道里面写入信息
close(fd);
exit(0);
}
程序说明:
将两个文件分别编译后,先运行processwrite,这时候,读端由于对写端的依赖,次进程会阻塞在终端,打开另一终端,运行processread,神奇的结果会出现;
read:hello world,this message send by fifo !
8.有名管道综合应用:
许多人会想到,既然管道是单向的,那么两条管道岂不是就可以实现互相通信了嘛!是的,下面例子就是实现两个进程之间互相通信:
//service.c
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#define BUF_SIZE 1024
#define FIFO_WRITE "writefifo" //服务端写管道名
#define FIFO_READ "readfifo" //服务端读管道名
int main()
{
errno=0;
int wfd,rfd;
char buf[BUF_SIZE];
int len;
umask(0);
if(mkfifo(FIFO_WRITE,S_IFIFO|0666)) //创建一个有名管道
{
printf("Create %s fail because:%s",FIFO_WRITE,strerror(errno));
exit(1);
}
umask(0);
wfd=open(FIFO_WRITE,O_WRONLY); //打开管道写端
if(wfd==-1)
{
printf("Open %s errno,because :%s",FIFO_WRITE,strerror(errno));
exit(1);
}
while((rfd=open(FIFO_READ,O_RDONLY))==-1) //打开管道读端
{
sleep(1); //如果打开失败,沉睡一秒,重新读取
}
while(1)
{
printf("Service:");
fgets(buf,BUF_SIZE,stdin); //获取用户的输入
buf[strlen(buf)-1]='\0'; //字符串要以‘\0‘结尾
if(strncmp(buf,"quit",4)==0) //若字符串以quit开始,则退出
{
close(wfd);
unlink(FIFO_WRITE); //断开连接
close(rfd);
exit(0);
}
write(wfd,buf,strlen(buf)); //向通道中写入东西
len=read(rfd,buf,BUF_SIZE); //从通道中获取信息
if(len>0)
{
buf[len]='\0';
printf( "Client :%s\n",buf );
}
}
}
//client.c
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#define BUF_SIZE 1024
#define FIFO_READ "writefifo" /*客户端写管道名,这里要特别注意,宏定义名与实际有反差*/
#define FIFO_WRITE "readfifo" //客户端读管道名,这里也要注意
int main()
{
int rfd,wfd;
char buf[BUF_SIZE];
int len;
errno=0;
umask(0);
if( mkfifo( FIFO_WRITE, S_IFIFO|0666 ) ) //创建有名管道
{
printf("creat fifo %s fail,because:%s ",FIFO_WRITE, strerror(errno));
exit(1);
}
while((rfd=open(FIFO_READ,O_RDONLY))==-1) //打开管道读端
{
sleep(1);
}
wfd=open(FIFO_WRITE, O_WRONLY); //打开管道写端
if(wfd==-1)
{
printf("Fail to open %s ,because: %s",FIFO_WRITE,strerror(errno));
exit(-1);
}
while(1)
{
len=read(rfd,buf,BUF_SIZE); //读取管道里的信息
if(len>0)
{
buf[len]='\0';
printf("Service:%s\n",buf);
}
printf("Client :");
fgets(buf,BUF_SIZE,stdin);
buf[strlen(buf)-1]='\0';
if(strncmp(buf,"quit",4)==0)
{
close(wfd);
unlink(FIFO_WRITE); //断开连接
close(rfd); //关闭文件
exit(0);
}
write(wfd,buf,strlen(buf)); //写入数据
}
}
程序说明:
分别编译后,首先运行service,后打开另一个终端运行client,然后就可以互相通信了。
效果图: