open管道

匿名管道(pipe)


管道是一种最基本的IPC机制,由pipe函数创建:

#include < unistd.h >int pipe(int filedes[2]);

调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。

开辟了管道之后如何实现两个进程间的通信?如下:

 
 

1. 父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。

2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

3. 父进程关闭管道读端,?子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

    • 使用管道有一些限制: 
      1、两个进程通过一个管道只能实现单向通信。 
      2、管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承管道文件描述符。
    • 使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

      1、如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程 从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
      2、如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。 
      3、如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。 
      4、 如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回

1. 从管道中读取数据

1. 如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0
2. 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数 

2. 向管道中写入数据

1. 向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞
2. 只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)

3. 用于shell

管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入,当在某个shell程序(Bourne shell或C shell等)键入who │ wc -l后,相应shell程序将创建who以及wc两个进程和这两个进程间的管道。考虑下面的命令行

kill -l | grep SIGRTMIN
30) SIGPWR    31) SIGSYS    32) SIGRTMIN    33) SIGRTMIN+1
34) SIGRTMIN+2    35) SIGRTMIN+3    36) SIGRTMIN+4    37) SIGRTMIN+5
38) SIGRTMIN+6    39) SIGRTMIN+7    40) SIGRTMIN+8    41) SIGRTMIN+9
42) SIGRTMIN+10    43) SIGRTMIN+11    44) SIGRTMIN+12    45) SIGRTMIN+13
46) SIGRTMIN+14    47) SIGRTMIN+15    48) SIGRTMAX-15    49) SIGRTMAX-14

4. 用于具有亲缘关系的进程间通信

管道实现细节

在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的

有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作

管道的局限性

管道的主要局限性正体现在它的特点上

1. 只支持单向数据流
2. 只能用于具有亲缘关系的进程之间
3. 没有名字
4. 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小)
5. 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等 

实例:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "config.h"
#include "unpipc.h"
#include "my_err.h"

/*
struct ipc_perm
{
    uid_t uid;//owner user id
    gid_t gid;//owner group id
    uid_t cuid;//creater user id
    gid_t cgid;//creater group id
    mode_t mode;//weite or reads
    ulong_t seq;//
    key_t key;
};*/

//get Posix IPC name
char *px_ipc_name(const char* name)
{
    char *dir,*dst,*slash;
    if((dst=malloc(PATH_MAX))==NULL)//
      return NULL;
    if((dir=getenv("PX_IPC_NAME"))==NULL)
    {
        #ifdef POSIX_IPC_PREFIX
            dir=POSIX_IPC_PREFIX //from config.h
        #else
                dir="/tmp/";
        #endif
    }
    slash=(dir[strlen(dir)-1]==‘/‘)?"":"/";
    snprintf(dst,PATH_MAX,"%s%s%s",dir,slash,name);
    return dst;
}

void client(int readfd,int writefd)
{
    size_t len, n;
    char buff[MAXLINE];

    fgets(buff,MAXLINE,stdin);
    len=strlen(buff);
    if(buff[len-1]==‘\n‘)
      len--;

    //write pathname to ipc channel
    write(writefd,buff,len);

    //read from ipc,write to standard output
    if((n=read(readfd,buff,MAXLINE))>0)//reads access from pipe
        write(STDOUT_FILENO,buff,n);//stdou_fileno from unistd.h
}

void server(int readfd,int writefd)
{
    int fd;
    size_t n;
    char buff[MAXLINE+1];

    //read pthname from ipc channel
    if((n=read(readfd,buff,MAXLINE))==0)
        err_quit("end-of-file while reading pathname");
    buff[n]=‘\0‘;

    if((fd=open(buff,O_RDONLY))<0)
    {
        snprintf(buff+n,sizeof(buff)-n,"can‘t open,%s\n",strerror(errno));
        n=strlen(buff);
        write(writefd,buff,n);//error must tell client
    }
    else
    {
        while((n=read(fd,buff,MAXLINE))>0)
          write(writefd,buff,n);
        close(fd);
    }
}

int main()
{
    //printf("%d:\n",PATH_MAX);
    int pipe1[2],pipe2[2];
    pid_t childpid;
    pipe(pipe1);
    pipe(pipe2);
    if((childpid=fork())==0)
    {
        close(pipe1[1]);//children close pipe1 write
        close(pipe2[0]);//children close pipe2 reads
        server(pipe1[0],pipe2[1]);
        exit(0);
    }
    close(pipe1[0]);//father close pipe1 reads
    close(pipe2[1]);//fathre close pipe2 write
    client(pipe2[0],pipe1[1]);
    waitpid(childpid,NULL,0);
    return 0;
}

原文地址:https://www.cnblogs.com/tianzeng/p/9353840.html

时间: 2024-10-13 19:30:58

open管道的相关文章

Linux基础入门--IO重定向及管道

