【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 管道


【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 管道


一、 无名管道

无名管道是临时的,在完成通信后将自动消失,因为文件描述符只能在某个进程中可见,因此被广泛应用于具有亲缘关系的进程间实现通信。采用的方法是先创建管道,再创建进程,使子进程继承父进程的创建的管道文件描述符,而要用无名管道实现非亲缘关系进程间的通信,则需要专门的文件描述符传递机制,这是可以实现的但需要借助其他机制,比如本地socket,此内容将在socket编程章节讲解。

1. 无名管道通信原理


2. 创建无名管道


3. 读写无名管道

(1) 以阻塞的方式读无名管道,如果当前没有进程(包括当前进程)可以访问写端,读操作都将立即返回。并按如下方式操作。

  • 如果管道现无数据,立即返回0。
  • 如果管道现有数据大于要读取的数据,立即读取期望大小的数据。
  • 如果管道现有数据小于要读取的数据,立即读取现有所有数据。
(2)以阻塞的方式读无名管道,如果当前有进程(包括当前进程)可以访问写端,按如下方式操作。
  • 如果管道现无数据,读操作阻塞。
  • 如果管道现有数据大于要读取的数据,立即读取期望大小的数据。
  • 如果管道现有数据小于要读取的数据,立即读取现有所有数据。
(3)如果以阻塞方式写无名管道,如果当前没有进程(包括当前进程)可以访问读端,写操作将收到SIGPIPE信号,write函数返回-1。如果当前有进程可以访问读端,且管道中有空闲,则写入成功。
(4)如果以阻塞方式写无名管道,如果当前管道已满,则阻塞当前进程。多进程对管道的写操作存在交叉写入的可能性,需要相应的避免竞争的机制。
(5)如果以O_NDELAY或O_NONBLOCK设置了管道的读端,若管道有数据,将读取数据,若无数据,将立即返回-1,且置errno为EAGAIN为错误,表示标示管道中无数据。
(6)如果以O_NDELAY或O_NONBLOCK设置了管道的写端,若管道有空闲,将写入数据,若无足够空闲,立即返回-1,且置errno为EAGAIN为错误。

二、文件描述符重定向

(1)cat<test01
(2)cat>test02<test01
(3)cat>test02 2>error <test01
(4)cat>test02 1&2 <test01
(5)cat 1&2 1>test02<test01

三、重定向编程dup和dup2函数

输入重定向、输出重定向、错误输出重定向。dup和dup2函数可以实现文件描述符的复制操作。




需要注意的是,dup和dup2函数复制文件描述符的功能对所有文件都适用,但这一操作与在同一进程中再次打开某个文件是有本质的区别的,在同一进程中再次打开某文件将分配新的struct file文件表项,两者完全独立,而dup和dup2函数是共享struct file文件表项的。

用dup2函数实现who|sort,示例代码:

<span style="font-size:18px;">#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
	int fds[2];
	if(pipe(fds)==-1)
	{
		perror("pipe");
		exit(EXIT_FAILURE);
	}
	if (fork()== 0) 
	{
		char buf[128];
		dup2(fds[0], 0);
		close(fds[1]);				//must include ,or block
		execlp("sort", "sort", (char *)0);
		//execlp("cat", "cat", (char *)0);
	}
	else 
	{
		if(fork() == 0) 
		{
			dup2(fds[1], 1);
			close(fds[0]);
			execlp("who", "who", (char *)0);
		}
		else 
		{
			close(fds[0]);
			close(fds[1]);
			 wait(NULL);
			 wait(NULL);
		}
	}
	return 0;
}</span>

四、流重定向popen函数


popen示例代码,实现功能echo test|cat:

<span style="font-size:18px;">#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<limits.h>
#include<string.h>
int main(int argc,char *argv[])
{
	FILE *finput,*foutput;
	char buffer[PIPE_BUF];
	int n;

	finput=popen("echo -e test!","r");
	foutput=popen("cat","w");

	read(fileno(finput),buffer,strlen("test!"));
	write(fileno(foutput),buffer,strlen("test"));
	
	pclose(finput);
	pclose(foutput);

	printf("\n");
	exit(EXIT_SUCCESS);
}</span>

五、有名管道FIFO

FIFO可以在同主机任意进程间实现通信。

1. 创建方法


2. 读写有名管道

和无名管道一样,有名管道也是一种特殊类型的文件,实质仍然是一段内核管理的内存空间,但在通过write和read系统调用来执行读写操作前,需要用open函数线打开管道文件。读写方式与无名管道类似,具体内容参考教材229页内容。

3. 非亲缘关系进程使用有名管道通信应用实例

