管道_管道通信 - CSDN
精华内容
参与话题
  • linux系统——管道

    千次阅读 2018-11-06 16:09:53
    一、管道基础 从一个进程连接数据到另一个进程时,使用管道(pipe),通常是把一个进程的输出通过管道连接到另一个进程的输入 linux的bash就是通过管道连接的: 例如:ps |grep hh,键盘敲入ps命令,ps执行后...

    一、管道基础

    从一个进程连接数据到另一个进程时,使用管道(pipe),通常是把一个进程的输出通过管道连接到另一个进程的输入, linux的bash就是通过管道连接的
    例如:ps |grep hh,键盘敲入ps命令,ps执行后的输出作为grep的输入,执行后输出到终端屏幕

    二、进程匿名管道——poen调用

    1、相关函数

    2、poen的实现原理

    请求popen调用执行另外一个程序的时候,首先启动的是shell,即系统中的sh,之后把参数传给它
    好处:所以的参数都是shell来解析的,因此可以使shell得到拓展(如*.c所指的就是所有c文件)
    坏处:针对每个popen调用,要启动一个被请求的程序,还要启动一个shell,每个popen调用都多启动两个进程,从系统资源来说成本较高,调用效率也略低

    3、popen例子

    在一个popen调用中使用了cat程序、wc程序、shell程序,并进行了异常重定向,但是只能看到最终popen调用的输出结果

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        FILE *read_fp;
        char buffer[BUFSIZ + 1];
        int chars_read;
    
        memset(buffer, '\0', sizeof(buffer));
        read_fp = popen("cat popen*.c | wc -l", "r");//以读的方式打开管道,此时调用进程可通过fread()读取调用线程的数据
        //因为不知道要读取的数据的大小,因此循环使用fread函数进行读取,直到fread函数返回的读取的数据为0
        if (read_fp != NULL) {
            chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
            while (chars_read > 0) {
                buffer[chars_read - 1] = '\0';
                printf("Reading:-\n %s\n", buffer);
                chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp);
            }
            pclose(read_fp);
            exit(EXIT_SUCCESS);
        }
        exit(EXIT_FAILURE);
    }
    

    三、进程匿名管道——pipe调用

    比popen调用更底层,对读写数据更多控制

    1、相关函数

    2、pipe例子——单进程例子下的管道

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        int data_processed;
        int file_pipes[2];
        const char some_data[] = "123";
        char buffer[BUFSIZ + 1];
    
        memset(buffer, '\0', sizeof(buffer));
    
        if (pipe(file_pipes) == 0) {
            data_processed = write(file_pipes[1], some_data, strlen(some_data));//从1端写数据
            printf("Wrote %d bytes\n", data_processed);
            data_processed = read(file_pipes[0], buffer, BUFSIZ);//从0端读数据,数据符合先进先出的FIFO规则
            printf("Read %d bytes: %s\n", data_processed, buffer);
            exit(EXIT_SUCCESS);
        }
        exit(EXIT_FAILURE);
    }
    

    3、pipe管道——主进程和分离进程间的管道

    fork()创建的进程后,新进程的许多属性跟原进程是相同的,新进程几乎跟原进程一模一样,但是有自己的数据空间、环境和文件描述符
    父进程的原来数据空间、环境和文件描述符依然存在

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        int data_processed;
        int file_pipes[2];
        const char some_data[] = "123";
        char buffer[BUFSIZ + 1];
        pid_t fork_result;
    
        memset(buffer, '\0', sizeof(buffer));
    
        if (pipe(file_pipes) == 0) {//主进程中创建管道
            fork_result = fork();//创建一个分离的子进程
            if (fork_result == -1) {
                fprintf(stderr, "Fork failure");
                exit(EXIT_FAILURE);
            }
    
    // We've made sure the fork worked, so if fork_result equals zero, we're in the child process.
    
            if (fork_result == 0) {//子进程中动作——读
                data_processed = read(file_pipes[0], buffer, BUFSIZ);
                printf("Read %d bytes: %s\n", data_processed, buffer);
                exit(EXIT_SUCCESS);
            }
    // Otherwise, we must be the parent process.
            else {//父进程中动作——写
                data_processed = write(file_pipes[1], some_data,
                                       strlen(some_data));
                printf("Wrote %d bytes\n", data_processed);
            }
        }
        exit(EXIT_SUCCESS);
    }
    

    4、pipe管道——主进程和其他进程之间的管道

    先用fork()创建一个跟主进程分离开的子进程,再在子进程中使用exec系列函数,从子进程中创建一个复制进程,
    此时的情况是:子进程已被exec创建的进程替代了,要想在复制进程中使用主进程的数据,该数据必须作为参数,在exec创建进程的时候传递过去
    /程序1——生产者/

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        int data_processed;
        int file_pipes[2];
        const char some_data[] = "123";
        char buffer[BUFSIZ + 1];
        pid_t fork_result;
    
        memset(buffer, '\0', sizeof(buffer));
    
        if (pipe(file_pipes) == 0) {//程序1的主进程创建管道
            fork_result = fork();
            if (fork_result == (pid_t)-1) {
                fprintf(stderr, "Fork failure");
                exit(EXIT_FAILURE);
            }
    
            if (fork_result == 0) {//已经在由fork创建的相对独立的进程内,此时再调用execl函数替换进程为Ppip4
                sprintf(buffer, "%d", file_pipes[0]);
                (void)execl("pipe4", "pipe4", buffer, (char *)0);//子进程内调用execl函数创建一个子进程的复制进程
                exit(EXIT_FAILURE);
            }
            else {
                data_processed = write(file_pipes[1], some_data,
                                       strlen(some_data));//程序1的父进程动作,向管道写数据
                printf("%d - wrote %d bytes\n", getpid(), data_processed);
            }
        }
        exit(EXIT_SUCCESS);
    }
    

    /程序2——消费者 程序名:pipe4/

    // The 'consumer' program, pipe4.c, that reads the data is much simpler.
    
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        int data_processed;
        char buffer[BUFSIZ + 1];
        int file_descriptor;
    
        memset(buffer, '\0', sizeof(buffer));
        sscanf(argv[1], "%d", &file_descriptor);
        data_processed = read(file_descriptor, buffer, BUFSIZ);//程序2的动作,从管道中读数据
    
        printf("%d - read %d bytes: %s\n", getpid(), data_processed, buffer);
        exit(EXIT_SUCCESS);
    }
    

    5、管道关闭后的读操作

    6、把管道作为标准输入和标准输出

    在这里插入图片描述

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        int data_processed;
        int file_pipes[2];
        const char some_data[] = "123";
        pid_t fork_result;
    
        if (pipe(file_pipes) == 0) {//创建管道,父子进程共4个文件描述符,t file_pipes[2]各有一份副本在父子进程
            fork_result = fork();//创建分离进程
            if (fork_result == (pid_t)-1) {
                fprintf(stderr, "Fork failure");
                exit(EXIT_FAILURE);
            }
    
            if (fork_result == (pid_t)0) {//在子进程中
                close(0);//关闭标准输入
                dup(file_pipes[0]);//打开一个新的文件描述符,该dup()函数总取最小的可用值,因此管道输入——》标准输入
                close(file_pipes[0]);//关闭管道原来用来读取的数据的文件描述
                close(file_pipes[1]);//因为子进程中不会向管道写输入,故关闭
    
                execlp("od", "od", "-c", (char *)0);
                exit(EXIT_FAILURE);
            }
            else {//现在在父进程中
                close(file_pipes[0]);//父进程不会从管道读取数据,因此关闭管道读取端
                data_processed = write(file_pipes[1], some_data,
                                       strlen(some_data));
                close(file_pipes[1]);//父进程向管道写完数据后,关闭管道写入端
                printf("%d - wrote %d bytes\n", (int)getpid(), data_processed);
            }
        }
        exit(EXIT_SUCCESS);
    }
    

    图示如下:
    在这里插入图片描述

    四、命名管道——FIFO

    主要用在不相关的进程间交换数据

    1、创建FIFO文件

    使用函数创建的是一个特殊的文件,文件的模式受umask影响mode_t mode=0777,umask=0022,则创建的特殊文件的权限值为755

    2、访问FIFO文件


    例子如下:
    程序中没有删除创建的文件,时因为我们无法知道是否有其他程序正在使用它

    // Let's start with the header files, a #define and the check that the correct number
    // of command-line arguments have been supplied.
    
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/my_fifo"
    
    int main(int argc, char *argv[])
    {
        int res;
        int open_mode = 0;
        int i;
    
        if (argc < 2) {
            fprintf(stderr, "Usage: %s <some combination of\
                   O_RDONLY O_WRONLY O_NONBLOCK>\n", *argv);
            exit(EXIT_FAILURE);
        }
    
    // Assuming that the program passed the test, we now set the value of open_mode
    // from those arguments.
    
        for(i = 1; i < argc; i++) {
            if (strncmp(*++argv, "O_RDONLY", 8) == 0)
                 open_mode |= O_RDONLY;
            if (strncmp(*argv, "O_WRONLY", 8) == 0)
                 open_mode |= O_WRONLY;
            if (strncmp(*argv, "O_NONBLOCK", 10) == 0)
                 open_mode |= O_NONBLOCK;
         }
    
    // We now check whether the FIFO exists and create it if necessary.
    // Then the FIFO is opened and output given to that effect while the program
    // catches forty winks. Last of all, the FIFO is closed.
    
        if (access(FIFO_NAME, F_OK) == -1) {
            res = mkfifo(FIFO_NAME, 0777);//创建命令管道文件
            if (res != 0) {
                fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
                exit(EXIT_FAILURE);
            }
        }
    
        printf("Process %d opening FIFO\n", getpid());
        res = open(FIFO_NAME, open_mode);//打开命名管道文件
        printf("Process %d result %d\n", getpid(), res);
        sleep(5);
        if (res != -1) (void)close(res);//关闭命名管道文件
        printf("Process %d finished\n", getpid());
        exit(EXIT_SUCCESS);
     }
    使用方式1:
    //以读的方式打开命名管道,open调用将阻塞,直到另一个进程已写方式打开命名管道
    $./fifo2 O_RDONLY &
    //以写的方式打开命名管道,open函数将阻塞,直到另一个进程以读方式打开命名管道
    $./fifo2 O_WDONLY
    
    使用方式2:
    //读方式+标志模式打开管道,open函数不会阻塞
    $./fifo2 O_RDONLY O_NONBLOCK &
    $./fifo2 O_WDONLY
    
    使用方式3:
    //写方式+标志模式打开管道
    $./fifo2 O_WDONLY O_NONBLOCK &
    $./fifo2 O_RDONLY
    

    3、O_NONBLOCK对FIFO文件的读写有影响

    在这里插入图片描述
    程序例子:

    //生产者程序fifo3.c
    #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"
    #define BUFFER_SIZE PIPE_BUF                 //定义在limit.h中,在linux和unix系统中一般是4096
    #define TEN_MEG (1024 * 1024 * 10)            //单次进入管道的长度
    
    int main()
    {
        int pipe_fd;
        int res;
        int open_mode = O_WRONLY;
        int bytes_sent = 0;
        char buffer[BUFFER_SIZE + 1];
    
        if (access(FIFO_NAME, F_OK) == -1) {//检测FIFO管道文件是否存在
            res = mkfifo(FIFO_NAME, 0777);//若不存在则创建管道文件
            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, open_mode);//以读的方式打开管道,此时阻塞直到有进程以写方式打开管道
        printf("Process %d result %d\n", getpid(), pipe_fd);
    
        if (pipe_fd != -1) {
            while(bytes_sent < TEN_MEG) {
                res = write(pipe_fd, buffer, BUFFER_SIZE);
                if (res == -1) {
                    fprintf(stderr, "Write error on pipe\n");
                    exit(EXIT_FAILURE);
                }
                bytes_sent += res;
            }
            (void)close(pipe_fd); 
        }
        else {
            exit(EXIT_FAILURE);        
        }
    
        printf("Process %d finished\n", getpid());
        exit(EXIT_SUCCESS);
    }
    
    //消费者程序fifo4.c
    #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"
    #define BUFFER_SIZE PIPE_BUF
    
    int main()
    {
        int pipe_fd;
        int res;
        int open_mode = O_RDONLY;
        char buffer[BUFFER_SIZE + 1];
        int bytes_read = 0;
    
        memset(buffer, '\0', sizeof(buffer));
        
        printf("Process %d opening FIFO O_RDONLY\n", getpid());
        pipe_fd = open(FIFO_NAME, open_mode);//以读的方式打开管道,阻塞,直到有进程以写的方式打开管道
        printf("Process %d result %d\n", getpid(), pipe_fd);
    
        if (pipe_fd != -1) {
            do {
                res = read(pipe_fd, buffer, BUFFER_SIZE);
                bytes_read += res;
            } while (res > 0);
            (void)close(pipe_fd);
        }
        else {
            exit(EXIT_FAILURE);
        }
    
        printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
        exit(EXIT_SUCCESS);
    }
    
    展开全文
  • Linux系统编程——进程间通信:管道(pipe)

    万次阅读 多人点赞 2015-06-10 15:52:11
    管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制。 每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的...

    管道的概述

    管道也叫无名管道,它是是 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]\n", 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\n");
    		
    		// 从管道里读数据,如果管道没有数据, read()会阻塞
    		read(fd_pipe[0], str, sizeof(str));
    		
    		printf("after read\n");
    		
    		printf("str=[%s]\n", 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]\n", 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\n", i);
    		}
    		
    		_exit(0);
    	}else if( pid > 0){// 父进程
    	
    		wait(NULL);	// 等待子进程结束,回收其资源
    	}
    	
    	return 0;
    }

    运行结果如下:



    2)通信过程中,别的进程先结束后,当前进程读端口关闭后,向管道内写数据时,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]); // 当前进程读端口关闭
    		
    		char buf[50] = "12345";
    		
    		// 当前进程读端口关闭
    		// write()会收到 SIGPIPE 信号,默认动作为中断当前进程
    		write(fd_pipe[1], buf, strlen(buf));
    		
    		while(1);	// 阻塞
    	}
    	
    	return 0;
    }

    运行结果如下:



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

    展开全文
  • 进程间的通信方式——pipe(管道

    万次阅读 多人点赞 2018-07-30 18:12:52
    详解进程间通信方式———管道管道是如何实现进程间通信的;以及管道读取数据的四种方式,以及管道容量的大小。

    1.进程间通信

       每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
    

    这里写图片描述

    不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同,而 pipe就是提供这份公共资源的形式的一种。

    2.匿名管道

    2.1管道的创建

       管道是由调用pipe函数来创建
    
    #include <unistd.h>
    int pipe (int fd[2]);
                             //返回:成功返回0,出错返回-1     
     fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
    

    2.2管道如何实现进程间的通信

    (1)父进程创建管道,得到两个⽂件描述符指向管道的两端

    (2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。

    (3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。
    这里写图片描述

    2.3如和用代码实现管道通信

    1. #include <stdio.h>  
    2. #include <unistd.h>  
    3. #include <string.h>  
    4. #include <errno.h>  
    5. int main()  
    6. {  
    7.     int fd[2];  
    8.     int ret = pipe(fd);  
    9.     if (ret == -1)  
    10.     {  
    11.         perror(”pipe error\n”);  
    12.         return 1;  
    13.     }  
    14.     pid_t id = fork();  
    15.     if (id == 0)  
    16.     {//child  
    17.         int i = 0;  
    18.         close(fd[0]);  
    19.         char *child = “I am  child!”;  
    20.         while (i<5)  
    21.         {  
    22.             write(fd[1], child, strlen(child) + 1);  
    23.             sleep(2);  
    24.             i++;  
    25.         }  
    26.     }  
    27.     else if (id>0)  
    28.     {//father  
    29.         close(fd[1]);  
    30.         char msg[100];  
    31.         int j = 0;  
    32.         while (j<5)  
    33.         {  
    34.             memset(msg,’\0’,sizeof(msg));  
    35.             ssize_t s = read(fd[0], msg, sizeof(msg));  
    36.             if (s>0)  
    37.             {  
    38.                 msg[s - 1] = ’\0’;  
    39.             }  
    40.             printf(”%s\n”, msg);  
    41.             j++;  
    42.         }  
    43.     }  
    44.     else  
    45.     {//error  
    46.         perror(”fork error\n”);  
    47.         return 2;  
    48.     }  
    49.     return  0;  
    50. }  
    save_snippets.png

    运行结果:

    每隔2秒打印一次I am child!        并且打印了五次。
    

    这里写图片描述

    2.4管道读取数据的四种的情况

    (1)读端不读,写端一直写
    这里写图片描述
    (2)写端不写,但是读端一直读
    这里写图片描述

    (3)读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。
    这里写图片描述

    如果一个管道读端一直在读数据,而管道写端的引⽤计数⼤于0决定管道是否会堵塞,引用计数大于0,只读不写会导致管道堵塞。

    (4)读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。

    这里写图片描述

    1. #include <stdio.h>  
    2. #include <unistd.h>  
    3. #include <string.h>  
    4. #include <errno.h>  
    5. int main()  
    6. {  
    7.     int fd[2];  
    8.     int ret = pipe(fd);  
    9.     if (ret == -1)  
    10.     {  
    11.         perror(”pipe error\n”);  
    12.         return 1;  
    13.     }  
    14.     pid_t id = fork();  
    15.     if (id == 0)  
    16.     {//child  
    17.         int i = 0;  
    18.         close(fd[0]);  
    19.         char *child = “I am  child!”;  
    20.         while (i<10)  
    21.         {  
    22.             write(fd[1], child, strlen(child) + 1);  
    23.             sleep(2);  
    24.             i++;  
    25.         }  
    26.     }  
    27.     else if (id>0)  
    28.     {//father  
    29.         close(fd[1]);  
    30.         char msg[100];  
    31.         int status = 0;  
    32.         int j = 0;  
    33.         while (j<5)  
    34.         {  
    35.             memset(msg, ’\0’sizeof(msg));  
    36.             ssize_t s = read(fd[0], msg, sizeof(msg));  
    37.             if (s>0)  
    38.             {  
    39.                 msg[s - 1] = ’\0’;  
    40.             }  
    41.             printf(”%s  %d\n”, msg, j);  
    42.             j++;  
    43.         }  
    44.         //写方还在继续,而读方已经关闭它的读端  
    45.         close(fd[0]);  
    46.         pid_t ret = waitpid(id, &status, 0);  
    47.         printf(”exitsingle(%d),exit(%d)\n”, status & 0xff, (status >> 8) & 0xff);  
    48.         //低八位存放该子进程退出时是否收到信号  
    49.         //此低八位子进程正常退出时,退出码是多少  
    50.     }  
    51.     else  
    52.     {//error  
    53.         perror(”fork error\n”);  
    54.         return 2;  
    55.     }  
    56.     return  0;  
    57. }  
    save_snippets.png

    运行结果:

    这里写图片描述

    使用kill -l 查看13号信号,可以知道13号信号代表SIGPIPE。

    总结:
    如果一个管道的写端一直在写,而读端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只写不读再次调用write会导致管道堵塞;
    如果一个管道的读端一直在读,而写端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只读不写再次调用read会导致管道堵塞;
    而当他们的引用计数等于0时,只写不读会导致写端的进程收到一个SIGPIPE信号,导致进程终止,只写不读会导致read返回0,就像读到⽂件末尾⼀样。

    2.5管道特点

    1.管道只允许具有血缘关系的进程间通信,如父子进程间的通信。
    
    2.管道只允许单向通信。
    
    3.管道内部保证同步机制,从而保证访问数据的一致性。
    
    4.面向字节流
    
    5.管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失管道也消失。
    

    2.6管道容量大小

    测试管道容量大小只需要将写端一直写,读端不读且不关闭fd[0],即可。
    测试代码:

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    int main()
    {
        int fd[2];
        int ret = pipe(fd);
        if (ret == -1)
        {
            perror("pipe error\n");
            return 1;
        }
        pid_t id = fork();
        if (id == 0)
        {//child
            int i = 0;
            close(fd[0]);
            char *child = "I am  child!";
            while (++i)
            {
                write(fd[1], child, strlen(child) + 1);
                printf("pipe capacity: %d\n", i*(strlen(child) + 1));
            }
            close(fd[1]);
        }
        else if (id>0)
        {//father
            close(fd[1]);
            waitpid(id, NULL, 0);
        }
        else
        {//error
            perror("fork error\n");
            return 2;
        }
        return  0;
    }

    可以看到写到65520之后管道堵塞了,而65536即为64K大小即为管道的容量(由于代码问题,少统计一次数据)。

    这里写图片描述

    进程间通信方式———信号量(Semaphore)
    http://blog.csdn.net/skyroben/article/details/72513985

    进程间通信方式———消息队列
    http://blog.csdn.net/skyroben/article/details/72520501

    进程间通信方式——共享内存
    http://blog.csdn.net/skyroben/article/details/72625028

    展开全文
  • 什么是管道

    千次阅读 2018-01-28 15:04:28
    而这里要讲的是计算机中的管道,确切的说是linux操作系统中的管道。那么,linux中的管道是什么?有什么作用? 管道 对于从事互联网行业的人来说,管道技术几乎每天都会用到。在计算机世界中,由于进程之间相互独立...

    ● 每周一言

    如果有不一样,就努力不让它一样。

    导语

    一说到管道,我们可能想到的是超级玛丽的下水道,亦或是曾经流行一时的小游戏flappy bird。而这里要讲的是计算机中的管道,确切的说是linux操作系统中的管道。那么,linux中的管道是什么?有什么作用?

    管道

    对于从事互联网行业的人来说,管道技术几乎每天都会用到。在计算机世界中,由于进程之间相互独立,信息无法交互。而计算机中的管道,就是一种解决进程间信息交互问题的手段。

    fig1

    管道类型分为两种:匿名管道和命名管道。

    匿名管道指不带名字标识的管道,用于父进程与其子进程之间的通信。命名管道则是带有名字标识符的管道,支持任意两个进程之间的通信。

    我们知道,计算机的信息通信方式分为单工通信、半双工通信及全双工通信三种,而管道属于半双工通信。半双工通信可以实现双向通信,但不能在两个方向上同时进行,必须交替进行。

    fig2

    当然,对于平常的bash和shell命令使用,是涉及不到匿名管道和命名管道这两个概念的,这两类管道一般出现在系统编程当中。

    在bash和shell中,通常用“|” 在两个命令之间创建管道,这个“|”叫管道命令符

    “|”管道命令符的作用是串联起一组命令的输入输出数据流。简单来讲就是,前一个命令执行后不回显结果,而是把结果通过“|”传递给后一个命令,以此类推直到没有管道符后终止命令,并回显最终结果。

    fig3

    比如命令:cat example.txt | wc -l。先按行读文件通过管道传递给wc命令计数。当然,这里也可以直接 wc -l example.txt 计数。

    以上便是计算机中管道的介绍,敬请期待下节内容。

    结语

    感谢各位的耐心阅读,后续文章于每周日奉上,敬请期待。欢迎大家关注小斗公众号 对半独白

    face

    展开全文
  • 管道

    2017-01-02 12:38:52
    管道 管道是进程间通信的主要手段之一。一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端。管道是一种特殊的文件,它不属于某一种文件系统,而是...
  • 管道(一)

    2019-09-10 08:51:53
    一、什么是管道 (1)管道是Unix中最古老的进程间通信的形式。 (2)我们把从一个进程连接到另一个进程的一个数据流称为一个“管道” (3)本质:内核缓冲区 二、管道限制 (1)管道是半双工的,数据只能向一个...
  • linux管道pipe详解

    万次阅读 多人点赞 2018-08-24 17:58:04
    管道 管道的概念: 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质: 1. 其本质是一个伪文件(实为内核缓冲区) 2. 由两个文件描述符引用...
  • 管道pipe理解

    2019-11-06 10:35:08
    管道pipe理解管道特点管道容量的大小功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants...
  • CATALOGpsexec部分已有资产从被连接主机(lisi)的事件来看psexec到底做了什么从攻击机的流量上来看psexec命令背后做了什么命名管道我理解的命名管道利用具体代码实现服务端代码客户端代码 psexec部分 psexec一般会被...
  • 【Linux】Linux的管道

    万次阅读 多人点赞 2019-02-05 16:51:25
    管道是Linux由Unix那里继承过来的进程间的通信机制,它是Unix早期的一个重要通信机制。其思想是,在内存中创建一个共享文件,从而使通信双方利用这个共享文件来传递信息。由于这种方式具有单向传递数据的特点,所以...
  • 管道(UNIX) 在类Unix计算机操作系统,一个管道是一个序列过程中通过其链接在一起的标准流,使每一个过程(输出标准输出)直接提供输入(标准输入)到下一个。 管线标准的shell语法是列出多个命令,以竖线(在...
  • linux无名管道和有名管道

    千次阅读 2017-04-07 16:55:41
    1)无名管道:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。  单独构成一种独立的文件系统:管道对于管道两端的进程而...
  • 有名管道和无名管道

    万次阅读 2016-10-31 21:53:47
    1.  (1)数据传输  一个进程需要将它的数据发送给另一个进程 (2)资源共享  多个进程之间共享同样的资源 (3)通知事件 ... 一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件 ...
  • 管道和命名管道

    千次阅读 2007-10-20 19:47:00
    “命名管道”或“命名管线”(Named Pipes )是一种简单的进程间通信( I P C)机制,Microsoft Windows NT,Windows 2000、Windows 95以及Windows 98均提供了对它的支持(但不包括Windows CE)。命名管道可在同一台...
  • windows管道

    千次阅读 2012-12-26 08:52:33
    也就是通过命名管道来实现,前面介绍的那三种方式呢,都是有缺陷或者说局限性太强, 而这里介绍的命名管道相对来说,在这方面就做得好很多了, 比如,剪贴板的话只能实现本机上进程之间的通信, 而邮槽的话...
  • 匿名管道和命名管道

    万次阅读 2017-06-19 15:16:31
    进程间通信(IPC)每个进程有各自不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到。所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间中拷贝到缓冲...匿名管道
  • Windows管道系统 - 命名管道

    千次阅读 2011-06-03 10:01:00
    命名管道一个命名管道是一个有名的、用于管道服务端与一个或多个管道客户端进行单路(“One-way”)或双向通讯的管道。一个命名管道的所有实例共享同一个管道名字,但是每一个实例都有它自己的管道句柄和缓冲区,并...
  • windows管道系统 ----命名管道

    千次阅读 2014-06-07 23:03:55
    一个命名管道是一个有名的、用于管道服务端与一个或多个管道客户端进行单路(“One-way”)或双向通讯的管道。一个命名管道的所有实例共享同一个管道名字,但是每一个实例都有它自己的管道句柄和缓冲区,并为客户/...
  • 管道通信

    万次阅读 2013-02-28 14:45:38
    什么是管道?    管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。 管道的分类    管道...
  • 匿名管道和有名管道

    千次阅读 2018-07-08 17:27:20
    1. 管道的概念本质: 内核缓冲区  伪文件 - 不占用磁盘空间特点: 两部分: 读端,写端,对应两个文件描述符 数据写端流入, 读端流出 操作管道的进程被销毁之后,管道自动被释放了 管道读写两端默认是阻塞的 2....
1 2 3 4 5 ... 20
收藏数 398,821
精华内容 159,528
关键字:

管道