进程间通信 管道和FIFO

进程间通信 管道和FIFO

管道,顾名思义就是像水管一样,水管是用来流通水的,而对与管道来说,做为进程间通信的一种方式,传送的是数据而已。对于管道来说,没有名字,在程序内部以标识符进行识别,这也就注定了管道只能用于有亲缘关系的各个进程间使用。但是这一缺点可以通过FIFO,又程为有名管道来弥补,先介绍一下管道的基本函数。

#include<unistd.h>

intpipe(int fd[2]);

pipe函数用于管道的创建,函数返回两个文件描述符,fd[0]用来打开读和fd[1]用来打开写,管道是单个进程创建的,但是很少在单个进程之间使用,大部分的使用例子如下,父进程创建了一个管道,通过fork调用创建了子进程,子进程拥有父进程打开管道的描述符,然后双方一方用于从管道中读出数据,另一方用于从管道中写入数据,以此来进行进程之间的通信。

下面写一个程序,完成如下功能,客户服务程序从标准输入读取文件路径名,然后通过管道传送给服务器,然后服务器调用系统函数读取文件内容,然后通过管道返回给客户端,客户端收到内容以后输出到标准输出或者标准错误上去,由于双方都需要读写,所以需要使用双管道进行通信

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
    int fone[2],ftwo[2];//双管道,描述符
    void server(int,int);//服务器程序
    void client(int,int);//客户端程序
    pid_t childfd;
    pipe(fone);
    pipe(ftwo);//创建管道
    if((childfd=fork())<0)
	perror("fail to fork!");
    if(childfd==0){
	close(fone[1]);
	close(ftwo[0]);
	server(fone[0],ftwo[1]);//调用服务器程序
	exit(0);
    }
    close(fone[0]);
    close(ftwo[1]);
    client(fone[1],ftwo[0]);//调用客户端程序
    waitpid(childfd,NULL,0);
    return 0;
} 
void server(int readfd,int writefd)
{
    char buf[1024];
    int fd,n,len;
    if((n=read(readfd,buf,1024))==0)//读出文件路径
    {
	sprintf(buf,"can not recive the massage %s\n",strerror(errno));
	n=strlen(buf);
   	write(writefd,buf,n);
    }
    if((fd=open(buf,O_RDONLY))<0)//打开文件
    {
	sprintf(buf+n,"sorry can not open :%s",strerror(errno));
	n=strlen(buf);
    	write(writefd,buf,n);
    }
    else
    {
	printf("server:runing\n");
	while((n=read(fd,buf,1024))>0)//读文件
	   write(writefd,buf,n);//写入管道
    	close(fd);
    }
}
void client(int writefd,int readfd)
{
     char buf[1025];
     int len,n;
     fgets(buf,1024,stdin);//从标准输入读取路径
     printf("filename is :%s",buf);
     len=strlen(buf);
     if(buf[len-1]=='\n')
      buf[len-1]='\0';
     write(writefd,buf,len);//写入管道
     printf("client runing!\n");
     while((n=read(readfd,buf,1024))>0)//从管道读出数据
	write(1,buf,n);//输出到标准输出
}



标准I/O函数库提供了另外函数对管道进行操作
#include<stdio.h>
FILE *popen(const char *commandconst char *type);
int pclose(FILE *stream);
popen创建了一个管道,并启动另外一个进程,commandshell命令,如果typer,那么调用进程读command的标准输出,如果typew,调用进程往command写。这个函数使用起来还是比较方便的,个人觉得比exec函数更加容易控制,不过对于popen函数而言,只能重定向标准输入和标准输入,如果是标准出错的话,还是会输出到程序本书的标准出错中去的。

管道没有名字,因此只能用于有亲缘关系的进程之间使用,FIFO类似与管道,先进先出,单项数据流,与管道不同的就是能够实现无亲缘关系之间通信。
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
mkfifo用于管道的创建 pathnameunix路径名,mode用于指定权限位,定义在sys/stat.h中,隐含指定了O_CREAT|O_EXCL如果没有的话创建,如过存在的话,返回错误。
我们可以用管道重新写上面的客户服务器模式,只需要把创建两个管道,换成创建两个fifo就行了,fifopathname为参数名创建以后打开进行通信。
关于管道和FIFO阻塞的问题。如果一个函数读取一个阻塞的管道或者FIFO的话,如果该管道里面没有数据,那么读数据挂起,一直到管道内部有数据为止,如果设置成非阻塞的话,那么没有数据的话读取返回一个错误。FIFO的阻塞标志可以在open的时候指定。
open(fifo,O_WRONLY|O_NONBLOCK,0)
O_NONBLOCK非阻塞标志设置,可以进行与或;对于管道来说,没有名字也就没有办法打开,可以用fcntl来获取和设置
flag=fcntl(fd,F_GETFL,0); 用来获取标志位 与或以后用 fcntl(fd,F_SETFL,flag);进行设置。
下面就写一个无亲源关系的客户服务器程序,服务器以公共的名字创建FIFO,客户以一定的规则加上自己的pid创建自己的FIFO。客户向服务器发送自己pid和文件名,服务器返回文件的内容到客户的有名管道中,供客户读出。

服务器端程序:
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<errno.h>
#define flag O_CREAT|O_EXCL
#define mode S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
int main()
{
    int fdread,fdwrite,fdfile;
    long  len,pid,usrfd;
    char buff[1024],usrfile[1024],filename[1024];
    if(mkfifo("/tmp/server.fifo",mode)<0){
	perror("server:fail to mkfifo server.fifo");
	exit(1);
    }
    fdread=open("/tmp/server.fifo",O_RDONLY);
    fdwrite=open("/tmp/server.fifo",O_WRONLY);
    read(fdread,buff,1024);
    sscanf(buff,"%ld %s",&pid,filename);
    sprintf(usrfile,"/tmp/fifo.client.%ld",(long)pid);
    if((usrfd=open(usrfile,O_WRONLY))<0){
	perror("server:fail to open the client");
	exit(1);
    }
    if((fdfile=open(filename,O_RDONLY))<0){
	perror("server:fail to open the file ");
	exit(1);
    }
    while((len=read(fdfile,buff,1024))>0)
	write(usrfd,buff,len);
    close(usrfd);
    close(fdfile);
    return 0;
}

客户端程序:
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>
#define flag O_CREAT|O_EXCL
#define mode S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
int main()
{
    pid_t pid;
    char buf[1024],filename[1024];
    char clientfifo[1024];
    int  fdserver,fdclient;
    int  len;
    pid=getpid();
    scanf("%s",filename);
    sprintf(buf,"%ld %s",(long)pid,filename);
    if((fdserver=open("/tmp/server.fifo",O_WRONLY))<0){
	perror("client:fail to open the server.fifo");
	exit(1);
    }
    len=strlen(buf);
    write(fdserver,buf,len);
    sprintf(buf,"/tmp/fifo.client.%ld",(long)pid);
    if(mkfifo(buf,mode)<0){
	perror("client:fail to mkfifo the");
	exit(1);
    }
    if((fdclient=open(buf,O_RDONLY))<0){
	perror("client:fail to open client fifo");
	exit(1);
    }
    while((len=read(fdclient,buf,1024))>0)
 	write(1,buf,len);
    close(fdserver);
    close(fdclient);
    return 0;
}


可以实现单服务器,多用户模式,服务器在程序后台运行,前台向服务器提交请求,服务器端进行回应。系统对于管道和FIFO有如下限制,OPEN_MAX一个进程在任意时刻可以打开的最大描述符个数,PIPR_BUF可原子的往一个管道或者fifo的最大数据量关于这两个限制,可以通过sysconf函数查看,用过ulimit命令和limit命令进行修改。其中很多规则制约这fifo和管道的是否阻塞问题和原子操作问题,关于这方面的讨论,才疏学浅,不便多说了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值