管道是一种只允许用在有亲属关系的进程间通信的方式,由函数pipe创建一个管道,read,write进行读写操作。

       #include <unistd.h>

       int pipe(int pipefd[]);

参数pipefd[2]数组返回打开的读写描述符,pipefd[0]为读,pipefd[1]为写。

  第一个问题:文件描述符怎么会出现一个只能读,一个只能写呢?猜想是对一个文件打开了2次,一个以只读打开,一个以只写打开。使用fcntl来验证下:

       #include <unistd.h>
#include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
       F_GETFL (void)
Get the file access mode and the file status flags; arg is ignored.

cmd为F_GETFL时,最后一个参数arg被忽略。测试代码:

 #include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h> int main(void)
{
int flags;
int fd[]; if (pipe(fd) < )
{
perror("pipe error");
} flags = fcntl(fd[], F_GETFL,);
if ( flags < )
{
perror("fcntl");
close(fd[]);
close(fd[]);
}
switch (flags & O_ACCMODE)
{
case O_RDONLY:
printf("read only\n");
break; case O_WRONLY:
printf("write only\n");
break; case O_RDWR:
printf("read write\n");
break; default:
printf("unknown access mode\n");
} flags = fcntl(fd[], F_GETFL,);
if ( flags < )
{
perror("fcntl");
close(fd[]);
close(fd[]);
}
switch (flags & O_ACCMODE)
{
case O_RDONLY:
printf("read only\n");
break; case O_WRONLY:
printf("write only\n");
break; case O_RDWR:
printf("read write\n");
break; default:
printf("unknown access mode\n");
}
close(fd[]);
close(fd[]);
exit();
}

运行结果:

read only
write only

与猜想相符。

  数据的流向:

从图中可以看出,进程可以以pipefd[1]写完,然后以pipefd[0]读,自己写自己读,这条数据流是通的。 验证:

 #include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h> #define MAXLINE 4096
int main(void)
{
int flags;
int fd[], n;
char buf[MAXLINE];
if (pipe(fd) < )
{
perror("pipe error");
} n = write(fd[], "hello world\n", MAXLINE);
if ( n < )
{
perror("write");
goto end;
}
n = read(fd[],buf, n);
if ( n < )
{
perror("read");
goto end;
}
printf("read:%s\n",buf); end:
close(fd[]);
close(fd[]);
exit();
}

输出:

read:hello world

  既然是进程间通信,那么管道在同一个进程中读写基本是没什么意义的,管道常用的方式是,先创建一个管道,然后fork,父子进程就共享了这个管道了。数据流向如图:

这样,管道的写端有2个进程操作,读端有2个进程操作。但是这样一来就出现了一个问题,假设父进程读,那么这个数据是它自己写进去的呢?还是子进程写进去的?无法区分。通常一个进程关闭它的读,另一个进程关闭它的写,这样,数据流向就只有一个方向了,数据来自谁就显而易见了。如图:

