Linux网络编程学习(六) ----- 管道(第四章)

1、管道的定义

管道就是将一个程序的输出和另外一个程序的输入连接起来的单向通道,比如命令: ls -l|more,就建立了一个管道,获取ls -l的输出作为more的输入,数据就沿着管道从管道的左边流到了管道的右边。

实际上内核为进程建立了两个句柄f1和f2,进程通过句柄f1向管道写入数据,同时通过f2从管道读出数据,这里是同一个进程建立了自己的管道

进程间的管道:通过fork出来的子进程,与父进程一样,拥有对同一个管道的读写句柄,但管道是单向的,因此要决定数据流向,如父进程到子进程或者子进程到父进程,那么不需要的句柄就要关闭

2、管道的建立和使用

1)通过pipe()函数建立管道,函数声明是int pipe(fd[2]) ,其中fd[0]用来从管道读,fd[1]向管道写

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
	int fd[2], nbytes;
	pid_t childpid;
	char string[] = “Hello, world!\n”;
	char readbuffer[80];
	pipe(fd);
	if((childpid = fork()) == -1)
	{
		perror(“fork”);
		exit(1);
	}
	if(childpid == 0)
	{
		/* 子进程关闭管道的读句柄 */
		close(fd[0]);
		/* 通过写句柄向管道写入信息 */
		write(fd[1], string, strlen(string));
		_exit(0);
	}
	else
	{
		/* 父进程关闭管道的写句柄 */
		close(fd[1]);
		/* 通过读句柄从管道读出信息 */
		nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
		printf(“Received string: %s”, readbuffer);
	}
	return(0);
}

  假定了数据流向是子进程到父进程,那么就由子进程写,父进程读,所以子进程要关闭读句柄,而父进程要关闭写句柄

2)使用popen()/pclose()函数,函数声明是FILE* popen(char * command, char *type)

popen()首先调用pipe()建立一个管道,然后用fork()函数建立一个子进程,运行一个shell环节,然后在这个环境中运行"command"参数指定的程序,数据管道流向由type控制,要么为"r"或者为"w",两者取其一,且type只取一个字符,比如"rw",那么只取"r",最后用pclose()关闭即可

#include <stdio.h>
#define MAXSTRS 5
int main(void)
{
	int cntr;
	FILE *pipe_fp;
	char *strings[MAXSTRS] = { "roy", "zixia", "gouki","supper", "mmwan"};
	/* 用popen 建立管道 */
	if (( pipe_fp = popen("sort", "w")) == NULL)
	{
		perror("popen");
		exit(1);
	}
	/* Processing loop */
	for(cntr=0; cntr<MAXSTRS; cntr++)
	{
		fputs(strings[cntr], pipe_fp);
		fputc(‘\n‘, pipe_fp);
	}
	/* 关闭管道 */
	pclose(pipe_fp);
	return(0);
}

  popen()可以节省源代码,同时还可以在"command"中使用任意合法的shell指令,包括重定向和管道,如下均为合法

popen("ls ~roy", "r");
popen("sort > /tmp/zixia", "w");
popen("sort | uniq | more", "w");

  如下例子为建立了两个管道,一个读管道,一个写管道

#include <stdio.h>
int main(void)
{
	FILE *pipein_fp, *pipeout_fp;
	char readbuf[80];
	/* 用popen 建立一个通向"ls:的读管道*/
	if (( pipein_fp = popen("ls", "r")) == NULL)
	{
		perror("popen");
		exit(1);
	}
	/* 用popen 建立一个通向"sort"的写管道 */
	if (( pipeout_fp = popen("sort", "w")) == NULL)
	{
		perror("popen");
		exit(1);
	}
	/* 进程循环 */
	while(fgets(readbuf, 80, pipein_fp))
	{
		fputs(readbuf, pipeout_fp);
	}

	/* 关闭打开的管道 */
	pclose(pipein_fp);
	pclose(pipeout_fp);
	return(0);
}

  

3、tips

1)pipe()调用必须在fork()前

2)及时关闭不需要的管道句柄

3)管道只能实现父子进程之间的通信,若两个进程没有fork()关系,必须考虑其他进程通信方式

原文地址:https://www.cnblogs.com/xqn2017/p/8901170.html

时间: 2024-10-11 03:22:54

Linux网络编程学习(六) ----- 管道(第四章)的相关文章

linux网络编程学习笔记之二 -----错误异常处理和各种碎碎(更新中)

errno 在unix系统中对大部分系统调用非正常返回时,通常返回值为-1,并设置全局变量errno(errno.h),如socket(), bind(), accept(), listen().erron存放一个正整数来保存上次出错的错误值. 对线程而言,每个线程都有专用的errno变量,不必考虑同步问题. strerror converts to English (Note: use strerror_r for thread safety) perror is simplified str

linux网络编程学习笔记之六 -----I/O多路复用服务端

