44.Linux 管道

news/2024/7/7 10:26:05

 管道的概念:

        管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:

1.其本质是一个伪文件(实为内核缓冲区)。

2.由两个文件描述符(fd[0]、fd[1])引用,一个表示读端,一个表示写段。

3.规定数据从管道的写端流入管道,从读端流出

管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。

管道的局限性:

①数据不能进程自己写,自己读。

②管道中数据不可反复读取。一旦读走,管道中不再存在。

③采用半双工通信方式(对讲机),数据只能在单方向上流动

④只能在有公共祖先(亲缘关系)的进程间使用管道

常见

 管道:

        实现原理:内核借助环形队列机制,使用内核缓冲区实现的。

 特性:       ①伪文件

                  ②管道中的数据只能一次读取

                  ③数据在管道中,只能单向流动。

局限性:1.自己写,不能自己读

               2.数据一读走,管道就没有了

               3.半双工通信

               4.血缘关系进程可用 

父子进程之间共享文件描述符

创建管道:

pipe函数:        创建并打开管道

int pipe(int pipefd[2]);
 

参数:        fd[0]:读段

                   fd[1]:写段     

返回值:     成功:0

                   失败:-1

例程

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{	
	int ret;
	int fd[2];
	pid_t pid;
	char buf[1024];
	char *str = "hello pipe\n";
    //创建管道
	ret = pipe(fd);
	if(ret == -1)
	{
		perror("pipe error");
	}
	//创建父子进程
	pid=fork();
	if(pid>0)
	{
		close(fd[0]);
		write(fd[1],str,strlen(str));
		sleep(1);
		close(fd[1]);
	}
	else if(pid==0)
	{
		close(fd[1]);
		ret = read(fd[0],buf,sizeof(buf));
		write(STDOUT_FILENO,buf,ret);
		close(fd[0]);
	}

	return 0;
}

执行结果如下:

   

管道的读写行为:

        读管道:

              1.管道有数据,read返回实际读到的字节数。

             2.管道无数据。(1)无写段,read返回0(读到文件尾)

                                                (2)有写段,read阻塞等待。

证明例程如下:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{	
	int ret;
	int fd[2];
	pid_t pid;
	char buf[1024];
	char *str = "hello pipe\n";

	ret = pipe(fd);
	if(ret == -1)
	{
		perror("pipe error");
	}
	
	pid=fork();
	if(pid>0)
	{
		close(fd[0]);
		sleep(3);
 
		close(fd[1]);
	}
	else if(pid==0)
	{
		close(fd[1]);
		ret = read(fd[0],buf,sizeof(buf));
		printf("son read ret =%d\n",ret);
		write(STDOUT_FILENO,buf,ret);
		close(fd[0]);
	}

	return 0;
}

 执行结果如下:

写管道:                  

         1.无读端,异常终止。 (SIGPIPE导致的)                       

        2.有读端:        (1)管道已满,阻塞等待。

                                 (2)管道未满,返回写出的字节个数。   

用管道实现父子进程ls | wc -l

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

int main()
{
	int fd[2];
	int ret;
	pid_t pid;

	ret = pipe(fd);
	if(ret == -1)
	{
		perror("pipe error");
	}

	pid=fork();
	if(pid == -1)
	{
		perror("fork error");
	}
	else if(pid>0)
	{	
		close(fd[0]);
		dup2(fd[1],STDOUT_FILENO);
		sleep(1);
		execlp("ls","ls",NULL);
	}
	else if(pid==0)
	{
		close(fd[1]);
		dup2(fd[0],STDIN_FILENO);
		execlp("wc","wc","-l",NULL);
	}
 
	return 0;
}

执行结果如下:

 

练习:使用管道实现兄弟进程间通信。兄:ls         弟:wc  -l   父:等待回收子进程。

        要求:使用“循环创建N个子进程”模型创建兄弟进程,使用循环因子i标示。注意管道读写行为。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
        int fd[2];
        int ret;
        pid_t pid;

        ret = pipe(fd);
        if(ret == -1)
        {
                perror("pipe error");
        }

        for(int i=0;i<2;i++)
        {
                pid=fork();
                if(pid == -1){
                        perror("fork error");
                }
                else if(pid==0){
                        break;
                }
                if(i==2){
                        close(fd[0]);
                        close(fd[1]);

                        wait(NULL);
                        wait(NULL);
                }else if(i==0){
                        close(fd[0]);
                        dup2(fd[1],STDOUT_FILENO);
                        execlp("ls","ls",NULL);
                        perror("execlp error");
                }else if(i==1){
                        close(fd[1]);
                        dup2(fd[0],STDIN_FILENO);
                        execlp("wc","wc","-l",NULL);
                        perror("execlp error");
                }
        }
        return 0;

}