IO重定向及管道 一直都提到,程序:指令+数据 其实程序也有IO,数据的来源有多个地方:文件.外部 可用于输入的设备:文件(linux一切皆文件) 键盘设备.文件系统上的常规文件加载内容.网卡等: 可用于输出的设备:文件(linux一切皆文件) 显示器.文件系统上的常规文件输出保存.网卡等: 程序的数据流有三种: 输入的数据流:<-- 标准输入(stdin),是键盘: 输出的数据流:--> 标准输出(stdout),显示器: 错误输出流:  --> 错误输出(stderr),显示器: f

Java NIO (五) 管道 (Pipe)

Java NIO 管道是2个线程之间的单向数据连接.Pipe有一个source通道和一个sink通道.数据会被写到sink通道,从source通道读取. 如下图: 向管道写数据: 从管道读数据: 1. 从读取管道的数据,需要访问source通道. 2. 调用source通道的read()方法来读取数据

004-用户、组、权限管理命令及bash配置文件、管道、重定向、grep

l  1.用户管理命令 1.)创建用户命令 useradd  [OPTIONS] USERNAME -u(UID):指定用户UID -g(GID):指定基本组 -G( group,...):指定附加组 -c"COMMENT":指定注释信息 -d(home_dir):指定家目录/path/to/directory -s:指定SHELL路径(/etc/shells指定了当前系统可用的安全shell) -m –k:强行为用户创建家目录 -M:不创建用户家目录 /etc/login.defs

探秘linux-文件管理(inode理解)及管道和IO重定向

一.文件管理 1.Linux系统上各主要目录的简介 / 根,所有文件的起点 bin 存放操作系统启动时的引导程序,以及操作系统内核文件 boot 存放操作系统启动时的引导程序,以及操作系统内核文件 dev 存放设备文件和特殊文件(如字符设备) etc 存放配置文件的目录 home 普通用户的家目录默认都在此目录下 lib 存放系统库和内核模块文件 (/lib/modules) lib64 存放x86_64位系统上共享库文件 media 系统上提供的设备挂载点 misc 系统上提供的设备挂载点 m

通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成,前者负责监听请求并将接收的请求传递给给HttpApplication对象处理,后者则将请求处理任务委托给注册的中间件来完成.中间件的注册是通过ApplicationBuilder对象来完成的,所以我们先来了解一下这究竟是个怎样的对象.[本文已经同步到<ASP.NET Core框架揭秘>之中] [

标准的I/O和管道使用方法

标准的I/O和管道 程序:指令+数据 系统自带了许多二进制程序,这些二进制程序共有两部分组成,一部分是指令,一部分是数据. 指令就是指二进制程序中的内容,然而程序的运行,也需要数据来支撑. 标准输出  STDOUT  -1   standard  output 输入命令所呈现在屏幕上的内容称之为标准输出,但是不是所有的命令都有标准输出.例如cd  rm后面不跟参数时,就没有标准输出. 标准输出的默认设备就是当前终端窗口. 标准错误  STDERR  -2  standard  error 当命令

linux中的管道

管道是一种最基本的IPC机制,由pipe函数创建: #include <unistd.h> int pipe(int filedes[2]); 调用pipe函数就是在内核区开辟一块缓冲区(称为管道).filedes[0]指向管道的读端,filedes[1]指向管道的写端.管道实际上就是一个打开的文件.pipe函数成功返回0,失败返回-1. 如何用管道实现两个进程间的通信? 1.父进程调用pipe函数开辟管道,得到两个文件描述符指向管道的两端. 2.父进程调用fork()创建子进程,那么子进程也

管道及其容量

1.管道 管道是一种最基本的IPC机制,由pipe函数创建: #include <unistd.h> int pipe(int filedes[2]); 调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端.所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件

Linux下的进程通信方式(IPC)——管道通信

Unix IPC: 管道.命名管道(FIFO)      管道 1.概念 管道是单向的(半双工).先进先出.无结构的字节流,它把一个进程的输出和另一个进程的输入连接在一起. 写进程在管道的尾端写入数据,读进程在管道的首端读出数据.数据读出后将从管道中移走,其它读进程都不能再读到这些数据. 管道提供了简单的流控制机制.进程试图读一个空管道时,在数据写入管道前,进程将一直阻塞.同样,管道已经满时,进程再试图写管道,在其它进程从管道中读走数据之前,写进程将一直阻塞. 2.管道的特点 (1)单向数据通信

管道请求处理流程

创建一个“迷你版”的管道来模拟真实管道请求处理流程 从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但是就具体的实现来说,由于其中涉及很多对象的交互,我想很少人能够地把它弄清楚.如果想非常深刻地认识ASP.NET Core的请求处理管道,可以分两个步骤来进行,我们首先可以在忽略细节的前提下搞清楚管道处理HTTP请求的总体流程,然后再此基础上补充之前遗漏的细节