测试代码:

 #include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> #define MAXLINE 4096 int main(void)
{
int n;
int fd[];
pid_t pid;
char line[MAXLINE]; if (pipe(fd) < )
perror("pipe error");
if ((pid = fork()) < )
{
perror("fork error");
}
else if (pid > ) /* parent */
{
close(fd[]);
write(fd[], "hello world\n", );
}
else /* child */
{
close(fd[]);
n = read(fd[], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
exit();
}

结果:

hello world

  读一个空的管道或者写一个满的管道都将导致阻塞,不过可以通过fcntlF_SETFL设置为O_NONBLOCK,从而不阻塞。

  当管道一端被关闭后,有下列2条规则:

  1.当读一个写端所有文件描述符引用都已被关闭的管道时,在所有数据被读完后,read将返回0。表示无数据可读。

  2.当写一个读端所有文件描述符引用都已被关闭的管道时,将产生SIGPIPE信号,write返回-1。

  

  混淆的东西,管道的容量和管道的缓冲区大小。

    管道的容量:指管道满时装的字节数,自2.6.11内核后,容量为64k。管道满了就会导致写操作产生阻塞。

    管道缓冲区大小:由PIPE_BUF指定,指的是保证管道写操作为原子操作的最大值,如果一次写入的内容超过这个值,那么这次的写操作就不是原子的。什么意思呢?就是指,可能存在多个进程写同一个管道,如果一次写入的字节数大于缓冲区大小,则可能会出现A进程写入的内容中插入了B进程写入的内容。

  下面是出现这种情况的代码:

 #include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h> #define MAXLINE 4096+100 int main(void)
{
int n;
int fd[];
pid_t pid;
char line[MAXLINE]; if (pipe(fd) < )
{
perror("pipe error");
} if ((pid = fork()) < )
{
perror("fork error");
}
else if (pid > ) /* parent */
{
close(fd[]);
while ( )
{
n = read(fd[], line, MAXLINE);
write(STDOUT_FILENO, line, n);
write(STDOUT_FILENO, "\n\n\n", );
}
}
else /* child */
{
if ((pid = fork()) < )
{
perror("fork error");
}
else if (pid > )
{
close(fd[]); while ()
{
memset(line, 'a',MAXLINE);
write(fd[], line, MAXLINE);
}
}
else
{
close(fd[]); while ( )
{
memset(line, 'b',MAXLINE);
write(fd[], line, MAXLINE);
}
}
} exit();
}

IPC之PIPE的更多相关文章

  1. Linux IPC BSD Pipe

    mkfifo() //创建有名管道(FIFO special file),创建完了就像普通文件一样open(),再读写,成功返回0,失败返回-1设errno.VS$man 3 mkfifo #incl ...

  2. Unix IPC之pipe

    pipe创建函数: #include <unistd.h> /* Create a one-way communication channel (pipe). If successful, ...

  3. linux IPC的PIPE

    一.PIPE(无名管道) 函数原型: #include <unistd.h> ]); 通常,进程会先调用pipe,接着调用fork,从而创建从父进程到子进程的IPC通道. 父进程和子进程之 ...

  4. CVE-2017-7494 Linux Samba named pipe file Open Vul Lead to DLL Execution

    catalogue . 漏洞复现 . 漏洞代码原理分析 . 漏洞利用前提 . 临时缓解 && 修复手段 1. 漏洞复现 . SMB登录上去 . 枚举共享目录,得到共享目录/文件列表,匿 ...

  5. 【APUE】Chapter16 Network IPC: Sockets & makefile写法学习

    16.1 Introduction Chapter15讲的是同一个machine之间不同进程的通信,这一章内容是不同machine之间通过network通信,切入点是socket. 16.2 Sock ...

  6. 高级IPC DBus

    What is IPC IPC [Inter-Process Communication] 进程间通信,指至少两个进程或线程间传送数据或信号的一些技术或方法.在Linux/Unix中,提供了许多IPC ...

  7. (转)解决 ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务

    下面操作默认在安装Oralce数据库的服务器上运行. 1)确保Oracle 基本服务都已启动 OracleDBConsoleorcl OracleOraDb11g_home1TNSListener O ...

  8. python高级之多进程

    python高级之多进程 本节内容 多进程概念 Process类 进程间通讯 进程同步 进程池 1.多进程概念 multiprocessing is a package that supports s ...

  9. oracle 监听启动、停止、查看命令

    1.su oracle 然后启动监听器 1.lsnrctl start  会看到启动成功的界面; 1.lsnrctl stop  停止监听器命令. 1.lsnrctl status  查看监听器命令. ...

随机推荐

  1. EntityFramework 更新表结构到数据库

    在程序包管理器控制台 1.执行:Enable-Migrations -Force  生成:Migrations 2 修改AutomaticMigrationsEnabled默认为false改为true ...

  2. HDU 2256 Problem of Precision 数论矩阵快速幂

    题目要求求出(√2+√3)2n的整数部分再mod 1024. (√2+√3)2n=(5+2√6)n 如果直接计算,用double存值,当n很大的时候,精度损失会变大,无法得到想要的结果. 我们发现(5 ...

  3. ArrayList、Vector、HashMap、HashTable、HashSet的默认初始容量、加载因子、扩容增量

    这里要讨论这些常用的默认初始容量和扩容的原因是: 当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全 ...

  4. Durandal介绍

         Durandal是一个JS框架用于构建客户端single page application(SPAs).它支持MVC,MVP与MVVM前端构架模式.使用RequireJS做为其基本约定层,D ...

  5. Android应用开发基础之九:内容提供者(ContentProvider)

    内容提供者 应用的数据库是不允许其他应用访问的 内容提供者的作用:就是让别的应用访问到你的数据库 自定义内容提供者,继承ContentProvider类,重写增删改查方法,在方法中写增删改查数据库的代 ...

  6. Swift 学习笔记第一天-变量常量,及数据类型

    1.定义变量 用关键字 var 比如 var i=2 2.定义常量用let 如let c=3 可见Swift 定义时不用指定类型.由编译器推断 如果想指定类型 var i:Int32=2 练习 let ...

  7. 经典实用jQuery soChange幻灯片实例演示

    soChange一款多很经典的幻灯片的jQuery插件. 实例预览 引入文件 <link rel="stylesheet" type="text/css" ...

  8. javascript --- 子对象访问父对象的方式

    在传统面向对象的编程语言里,都会提供一种子类访问父类的特殊语法,引文我们在实现子类方法往往需要父类方法的额外辅助.在这种情况下,子类通常会调用父类中的同名方法,最终以便完成工作. javascript ...

  9. 实验12:Problem G: 强悍的矩阵运算来了

    这个题目主要是乘法运算符的重载,卡了我好久,矩阵的乘法用3个嵌套的for循环进行,要分清楚矩阵的乘法结果是第一个矩阵的行,第二个矩阵的列所组成的矩阵. 重载+,*运算符时,可以在参数列表中传两个矩阵引 ...

  10. 在 SharePoint Server 2013 中配置建议和使用率事件类型

    http://technet.microsoft.com/zh-cn/library/jj715889.aspx 适用于: SharePoint Server 2013 利用使用事件,您可以跟踪用户与 ...