【Linux】进程间通信 - 管道

       进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。IPC接口就提供了这种可能性。每个IPC方法均有它自己的优点和局限性,一般,对于单个程序而言使用所有的IPC方法是不常见的。

进程间通信目的:

    1.数据传输:一个进程需要将它的数据发送给另一个进程
    2.资源共享:多个进程之间共享同一份资源
    3.通知事件:一个进程需要向另一个或一组进程发送消息,通知他们发生了某种事件
    4.进程控制:有些进程希望完全控制另一给进程的执行(如Debug进程),此时控制希望能够拦截另一个进程的所有陷入和异常,并能够即使知道它的状态改变。

管道:

管道它是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。我们都知道在Linux下“一切皆文件”,其实这里的管道就是一个文件。管道实现进程通信就是让两个进程都能访问该文件。管道分为匿名管道和命名管道。

【匿名管道】

#include<unistd.h>  
功能:创建一个无名管道  
原型  
  int pipe(int fd[2]);  
参数  
  fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端  
返回值:成功返回0,失败返回错误代码

管道的特征: 
1、只提供单向通信,也就是说,两个进程都能访问这个文件,假设进程1往文件内写东西,那么进程2 就只能读取文件的内容。 
2、只能用于具有血缘关系的进程间通信,通常用于父子进程建通信 。
3、管道是基于字节流来通信的 。
4、依赖于文件系统,它的生命周期随进程的结束结束(随进程)。 

5、其本身自带同步互斥效果。

实例:从键盘读取数据,写入管道,读取管道,写到屏幕

#include<stdio.h>    
#include<stdlib.h>    
#include<unistd.h>    
#include<string.h>    
    
int main(void)    
{    
    int fds[2];    
    char buf[100];    
    int len;    
    if (pipe(fds) == -1)    
        perror("make pipe"), exit(1);    
    //读取数据    
    while (fgets(buf, 100, stdin)){    
        len = strlen(buf);    
    //写入管道    
        if (write(fds[1], buf, len) != len){    
            perror("write to pipe");    
            break;    
        }    
        memset(buf, 0x00, sizeof(buf));    
    //读取管道    
        if ((len = read(fds[0], buf, 100)) == -1){    
            perror("read from pipe");    
            break;    
        }    
    //写到屏幕    
        if (write(1, buf, len) != len){    
            perror("write to stdout");    
            break;    
        }    
    }    
}  

管道的读写规则:

    1、写端的文件描述符关闭,读端一直读。

    2、写端的文件描述符没关闭,但写端也不写数据,而读端一直读。

    3、写端一直写数据,读端不读,也不关闭它的文件描述符。

    4、写端写数据,而读端的文件描述符关闭。

【命名管道】(named pipe 或 FIFO

 管道应用的一个限制就是只能在具有相同祖先(具有亲缘关系)的进程间通信。 

 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

1、与匿名管道的区别:提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信。 
2、 FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。 
3、 FIFO(first input first output)总是遵循先进先出的原则,即第一个进来的数据会第一个被读走。

【命名管道的创建】:

1.可在命令行上创建: 
$ mkfifo filename

2.在程序里创建: 
int mkfifo (const char* filename , mode_t mode); 

注释:filename 为管道名称;mode 为权限

实例:用命名管道实现client/server通信:

首先创建Makefile文件:

.PHONY:all  
all:client server  
client:client.c  
    gcc -o $@ $^  
server:server.c  
    gcc -o $@ $^  
  
.PHONY:clean  
clean:  
    rm -f client server  

client.c

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <string.h>  
  
  
int main()  
{  
    int wfd = 0;  
    if((wfd = open("mypipe",O_WRONLY))==-1)//以只写方式打开管道  
    {  
        perror("open");  
        return 1;  
    }  
    char buf[1024];  
    while(1)  
    {  
        printf("Please Enter:");  
        //从标准输入读取数据  
        fflush(stdout);  
        size_t s = read(0,buf,sizeof(buf)-1);  
        if(s>0)  
        {  
            buf[s]=0;  
            write(wfd,buf,strlen(buf));//将读取到的数据写进管道  
        }  
        else if(s==-1)  
        {  
            perror("write");  
            return 2;  
        }  
    }  
    return 0;  
} 

server.c

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <string.h>  
  
  
int main()  
{  
    if(mkfifo("mypipe",0644)==-1)//创建命名管道  
    {  
        perror("mkfifo");  
        return 1;  
    }  
    int rfd = 0;  
    if((rfd = open("mypipe",O_RDONLY))==-1)//以只读方式打开管道  
    {  
        perror("open");  
        return 2;  
    }  
    char buf[1024];  
    while(1)  
    {  
        ssize_t s = read(rfd,buf,sizeof(buf)-1);//从管道中读数据  
        if(s>0)  
        {  
            buf[s-1]=0;  
            printf("client say:%s\n",buf);  
        }  
        else if(s==0)  
        {  
            printf("client quit!!!\n");  
            break;  
        }  
        else  
        {  
            perror("read");  
            return 3;  
        }  
    }  
    return 0;  
}  

输入make编译代码,然后输入./server ,这时就会创建一个管道,然后再打开一个终端,在这个新的终端上输入./client,就可以进行通信。如下图所示:


猜你喜欢

转载自blog.csdn.net/yaotengjian/article/details/79881433