测试:是否允许,一个pipe有一个写端,多个读端呢?

           是否允许有一个读端多个写端呢?

   

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
	pid_t pid;
	int fd[2],i,n;
	char buf[1024];

	int ret = pipe(fd);
	if(ret == -1){
		perror("pipe error");
		exit(1);
	}

	for(i=0;i<2;i++){
		if((pid=fork())==0)
			break;
		else if(pid==-1){
			perror("fork error");
			exit(1);
		}
	}

	if(i==0){
		close(fd[0]);
		write(fd[1],"1.hello\n",strlen("1.hello\n"));
	}else if(i==1){
		close(fd[0]);
		write(fd[1],"2.world\n",strlen("2.world\n"));
	}else{
		close(fd[1]);	
		n=read(fd[0],buf,1024);
		write(STDOUT_FILENO,buf,n);

		for(i=0;i<2;i++)
			wait(NULL);
	}

	return 0;
}

执行结果如下:

 

管道缓存区大小

ulimit -a

pipe size    (512bytes,-p)8

 

管道的优劣

优点:简单,相比信号,套接字实现进程间通信,简单很多。

缺点:1.只能单向通信,双向通信需要建立两个管道。

           2.只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用FIFO有名管道解决。      


FIFO

FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO,不相关的进程也能交换数据。

FIFO是Linux基础类型中的一种。但,FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。

(它存在的意义就是解决无亲缘关系的进程之间的通信)

命名管道fifo的创建和原理

创建方式:

1.命名:mkfifo        管道名

2.库函数:int mkfifo(const char *pathname,mode_t mode);

成功:0;

失败:-1;

一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可使用于fifo。如:close、read、write、unlink等。

fifo实现非亲缘关系进程间通信

fifo_w.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
        int fd,i;
        char buf[4096];

        if(argc<2){
                printf("please input fifoname:");
                return -1;
        }

        fd=open(argv[1],O_WRONLY);
        if(fd<0){
                perror("open error");
        }

        i=0;
        while(1){
                sprintf(buf,"hello fanfan %d\n",i++);
                write(fd,buf,strlen(buf));
                usleep(10000);
        }
        close(fd);
        return 0;
}

fifor.c

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
	int fd,len;
	char buf[4096];

	if(argc<2){
		printf("please input fifoname\n");
		return -1;
	}

	fd=open(argv[1],O_RDONLY);
	if(fd<0){
		perror("open error");
	}
	while(1){
		len=read(fd,buf,sizeof(buf));
		write(STDOUT_FILENO,buf,len);
		sleep(1);
	}
	close(fd);
	return 0;
}

执行结果如下:

 

vsp fifor.c
 


http://www.niftyadmin.cn/n/2432898.html

相关文章

从零单排学Redis【黄金】

前言 只有光头才能变强 好的&#xff0c;今天我们要上黄金段位了&#xff0c;如果还没经历过青铜和白银阶段的&#xff0c;可以先去蹭蹭经验再回来&#xff1a; 从零单排学Redis【青铜】从零单排学Redis【白银】看过相关Redis基础的同学可以知道Redis是单线程的&#xff0c;很多…

部署war包到Tomcat

部署war包到Tomcat 1. 开发给到一个war包&#xff0c;假设叫 a-b-c.war。 2. 打开Tomcat安装路径 &#xff0c;假设是“D:\Tomcat\apache-tomcat-7.0.68”&#xff0c;然后进入到 webapps文件夹。 3. 把 a-b-c.war丢到 webapps文件夹。 4. 启动Tomcat。 如果不需要更改配置文件…

MyEclipse2014搭建SSH框架

MyEclipse2014搭建SSH框架 2016-06-28 22:16 4868人阅读 评论(0) 收藏 举报版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 一. 创建一个Web Project 点击 “Next >”&#xff0c;默认设置&#xff0c;然后再点击“Next >”&#xff0c;勾选…

Redis集群环境下的-RedLock(真分布式锁) 实践

在不同进程需要互斥地访问共享资源时&#xff0c;分布式锁是一种非常有用的技术手段。 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器&#xff0c;但是这些库实现的方式差别很大&#xff0c;而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的…

复杂事件处理(CEP)语句

本页目录语法示例MATCH_RECOGNIZE用于从输入流中识别符合指定规则的事件&#xff0c;并按照指定的方式输出。 语法 SELECT [ ALL | DISTINCT ]{ * | projectItem [, projectItem ]* }FROM tableExpression[MATCH_RECOGNIZE ([PARTITION BY {partitionItem [, partitionItem]*}]…

45.Linux 目录操作

opendir函数 根据传入的目录名打开一个目录&#xff08;库函数&#xff09; DIR*类似于FILE* DIR *opendir&#xff08;const char *name&#xff09;; 成功返回 指向该目录结构体指针 失败返回NULL 参数支持相对路径、绝对路径两种方式&#xff1a;例…

Java读取 Mysql的 datetime类型

Java读取 Mysql的 datetime类型 1. 在Mysql数据库中使用DATETIME类型来存储时间&#xff0c;使用JDBC中读取这个字段的时候&#xff0c;应该使用 ResultSet.getTimestamp()&#xff0c;这样会得到一个java.sql.Timestamp类型的数据。 2. 在这里既不能使用 ResultSet.getDate()&…

46.Linux 重定向(duplicate)复制

&#xff08;duplicate&#xff09;复制 dup和dup2函数 int dup(int oldfd); oldfd:已有的文件描述符 返回:新文件描述符 #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int …