向有名管道发送数据的进程代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc,char *argv[])
{
    int pipe_fd;
    int res;
    char buffer[]="hello world!";

    if (access(FIFO_NAME, F_OK) == -1) 
	{
        res = mkfifo(FIFO_NAME, 0766);
        if (res != 0) 
		{
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

	printf("Process %d opening FIFO O_WRONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_WRONLY);
    printf("the file's descriptor is %d\n", pipe_fd);

    if (pipe_fd != -1) 
	{
            res = write(pipe_fd, buffer, sizeof(buffer));
            if (res == -1) 
			{
                fprintf(stderr, "Write error on pipe\n");
                exit(EXIT_FAILURE);
            }
		printf("write data is %s,%d bytes is wirte\n",buffer,res);
        (void)close(pipe_fd); 
    }
    else 
        exit(EXIT_FAILURE);        
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}

从有名管道读取数据的进程代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc,char *argv[])
{
    int pipe_fd;
    int res;
    char buffer[4096];
    int bytes_read = 0;

    memset(buffer, '\0', sizeof(buffer));
    
    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, O_RDONLY);
    printf("the file's descriptor is %d\n",pipe_fd);

    if (pipe_fd != -1) 
	{
            bytes_read = read(pipe_fd, buffer, sizeof(buffer));
			printf("the read data is %s\n",buffer);
        	close(pipe_fd);
    }
    else 
        exit(EXIT_FAILURE);
    printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
    exit(EXIT_SUCCESS);
}
运行操作过程,首先编译发送数据的进程代码和读取数据的进程代码。

执行写端,运行结果:

$ ./fifo_write 
Process 3191 opening FIFO O_WRONLY//因为没有进程打开读端,阻塞open函数
接着在另一终端执行读端,运行结果:

$ ./fifo_read 
Process 3267 opening FIFO O_RDONLY
the file's descriptor is 3
the read data is hello world!
Process 3267 finished, 13 bytes read
执行读端后,写端输出以下信息:

the file's descriptor is 3
write data is hello world!,13 bytes is wirte
Process 3191 finished

六、两类型管道具有以下特点

(1)管道是特殊类型的文件,在满足先入先出的原则条件下可能进行读写,但不能定位读写位置。
(2)管道是单向的,要实现双向,需要两个管道。无名管道只能实现亲缘关系进程间通信(即无名管道的两个文件描述符可以被两者都访问到),而有名管道以磁盘文件的方式存在,可以实现本机任意两进程间通信。
(3)无名管道阻塞问题。无名管道无须显式打开,创建时直接返回文件描述符,而在读写时需要确实对方的存在,否则将退出。即如果当前进程向无名管道的写数据时,必须确定其别一端为某个进程(这个进程可以是当前进程)拥有,即有一个(或多个)进程的文件描述符表中至少有一个成员指向管道的另一端(显然,能够读写管道当前端,则本端在当前进程中是可以访问的)。如果写入无名管道的数据超过其最大值,写操作将阻塞,如果管道中没有数据,读操作将阻塞,如果管道发现另一端断开(另一端文件描述符关闭),将自动退出。
(4)有名管道阻塞问题。有名管道在打开时需要确实对方的存在,否则将阻塞。即以读方式打开某管道,该操作得以继续执行的条件是:在此之前,已经有一个进程以写的方式打开此管道,否则阻塞,直到条件满足,因此有名管道将阻塞在打开位置。也可以以读写(O_RDWR)方式打开有名管道,进程能够继续执行(不阻塞),只是这样操作没有什么意思,即当前进程读,当前进程写。


原文链接:http://blog.csdn.net/geng823/article/details/40819141


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux高级程序设计》是由杨宗德和吕光宏合著的一本经典编程书籍。该书具有以下特点和价值: 首先,该书全面而深入地介绍了Linux高级编程概念和技术,涵盖了Linux系统编程的方方面面。读者可以从中学习到Linux程序设计的基本知识,如进程管理、线程编程、文件操作、内存管理等,同时也可以学习到高级技术,如网络编程、信号处理、套接字编程等。 其次,该书注重实践与理论的结合,通过丰富的代码示例和实际案例,帮助读者加深对Linux编程的理解和应用。这些示例和案例设计合理,代码规范,可以帮助读者快速掌握各种编程技术,并提供了实际项目开发经验。 再次,该书系统性强,内容全面而深入。从系统调用、文件系统到网络编程、进程间通信,从基础知识到高级技术,涵盖了Linux程序设计的方方面面。由浅入深的组织结构和清晰的逻辑思路,使得读者可以循序渐进地学习,并可以根据自身需求选择阅读的内容。 最后,该书是一本经典且广泛应用于教育和实践的优秀教材。它以其权威性和实用性备受好评,成为许多大学和学院的Linux编程教材。除此之外,该书也适合有一定编程基础的工程师和开发人员自学。 总之,杨宗德和吕光宏的《Linux高级程序设计》是一本经典而实用的Linux编程教材,内容丰富、全面,适合初学者和有一定基础的程序员学习和参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值