精华内容
参与话题
问答
  • linux管道pipe详解

    万次阅读 多人点赞 2016-12-23 17:16:36
    调用pipe系统函数即可创建一个管道。有如下特质: 1. 其本质是一个伪文件(实为内核缓冲区) 2. 由两个文件描述符引用,一个表示读端,一个表示写端。 3. 规定数据从管道的写端流入管道,从读端流出。 管道的原理: ...

    管道

    管道的概念:

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

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

    2. 由两个文件描述符引用,一个表示读端,一个表示写端。

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

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

    管道的局限性:

    ① 数据自己读不能自己写。

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

    ③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。

    ④ 只能在有公共祖先的进程间使用管道。

    常见的通信方式有,单工通信、半双工通信、全双工通信。

    pipe函数

    创建管道

        int pipe(int pipefd[2]); 成功:0;失败:-1,设置errno

    函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。

    管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤:

     

    1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。

    2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

    3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

        练习:父子进程使用管道通信,父写入字符串,子进程读出并,打印到屏幕。 【pipe.c】

    思考:为甚么,程序中没有使用sleep函数,但依然能保证子进程运行时一定会读到数据呢?

    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/wait.h>
    
    void sys_err(const char *str)
    {
        perror(str);
        exit(1);
    }
    
    int main(void)
    {
        pid_t pid;
        char buf[1024];
        int fd[2];
        char *p = "test for pipe\n";
        
       if (pipe(fd) == -1) 
           sys_err("pipe");
    
       pid = fork();
       if (pid < 0) {
           sys_err("fork err");
       } else if (pid == 0) {
            close(fd[1]);
            int len = read(fd[0], buf, sizeof(buf));
            write(STDOUT_FILENO, buf, len);
            close(fd[0]);
       } else {
           close(fd[0]);
           write(fd[1], p, strlen(p));
           wait(NULL);
           close(fd[1]);
       }
        
        return 0;
    }
    


    管道的读写行为

        使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

    1. 如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

    2. 如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

    3. 如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉,不终止进程。具体方法信号章节详细介绍。

    4. 如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。

    总结:

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

    2. 管道中无数据:

    (1) 管道写端被全部关闭,read返回0 (好像读到文件结尾)

      (2) 写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)

        ② 写管道: 1. 管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)

    2. 管道读端没有全部关闭:

    (1) 管道已满,write阻塞。

    (2) 管道未满,write将数据写入,并返回实际写入的字节数。

        练习:使用管道实现父子进程间通信,完成:ls | wc –l。假定父进程实现ls,子进程实现wc。

    ls命令正常会将结果集写出到stdout,但现在会写入管道的写端;wc –l 正常应该从stdin读取数据,但此时会从管道的读端读。      【pipe1.c】

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
    	pid_t pid;
    	int fd[2];
    
    	pipe(fd);
    	pid = fork();
    
    	if (pid == 0) {  //child
    		close(fd[1]);	                //子进程从管道中读数据,关闭写端
    		dup2(fd[0], STDIN_FILENO);		//让wc从管道中读取数据
    		execlp("wc", "wc", "-l", NULL);	//wc命令默认从标准读入取数据
    
    	} else {
    
    		close(fd[0]);	//父进程向管道中写数据,关闭读端
    		dup2(fd[1], STDOUT_FILENO);		//将ls的结果写入管道中
    		execlp("ls", "ls", NULL);		//ls输出结果默认对应屏幕
    	}
    
    	return 0;
    }
    
    
    
    
    
    /*
     *  程序不时的会出现先打印$提示符,再出程序运行结果的现象。
     *  这是因为:父进程执行ls命令,将输出结果给通过管道传递给
     *  子进程去执行wc命令,这时父进程若先于子进程打印wc运行结果
     *  之前被shell使用wait函数成功回收,shell就会先于子进程打印
     *  wc运行结果之前打印$提示符。
     *  解决方法:让子进程执行ls,父进程执行wc命令。或者在兄弟进程间完成。
     */
    
    


    程序执行,发现程序执行结束,shell还在阻塞等待用户输入。这是因为,shell → fork → ./pipe1, 程序pipe1的子进程将stdin重定向给管道,父进程执行的ls会将结果集通过管道写给子进程。若父进程在子进程打印wc的结果到屏幕之前被shell调用wait回收,shell就会先输出$提示符。

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

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

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
    	pid_t pid;
    	int fd[2], i;
    	
    	pipe(fd);
    
    	for (i = 0; i < 2; i++) {
    		if((pid = fork()) == 0) {
    			break;
            }
        }
    
    	if (i == 0) {			//兄
    		close(fd[0]);				//写,关闭读端
    		dup2(fd[1], STDOUT_FILENO);		
    		execlp("ls", "ls", NULL);	
    	} else if (i == 1) {	//弟
    		close(fd[1]);				//读,关闭写端
    		dup2(fd[0], STDIN_FILENO);		
    		execlp("wc", "wc", "-l", NULL);		
    	} else {
            close(fd[0]);
            close(fd[1]);
    		for(i = 0; i < 2; i++)		//两个儿子wait两次
    			wait(NULL);
    	}
    
    	return 0;
    }
    


        测试:是否允许,一个pipe有一个写端,多个读端呢?是否允许有一个读端多个写端呢? 【pipe3.c】

    #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("pipe 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]);       //父进程关闭写端,留读端读取数据    
    //		sleep(1);
    		n = read(fd[0], buf, 1024);     //从管道中读数据
    		write(STDOUT_FILENO, buf, n);
    
    		for(i = 0; i < 2; i++)		//两个儿子wait两次
    			wait(NULL);
    	}
    
    	return 0;
    }
    


    展开全文
  • pipe函数详解

    万次阅读 2018-11-19 18:44:14
    pipe函数可用于创建一个管道,以实现进程间的通信。 pipe函数的定义如下: #include<unistd.h> int pipe(int fd[2]); pipe函数定义中的fd参数是一个大小为2的一个数组类型的指针。该函数成功时返回0,并...

    pipe函数可用于创建一个管道,以实现进程间的通信。

    pipe函数的定义如下:

    #include<unistd.h>
    int pipe(int fd[2]);

            pipe函数定义中的fd参数是一个大小为2的一个数组类型的指针。该函数成功时返回0,并将一对打开的文件描述符值填入fd参数指向的数组。失败时返回 -1并设置errno。

            通过pipe函数创建的这两个文件描述符 fd[0] 和 fd[1] 分别构成管道的两端,往 fd[1] 写入的数据可以从 fd[0] 读出。并且 fd[1] 一端只能进行写操作,fd[0] 一端只能进行读操作,不能反过来使用。要实现双向数据传输,可以使用两个管道。

            默认情况下,这一对文件描述符都是阻塞的。此时,如果我们用read系统调用来读取一个空的管道,则read将被阻塞,知道管道内有数据可读;如果我们用write系统调用往一个满的管道中写数据,则write也将被阻塞,直到管道有足够的空闲空间可用(read读取数据后管道中将清除读走的数据)。当然,用户可自行将 fd[0] 和 fd[1] 设置为非阻塞的。

            如果管道的写端文件描述符 fd[1] 的引用计数减少至0,即没有任何进程需要往管道中写入数据,则对该管道的读端文件描述符 fd[0] 的read操作将返回0(管道内不存在数据的情况),即读到了文件结束标记(EOF,End Of File);反之,如果管道的读端文件描述符 fd[0] 的引用计数减少至0,即没有任何进程需要从管道读取数据,则针对该管道的写端文件描述符 fd[1] 的write操作将失败,并引发SIGPIPE信号(往读端被关闭的管道或socket连接中写数据)。

            管道内部传输的数据是字节流,这和TCP字节流的概念相同。但它们又存在细微的差别。应用层程序能往一个TCP连接中写入多少字节的数据,取决于对方接受窗口的大小和本端的拥塞窗口的大小。而管道的话本身拥有一个容量限制,它规定如果管道的写端应用程序不将管道中数据读走的话,该管道最多还能被写入多少字节的数据。管道容量的大小默认是65536字节。我们也可以使用fcntl函数来修改管道容量。

            此外,socket 的基础 API 中有一个socketpair 函数。它能够方便地创建双向管道。其函数定义如下:

    #include<sys/types.h>
    #include<sys/socket.h>
    int socketpair(int domain, int type, int protocol, int fd[2]);

            socketpair 前三个参数的含义和 socket 系统调用的三个参数完全相同,但domain只能使用 UNIX本地域协议族AF_UNIX,因为对于双向管道我们只能在本地使用。最后一个参数和 pipe系统调用的参数一样,只不过 socket系统调用创建的这一对文件描述符即使可读的也是可写的。

            socketpair函数执行成功返回0,失败返回 -1并设置errno。

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

    万次阅读 多人点赞 2017-05-10 00:43:13
    详解进程间通信方式———管道;管道是如何实现进程间通信的;以及管道读取数据的四种方式,以及管道容量的大小。

    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. }  

    运行结果:

    每隔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. }  

    运行结果:

    这里写图片描述

    使用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

    展开全文
  • linux管道pipe详解

    万次阅读 多人点赞 2018-08-24 17:58:04
    调用pipe系统函数即可创建一个管道。有如下特质: 1. 其本质是一个伪文件(实为内核缓冲区) 2. 由两个文件描述符引用,一个表示读端,一个表示写端。 3. 规定数据从管道的写端流入管道,从读端流出。 管道的原理:...

    管道

    管道的概念:

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

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

    2. 由两个文件描述符引用,一个表示读端,一个表示写端。

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

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

    管道的局限性:

    ① 数据自己读不能自己写。

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

    ③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。

    ④ 只能在有公共祖先的进程间使用管道。

    常见的通信方式有,单工通信、半双工通信、全双工通信。

    pipe函数

    创建管道

        int pipe(int pipefd[2]); 成功:0;失败:-1,设置errno

    函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。

    管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤:

    1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。

    2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

    3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

        练习:父子进程使用管道通信,父写入字符串,子进程读出并,打印到屏幕。 【pipe.c】

    思考:为甚么,程序中没有使用sleep函数,但依然能保证子进程运行时一定会读到数据呢?

     

    
     
    #include <unistd.h>
    
    #include <string.h>
    
    #include <stdlib.h>
    
    #include <stdio.h>
    
    #include <sys/wait.h>
    
    
    void sys_err(const char *str)
    
    {
    
    perror(str);
    
    exit(1);
    
    }
    
    
    int main(void)
    
    {
    
    pid_t pid;
    
    char buf[1024];
    
    int fd[2];
    
    char *p = "test for pipe\n";
    
    
    if (pipe(fd) == -1)
    
    sys_err("pipe");
    
    
    pid = fork();
    
    if (pid < 0) {
    
    sys_err("fork err");
    
    } else if (pid == 0) {
    
    close(fd[1]);
    
    int len = read(fd[0], buf, sizeof(buf));
    
    write(STDOUT_FILENO, buf, len);
    
    close(fd[0]);
    
    } else {
    
    close(fd[0]);
    
    write(fd[1], p, strlen(p));
    
    wait(NULL);
    
    close(fd[1]);
    
    }
    
    
    return 0;
    
    }

     

    管道的读写行为

        使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

    1. 如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

    2. 如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

    3. 如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉,不终止进程。具体方法信号章节详细介绍。

    4. 如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。

    总结:

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

    2. 管道中无数据:

    (1) 管道写端被全部关闭,read返回0 (好像读到文件结尾)

      (2) 写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)

        ② 写管道: 1. 管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)

    2. 管道读端没有全部关闭:

    (1) 管道已满,write阻塞。

    (2) 管道未满,write将数据写入,并返回实际写入的字节数。

        练习:使用管道实现父子进程间通信,完成:ls | wc –l。假定父进程实现ls,子进程实现wc。

    ls命令正常会将结果集写出到stdout,但现在会写入管道的写端;wc –l 正常应该从stdin读取数据,但此时会从管道的读端读。      【pipe1.c】

     

    
     
    #include <stdio.h>
    
    #include <unistd.h>
    
    #include <sys/wait.h>
    
    
    int main(void)
    
    {
    
    pid_t pid;
    
    int fd[2];
    
    
    pipe(fd);
    
    pid = fork();
    
    
    if (pid == 0) { //child
    
    close(fd[1]); //子进程从管道中读数据,关闭写端
    
    dup2(fd[0], STDIN_FILENO); //让wc从管道中读取数据
    
    execlp("wc", "wc", "-l", NULL); //wc命令默认从标准读入取数据
    
    
    } else {
    
    
    close(fd[0]); //父进程向管道中写数据,关闭读端
    
    dup2(fd[1], STDOUT_FILENO); //将ls的结果写入管道中
    
    execlp("ls", "ls", NULL); //ls输出结果默认对应屏幕
    
    }
    
    
    return 0;
    
    }
    
    
    
    
    
    
    /*
    
    * 程序不时的会出现先打印$提示符,再出程序运行结果的现象。
    
    * 这是因为:父进程执行ls命令,将输出结果给通过管道传递给
    
    * 子进程去执行wc命令,这时父进程若先于子进程打印wc运行结果
    
    * 之前被shell使用wait函数成功回收,shell就会先于子进程打印
    
    * wc运行结果之前打印$提示符。
    
    * 解决方法:让子进程执行ls,父进程执行wc命令。或者在兄弟进程间完成。
    
    */
    
    


     

     

    程序执行,发现程序执行结束,shell还在阻塞等待用户输入。这是因为,shell → fork → ./pipe1, 程序pipe1的子进程将stdin重定向给管道,父进程执行的ls会将结果集通过管道写给子进程。若父进程在子进程打印wc的结果到屏幕之前被shell调用wait回收,shell就会先输出$提示符。

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

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

     

    
     
    #include <stdio.h>
    
    #include <unistd.h>
    
    #include <sys/wait.h>
    
    
    int main(void)
    
    {
    
    pid_t pid;
    
    int fd[2], i;
    
    
    pipe(fd);
    
    
    for (i = 0; i < 2; i++) {
    
    if((pid = fork()) == 0) {
    
    break;
    
    }
    
    }
    
    
    if (i == 0) { //兄
    
    close(fd[0]); //写,关闭读端
    
    dup2(fd[1], STDOUT_FILENO);
    
    execlp("ls", "ls", NULL);
    
    } else if (i == 1) { //弟
    
    close(fd[1]); //读,关闭写端
    
    dup2(fd[0], STDIN_FILENO);
    
    execlp("wc", "wc", "-l", NULL);
    
    } else {
    
    close(fd[0]);
    
    close(fd[1]);
    
    for(i = 0; i < 2; i++) //两个儿子wait两次
    
    wait(NULL);
    
    }
    
    
    return 0;
    
    }


     

     

        测试:是否允许,一个pipe有一个写端,多个读端呢?是否允许有一个读端多个写端呢? 【pipe3.c】

     

    
     
    #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("pipe 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]); //父进程关闭写端,留读端读取数据
    
    // sleep(1);
    
    n = read(fd[0], buf, 1024); //从管道中读数据
    
    write(STDOUT_FILENO, buf, n);
    
    
    for(i = 0; i < 2; i++) //两个儿子wait两次
    
    wait(NULL);
    
    }
    
    
    return 0;
    
    }

     

     

    最近整理出了有关大数据,微服务,分布式,Java,Python,Web前端,产品运营,交互等1.7G的学习资料,有视频教程,源码,课件,软件工具,面试题等等,这里将珍藏多年的资源免费分享给各位小伙伴们。

    领取方式:https://blog.csdn.net/qq_42914528/article/details/81777449                                      

                                                             

    欢迎大家关注我的公众号:

    里面会分享很多JAVA技术、新知识、新技术、面试宝典等。
    有让技术爱好者(程序猿)得到很大提升的文章资料。

    还在犹豫什么,赶紧关注一波,微信搜索公众号:程序员的成长之路。或者扫描下方二维码进行关注。

                                                                         

                                                                           欢迎关注公众号,和我一起成长!

    展开全文
  • pipe 函数 (C语言)

    万次阅读 2010-11-22 17:10:00
    pipe 函数 (C语言) pipe我们用中文叫做管道。 以下讲解均是基于Linux为环境: 函数简介 所需头文件 #include<unistd.h><br />函数原型 int pipe(int fd[2]) 函数传入值 fd[2]:管道的两个...
  • Linux -- pipe管道

    2019-04-01 14:44:37
    该函数会创建一个管道,其中返回的fd[0]为读而打开,fd[1]为写而打开,一般用法是父进程利用pipe创建管道后,使用fork创建子进程,这样父子进程各有一对fd用于读取和写入管道,为了进程间的通信,需要各自保留一个读...
  • 同一局域网内Pipe管道通信代码借鉴服务端NamePipeServer.hNamePipeServer.cpp客户端NamePipeClient.hNamePipeClient.cppVS内的项目pipeName in ServerpipeName in ClientPipe链接同一局域网内的Pipe通信 代码借鉴 ...
  • JavaScript中的Pipe

    千次阅读 2019-05-30 18:19:13
    JavaScript中的Pipe 本文会介绍Pipe在函数式编程中的基本概念,怎么用Pipe让我们的代码变得更美好,以及新的pipe操作符,Fancy的东西在后面! 什么是Pipe? 先用一个最简单的例子来看一下什么是pipe,现在我们有...
  • Linux系统编程pipe()

    千次阅读 2019-01-09 19:11:59
    管道的概述 管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制。 无名管道有如下特点: 1、半双工,数据在同一时刻只能在一个方向上流动。...
  • Linux pipe函数

    万次阅读 2013-06-06 11:14:58
    pipe(建立管道): 1) 头文件 #include 2) 定义函数: int pipe(int filedes[2]); 3) 函数说明: pipe()会建立管道,并将文件描述词由参数filedes数组返回。  filedes[0]为管道里的读取端  filedes[1]则为...
  • pipenv的安装和使用

    万次阅读 2019-03-17 20:59:02
    一、安装 pip install pipenv 二、使用 1. 切换到目标目录下,输入pipenv – –two 或者 pipenv – –three,分别对应python2、python3环境 ... 然后该目录下会有一个Pipfile文件,内容为 ... ...
  • Pipe

    2017-09-29 04:29:07
    During the design phase of the new pipe shape the company ran into the problem of determining how far the light can reach inside each component of the pipe. Note that the material which the pipe is ...
  • 今天公司技术支持的童鞋报告一个客户的服务不工作了,紧急求助,于是远程登陆上... 查看采集数据的tomcat日志,习惯性的先翻到日志的最后去查看有没有异常的打印,果然发现了好几种异常信息,但是最多还是这个: ...
  • 一、当没有数据可读时 O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。 O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。 示例程序如下:  C++ Code  ...
  • pipe函数内核实现

    千次阅读 2017-12-09 17:32:10
    pipe源码分析 本文基于linux kernel 4.13 分析,与通用的2.6差距较大。请读者自行甄别本文的特性,是否符合自己当前环境。 本文要解决的问题 1:pipe源码分析 2:pipe大小限制 3:如果没有读...
  • linux内核管道pipe实现详解

    千次阅读 2017-08-12 14:45:42
    linux内核管道pipe实现详解
  • Named Pipe

    2008-12-16 23:38:00
    This page is a copy of http://developers.sun.com/solaris/articles/named_pipes.html.Introduction to Interprocess Communication Using Named Pipes Print-friendly VersionBy Faisal Faruqui, July 2002 ...
  • Linux进程间通信——pipe应用实例

    千次阅读 2017-01-09 19:25:37
    管道(pipe):管道可用于具有亲缘关系的进程间的通信,是一种半双工的方式,数据只能单向流动,允许一个进程和另一个与它有共同祖先的进程之间进行通信。下面是代码示例
  • Linux pipe详解

    千次阅读 2014-05-29 11:22:50
    1、 管道概述及相关API应用 1.1 管道相关的关键概念 管道是Linux 支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;...
  • Pipe实现分析

    千次阅读 2014-11-17 10:00:27
    一个大型的应用系统,往往需要众多进程协作,进程(Linux进程概念见附1)间通信的重要性显而易见。本系列文章阐述了Linux环境下的几种主要进程间通信手段,并针对每个通信手段关键技术环节给出详细实例。...

空空如也

1 2 3 4 5 ... 20
收藏数 181,670
精华内容 72,668
关键字:

pipe