代码改变世界

Linux进程间通信之管道

2015-08-04 09:36  dream_er  阅读(351)  评论(0编辑  收藏  举报

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操作同样适用,如readwrite等。

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.有名管道的创建

用于创建管道的函数有两个分别为:mknodmkfifo,这两个函数原型分别为;

#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|0666dev指的是设备值,该值取决创建的文件的种

类,只有在创建设备文件时才用得到。函数调用成功反或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,然后就可以互相通信了。

效果图: