• Linux系统编程——进程间通信:管道(pipe)


    管道的概述

    管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,全部的 UNIX 系统都支持这样的通信机制。


    无名管道有例如以下特点:

    1、半双工,数据在同一时刻仅仅能在一个方向上流动。

    2、数据仅仅能从管道的一端写入,从还有一端读出。

    3、写入管道中的数据遵循先入先出的规则。

    4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

    5、管道不是普通的文件,不属于某个文件系统,其仅仅存在于内存中。

    6、管道在内存中相应一个缓冲区。

    不同的系统其大小不一定同样。

    7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写很多其它的数据。

    8、管道没有名字。仅仅能在具有公共祖先的进程(父进程与子进程。或者两个兄弟进程,具有亲缘关系)之间使用。 


    对于无名管道特点的理解。我们能够类比现实生活中管子,管子的一端塞东西。管子的还有一端取东西。


    无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描写叙述符。



    管道的操作

    所需头文件:

    #include <unistd.h>


    int pipe(int filedes[2]);

    功能:

    创建无名管道。

    參数:

    filedes: 为 int 型数组的首地址,其存放了管道的文件描写叙述符 filedes[0]、filedes[1]。


    当一个管道建立时,它会创建两个文件描写叙述符 fd[0] 和 fd[1]。当中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道。

    一般文件 I/O 的函数都能够用来操作管道( lseek() 除外)。

    返回值:

    成功:0

    失败:-1


    以下我们写这个一个样例,子进程通过无名管道给父进程传递一个字符串数据:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
    	int fd_pipe[2] = {0};
    	pid_t pid;
    	
    	if( pipe(fd_pipe) < 0 ){// 创建无名管道
    		perror("pipe");
    	}
    	
    	pid = fork(); // 创建进程
    	if( pid < 0 ){ // 出错
    		perror("fork");
    		exit(-1);
    	}
    	
    	if( pid == 0 ){ // 子进程
    		char buf[] = "I am mike";
    		// 往管道写端写数据
    		write(fd_pipe[1], buf, strlen(buf));
    		
    		_exit(0);
    	}else if( pid > 0){// 父进程
    		wait(NULL);	// 等待子进程结束。回收其资源
    		
    		char str[50] = {0};
    		
    		// 从管道里读数据
    		read(fd_pipe[0], str, sizeof(str));
    		
    		printf("str=[%s]
    ", str); // 打印数据
    	}
    	
    	return 0;
    }

    执行结果例如以下:



    管道的特点

    每一个管道仅仅有一个页面作为缓冲区,该页面是依照环形缓冲区的方式来使用的。这样的訪问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据须要写时,并且每当写满一个页面就须要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。对应的,假设管道中没有可读数据,“消费者” 进程就要睡眠等待,详细步骤例如以下图所看到的:


    默认的情况下,从管道中读写数据。最基本的特点就是堵塞问题(这一特点应该记住)当管道里没有数据,还有一个进程默认用 read() 函数从管道中读数据是堵塞的



    測试代码例如以下:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
    	int fd_pipe[2] = {0};
    	pid_t pid;
    	
    	if( pipe(fd_pipe) < 0 ){// 创建无名管道
    		perror("pipe");
    	}
    	
    	pid = fork(); // 创建进程
    	if( pid < 0 ){ // 出错
    		perror("fork");
    		exit(-1);
    	}
    	
    	if( pid == 0 ){ // 子进程
    		
    		_exit(0);
    	}else if( pid > 0){// 父进程
    	
    		wait(NULL);	// 等待子进程结束。回收其资源
    		
    		char str[50] = {0};
    		
    		printf("before read
    ");
    		
    		// 从管道里读数据,假设管道没有数据, read()会堵塞
    		read(fd_pipe[0], str, sizeof(str));
    		
    		printf("after read
    ");
    		
    		printf("str=[%s]
    ", str); // 打印数据
    	}
    	
    	return 0;
    }

    执行结果例如以下:


    当然。我们编程时可通过 fcntl() 函数设置文件的堵塞特性。

    设置为堵塞:fcntl(fd, F_SETFL, 0);

    设置为非堵塞:fcntl(fd, F_SETFL, O_NONBLOCK);


    測试代码例如以下:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    
    int main(int argc, char *argv[])
    {
    	int fd_pipe[2] = {0};
    	pid_t pid;
    	
    	if( pipe(fd_pipe) < 0 ){// 创建无名管道
    		perror("pipe");
    	}
    	
    	pid = fork(); // 创建进程
    	if( pid < 0 ){ // 出错
    		perror("fork");
    		exit(-1);
    	}
    	
    	if( pid == 0 ){ // 子进程
    		
    		sleep(3);
    		
    		char buf[] = "hello, mike";
    		write(fd_pipe[1], buf, strlen(buf)); // 写数据
    		
    		_exit(0);
    	}else if( pid > 0){// 父进程
    	
    		fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK); // 非堵塞
    		//fcntl(fd_pipe[0], F_SETFL, 0); // 堵塞
    		
    		while(1){
    			char str[50] = {0};
    			read( fd_pipe[0], str, sizeof(str) );//读数据
    			
    			printf("str=[%s]
    ", str);
    			sleep(1);
    		}
    	}
    	
    	return 0;
    }

    执行结果例如以下:



    默认的情况下,从管道中读写数据。还有例如以下特点(知道有这么回事就够了,不用刻意去记这些特点):

    1)调用 write() 函数向管道里写数据。当缓冲区已满时 write() 也会堵塞。


    測试代码例如以下:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
    	int fd_pipe[2] = {0};
    	pid_t pid;
    	
    	char buf[1024] = {0};
    	memset(buf, 'a', sizeof(buf)); // 往管道写的内容
    	int i = 0;
    	
    	if( pipe(fd_pipe) < 0 ){// 创建无名管道
    		perror("pipe");
    	}
    	
    	pid = fork(); // 创建进程
    	if( pid < 0 ){ // 出错
    		perror("fork");
    		exit(-1);
    	}
    	
    	if( pid == 0 ){ // 子进程
    		while(1){
    			write(fd_pipe[1], buf, sizeof(buf));
    			i++;
    			printf("i ======== %d
    ", i);
    		}
    		
    		_exit(0);
    	}else if( pid > 0){// 父进程
    	
    		wait(NULL);	// 等待子进程结束,回收其资源
    	}
    	
    	return 0;
    }

    执行结果例如以下:



    2)通信过程中,别的进程先结束后,当前进程读port关闭后,向管道内写数据时,write() 所在进程会(收到 SIGPIPE 信号)退出,收到 SIGPIPE 默认动作为中断当前进程。


    測试代码例如以下:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(int argc, char *argv[])
    {
    	int fd_pipe[2] = {0};
    	pid_t pid;
    	
    	if( pipe(fd_pipe) < 0 ){// 创建无名管道
    		perror("pipe");
    	}
    	
    	pid = fork(); // 创建进程
    	if( pid < 0 ){ // 出错
    		perror("fork");
    		exit(-1);
    	}
    	
    	if( pid == 0 ){ // 子进程
    		//close(fd_pipe[0]);
    		
    		_exit(0);
    	}else if( pid > 0 ){// 父进程
    	
    		wait(NULL);	// 等待子进程结束,回收其资源
    		
    		close(fd_pipe[0]); // 当前进程读port关闭
    		
    		char buf[50] = "12345";
    		
    		// 当前进程读port关闭
    		// write()会收到 SIGPIPE 信号,默认动作为中断当前进程
    		write(fd_pipe[1], buf, strlen(buf));
    		
    		while(1);	// 堵塞
    	}
    	
    	return 0;
    }

    执行结果例如以下:



    本教程演示样例代码下载请点此处。

  • 相关阅读:
    Nginx 部署多个 web 项目(虚拟主机)
    Nginx 配置文件
    Linux 安装 nginx
    Linux 安装 tomcat
    Linux 安装 Mysql 5.7.23
    Linux 安装 jdk8
    Linux 安装 lrzsz,使用 rz、sz 上传下载文件
    springMVC 拦截器
    spring 事务
    基于Aspectj 注解实现 spring AOP
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/7039347.html
Copyright © 2020-2023  润新知