多进程和多线程的目的是在于最大限度地利用CPU资源,当某个进程不需要占用太多CPU资源,而是需要I/O资源时,可以采用I/O多路复用,基本思路是让内核把进程挂起,直到有I/O事件发生时,再把控制返回给程序.这种事件驱动模型的高效之处在于,省去了进程和线程上下文切换的开销.整个程序运行在单一的进程上下文中,所有的逻辑流共享整个进程的地址空间.缺点是,编码复杂,而且随着每个逻辑流并发粒度的减小,编码复杂度会继续上升. I/O多路复用典型应用场合(摘自UNP6.1) select的模型就是这样一个实现

linux网络编程学习笔记之五 -----并发机制与线程?

进程线程分配方式 简述下常见的进程和线程分配方式:(好吧,我仅仅是举几个样例作为笔记...并发的水太深了,不敢妄谈...) 1.进程线程预分配 简言之,当I/O开销大于计算开销且并发量较大时,为了节省每次都要创建和销毁进程和线程的开销.能够在请求到达前预先进行分配. 2.进程线程延迟分配 预分配节省了处理时的负担,但操作系统管理这些进程线程也会带来一定的开销.由此,有个折中的方法是,当某个处理须要花费较长时间的时候,我们创建一个并发的进程或线程来处理该请求.实现也非常easy,在主线程中定时,定

linux网络编程学习笔记之四 -----多线程并发服务端

相对于使用进程实现并发,用线程的实现更加轻量.每个线程都是独立的逻辑流.线程是CPU上独立调度运行的最小单位,而进程是资源分配的单位.当然这是在微内核的操作系统上说的,简言之这种操作系统的内核是只提供最基本的OS服务,更多参看点击打开链接 每个线程有它自己的线程上下文,包括一个唯一的线程ID(linux上实现为unsigned long),栈,栈指针,程序计数器.通用目的寄存器和条件码,还有自己的信号掩码和优先级.同一个进程里的线程共享这个进程的整个虚拟地址空间,包括可执行的程序文本.程序的全局

linux网络编程学习笔记之五 -----并发机制与线程池

进程线程分配方式 简述下常见的进程和线程分配方式:(好吧,我只是举几个例子作为笔记...并发的水太深了,不敢妄谈...) 1.进程线程预分配 简言之,当I/O开销大于计算开销且并发量较大时,为了节省每次都要创建和销毁进程和线程的开销.可以在请求到达前预先进行分配. 2.进程线程延迟分配 预分配节省了处理时的负担,但操作系统管理这些进程线程也会带来一定的开销.由此,有个折中的方法是,当某个处理需要花费较长时间的时候,我们创建一个并发的进程或线程来处理该请求.实现也很简单,在主线程中定时,定时到期,

Linux网络编程学习(十一) ----- 五种I/O模式(第六章)

1.五种I/O模式是哪几个? 阻塞I/O,非阻塞I/O,I/O多路复用,信号驱动I/O(SIGIO),异步I/O 一般来讲程序进行输入操作有两个步骤,一是等待有数据可读,二是将数据从系统内核中拷贝到程序的数据集区. 对于一个套接字的数据操作,第一步就是等待数据从网络上传到本地,当数据包到达时,数据将会从网络层拷贝到内核的缓冲中,第二步就是从内核中将数据拷贝到程序的数据区中. 2.五种阻塞模式简介 1)阻塞I/O模式 套接字建立后默认的模式就是阻塞I/O模式,对于UDP而言,数据就绪的标志比较简单

linux网络编程学习笔记之四 -----多-threaded服务器

对于使用过程中并发.通过实现更轻量级线程. 每个线程都是一个独立的逻辑流. 主题是CPU在执行调度的最小独立单位,这个过程是资源分配单元.当然,这是在微内核操作系统说.总之,这是唯一的一个操作系统内核提供了最重要的OS服务,许多人看点击打开链接 每一个线程有它自己的线程上下文.包含一个唯一的线程ID(linux上实现为unsigned long),栈,栈指针.程序计数器.通用目的寄存器和条件码,还有自己的信号掩码和优先级.同一个进程里的线程共享这个进程的整个虚拟地址空间,包含可运行的程序文本.程

linux网络编程学习笔记之三 -----多进程并发服务端

首先是fork()函数.移步APUE 8.3.  比較清晰的解释能够參考http://blog.csdn.net/lingdxuyan/article/details/4993883和http://www.oschina.net/question/195301_62902 补充一点是:fork返回后,原进程中的每一个文件或套接口描写叙述符的引用计数加1(相当于被多打开了一次),每调用一次close,引用计数减1,仅仅有当引用计数减到0时才会真正关闭该套接字. 可运行文件被linux运行的唯一方式

Linux网络编程学习(三) ----- 进程控制实例

本节主要介绍一个进程控制的实例,功能就是在前台或者后台接收命令并执行命令,还能处理由若干个命令组成的命令行,该程序命名为samllsh. 基本逻辑就是 while(EOF not typed) { 从用户终端取得命令行 执行命令 } setp1:取得命令行内容,用uerin函数实现,处理步骤首先显示提示符,提示符的具体内容由用户通过参数传递给函数,然后每次从键盘读取一个字符,存入inpbuf中,结束时userin返回字符个数或者EOF(文件结尾),换行符也要存入inpbuf 代码如下: #inc