linux进程间通讯-匿名管道

1、什么是匿名管道

  管道是一种两个进程间进行单向通信的机制,常说的管道多是指无名管道,无名管道具有以下两个特点:

  1. 数据只能由一个进程流向另一个进程(其中一个读管道,一个写管道),如果要进行双工通信,需要建立两个管道。
  2. 匿名管道没有名字,因此,只能用于父子进程或者兄弟进程间通信,也就是说匿名管道只能用于具有亲缘关系的进程间通信。

  在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,一是这个文件的内容只存在内存中,二是管道克服了使用文件进行通信的两个问题:

  1. 限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。
  2. 读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。

  注意:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

2、匿名管道的使用

1. 使用pipe()函数

  如果说popen是一个高级的函数,pipe则是一个底层的调用。与popen函数不同的是,它在两个进程之间传递数据不需要启动一个shell来解释请求命令,同时它还提供对读写数据的更多的控制。其使用方法如下:

int fd1[2];
if(pipe(fd1)) { 
    printf("pipe() FAILED: errno=%d\n", errno);
    return -1;
}

  使用pipe()创建管道后就可以作为一般的文件来使用,所以对一般文件的操作函数如read(),write()等都适用于管道。

  注意:
  1. 通过管道通信的两个进程,一个进程向管道写数据,另外一个从中读数据。写入的数据每次都添加到管道缓冲区的末尾,读数据的时候都是从缓冲区的头部读出数据的。
  2. 匿名管道两端分别用描述符pipefd[0]及pipefd[1]来描述。管道两端的任务是固定的,一端只能用于读,由描述符pipefd[0]表示,称其为管道读端;另一端只能用于写,由描述符pipe[1]来表示,称其为管道写端。
  3. 该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信。

2. 使用popen()函数

popen函数原型:
FILE* popen (const char *command, const char *open_mode);

与popen函数相对应的函数是pclose函数,其原型如下:
int pclose(FILE *stream);

函数说明:
  popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command是要运行的程序名和相应的参数。open_mode只能是”r(只读)”和”w(只写)”的其中之一。注意,popen函数的返回值是一个FILE类型的指针,而Linux把一切都视为文件,也就是说我们可以使用stdio I/O库中的文件处理函数来对其进行操作。
  如果open_mode是”r”,主调用程序就可以使用被调用程序的输出,通过函数返回的FILE指针,就可以能过stdio函数(如fread)来读取程序的输出;如果open_mode是”w”,主调用程序就可以向被调用程序发送数据,即通过stdio函数(如fwrite)向被调用程序写数据,而被调用程序就可以在自己的标准输入中读取这些数据。
  pclose函数用于关闭由popen创建出的关联文件流。pclose只在popen启动的进程结束后才返回,如果调用pclose时被调用进程仍在运行,pclose调用将等待该进程结束。它返回关闭的文件流所在进程的退出码。

优缺点:
  当请求popen调用运行一个程序时,它首先启动shell,即系统中的sh命令,然后将command字符串作为一个参数传递给它。这样就带来了一个优点和一个缺点。
  优点是:在Linux中所有的参数扩展都是由shell来完成的。所以在启动程序(command中的命令程序)之前先启动shell来分析命令字符串,也就可以使各种shell扩展(如通配符)在程序启动之前就全部完成,这样我们就可以通过popen启动非常复杂的shell命令。
  缺点是:对于每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell,即每一个popen调用将启动两个进程,从效率和资源的角度看,popen函数的调用比正常方式要慢一些。

3、匿名管道的缺点

  1. 通讯的进程必须是有亲缘关系的进程。
  2. 匿名管道所传输的是无格式的字节流,这就需要管道输入方和输出方事先约定好数据格式。
  3. 管道的缓冲区大小是受限制的。LINUX专门为每个管道所使用的内核级缓冲区确切为 4096 字节。除非阅读器清空管道,否则一次超过 4K 的写操作将被阻塞。

4、使用例子

1. pipe()使用例子

#include<stdio.h>
#include<unistd.h>

int main()
{
    int n,fd[2];                         //这里的fd是文件描述符的数组,用于创建管道做准备的
    pid_t pid;
    char line[100] = {0};

    if(pipe(fd) < 0) {                   //创建管道
        printf("pipe create error/n");
    }

    if((pid = fork()) < 0) {             //利用fork()创建新进程
        printf("fork error/n");
    } else if(pid > 0) {                 //这里是父进程,先关闭管道的读出端,然后在管道的写端写入“hello world"
        close(fd[0]);
        write(fd[1], "hello word/n", 11);
    } else {
        close(fd[1]);                    //这里是子进程,先关闭管道的写入端,然后在管道的读出端读出数据
        n = read(fd[0], line, 100);
        printf("read data:%s\n", line);
    }

    exit(0);
}

2. popen()使用例子

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  

#define BUFSIZ 50

int main()  
{  
    FILE *read_fp = NULL;  
    FILE *write_fp = NULL;  
    char buffer[BUFSIZ + 1];  
    int chars_read = 0;  

    //初始化缓冲区  
    memset(buffer, '\0', sizeof(buffer));  

    //打开ls和grep进程  
    read_fp = popen("ls -l", "r");  
    write_fp = popen("grep rwxrwxr-x", "w");  

    //两个进程都打开成功  
    if(read_fp && write_fp) {  
        //读取一个数据块  
        chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);  
        while(chars_read > 0) {  
            buffer[chars_read] = '\0';  
            //把数据写入grep进程  
            fwrite(buffer, sizeof(char), chars_read, write_fp);  
            //还有数据可读,循环读取数据,直到读完所有数据  
            chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);  
        }  
        //关闭文件流  
        pclose(read_fp);  
        pclose(write_fp);  
        exit(EXIT_SUCCESS);  
    }  
    exit(EXIT_FAILURE);  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值