• IPC之PIPE


      管道是一种只允许用在有亲属关系的进程间通信的方式,由函数pipe创建一个管道,read,write进行读写操作。

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

     参数pipefd[2]数组返回打开的读写描述符,pipefd[0]为读,pipefd[1]为写。

      第一个问题:文件描述符怎么会出现一个只能读,一个只能写呢?猜想是对一个文件打开了2次,一个以只读打开,一个以只写打开。使用fcntl来验证下:

           #include <unistd.h>
           #include <fcntl.h>
    
           int fcntl(int fd, int cmd, ... /* arg */ );
           F_GETFL (void)
                  Get the file access mode and the file status flags; arg is ignored.

    cmd为F_GETFL时,最后一个参数arg被忽略。测试代码:

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <stdio.h>
     4 #include <fcntl.h>
     5 #include <signal.h>
     6 #include <unistd.h>
     7 #include <stdlib.h>
     8 
     9 int main(void)
    10 {
    11     int flags;
    12     int fd[2];
    13 
    14     if (pipe(fd) < 0)
    15     {
    16         perror("pipe error");
    17     }
    18 
    19     flags = fcntl(fd[0], F_GETFL,0);
    20     if ( flags < 0 )
    21     {
    22         perror("fcntl");
    23         close(fd[0]);
    24         close(fd[1]);
    25     }
    26     switch (flags & O_ACCMODE) 
    27     {
    28         case O_RDONLY:
    29             printf("read only
    ");
    30             break;
    31 
    32         case O_WRONLY:
    33             printf("write only
    ");
    34             break;
    35 
    36         case O_RDWR:
    37             printf("read write
    ");
    38             break;
    39 
    40         default:
    41             printf("unknown access mode
    ");
    42     }
    43 
    44     flags = fcntl(fd[1], F_GETFL,0);
    45     if ( flags < 0 )
    46     {
    47         perror("fcntl");
    48         close(fd[0]);
    49         close(fd[1]);
    50     }
    51     switch (flags & O_ACCMODE) 
    52     {
    53         case O_RDONLY:
    54             printf("read only
    ");
    55             break;
    56 
    57         case O_WRONLY:
    58             printf("write only
    ");
    59             break;
    60 
    61         case O_RDWR:
    62             printf("read write
    ");
    63             break;
    64 
    65         default:
    66             printf("unknown access mode
    ");
    67     }
    68     close(fd[0]);
    69     close(fd[1]);
    70     exit(0);
    71 }
    View Code

    运行结果:

    read only
    write only

    与猜想相符。

      数据的流向:

    从图中可以看出,进程可以以pipefd[1]写完,然后以pipefd[0]读,自己写自己读,这条数据流是通的。 验证:

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <stdio.h>
     4 #include <fcntl.h>
     5 #include <signal.h>
     6 #include <unistd.h>
     7 #include <stdlib.h>
     8 
     9 #define MAXLINE     4096
    10 int main(void)
    11 {
    12     int flags;
    13     int fd[2], n;
    14     char buf[MAXLINE];
    15     if (pipe(fd) < 0)
    16     {
    17         perror("pipe error");
    18     }
    19 
    20     n = write(fd[1], "hello world
    ", MAXLINE);
    21     if ( n < 0 )
    22     {
    23         perror("write");
    24         goto end;
    25     }
    26     n = read(fd[0],buf, n);
    27     if ( n < 0 )
    28     {
    29         perror("read");
    30         goto end;
    31     }
    32     printf("read:%s
    ",buf);
    33 
    34 end:
    35     close(fd[0]);
    36     close(fd[1]);
    37     exit(0);
    38 }
    View Code

    输出:

    read:hello world

      既然是进程间通信,那么管道在同一个进程中读写基本是没什么意义的,管道常用的方式是,先创建一个管道,然后fork,父子进程就共享了这个管道了。数据流向如图:

    这样,管道的写端有2个进程操作,读端有2个进程操作。但是这样一来就出现了一个问题,假设父进程读,那么这个数据是它自己写进去的呢?还是子进程写进去的?无法区分。通常一个进程关闭它的读,另一个进程关闭它的写,这样,数据流向就只有一个方向了,数据来自谁就显而易见了。如图:

    测试代码:

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <stdio.h>
     4 #include <fcntl.h>
     5 #include <signal.h>
     6 #include <unistd.h>
     7 #include <stdlib.h>
     8 #include <string.h>
     9 
    10 #define MAXLINE     4096
    11 
    12 int main(void)
    13 {
    14     int n;
    15     int fd[2];
    16     pid_t pid;
    17     char line[MAXLINE];
    18 
    19     if (pipe(fd) < 0)
    20         perror("pipe error");
    21     if ((pid = fork()) < 0)
    22     {
    23         perror("fork error");
    24     }
    25     else if (pid > 0)          /* parent */
    26     {
    27         close(fd[0]);
    28         write(fd[1], "hello world
    ", 12);
    29     }
    30     else                      /* child */
    31     {
    32         close(fd[1]);
    33         n = read(fd[0], line, MAXLINE);
    34         write(STDOUT_FILENO, line, n);
    35     }
    36     exit(0);
    37 }
    View Code

    结果:

    hello world

       读一个空的管道或者写一个满的管道都将导致阻塞,不过可以通过fcntlF_SETFL设置为O_NONBLOCK,从而不阻塞。

      当管道一端被关闭后,有下列2条规则:

      1.当读一个写端所有文件描述符引用都已被关闭的管道时,在所有数据被读完后,read将返回0。表示无数据可读。

      2.当写一个读端所有文件描述符引用都已被关闭的管道时,将产生SIGPIPE信号,write返回-1。

      

      混淆的东西,管道的容量管道的缓冲区大小

        管道的容量:指管道满时装的字节数,自2.6.11内核后,容量为64k。管道满了就会导致写操作产生阻塞。

        管道缓冲区大小:由PIPE_BUF指定,指的是保证管道写操作为原子操作的最大值,如果一次写入的内容超过这个值,那么这次的写操作就不是原子的。什么意思呢?就是指,可能存在多个进程写同一个管道,如果一次写入的字节数大于缓冲区大小,则可能会出现A进程写入的内容中插入了B进程写入的内容。

      下面是出现这种情况的代码:

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <stdio.h>
     4 #include <fcntl.h>
     5 #include <signal.h>
     6 #include <unistd.h>
     7 #include <stdlib.h>
     8 #include <string.h>
     9 
    10 #define MAXLINE     4096+100
    11 
    12 int main(void)
    13 {
    14     int n;
    15     int fd[2];
    16     pid_t pid;
    17     char line[MAXLINE];
    18 
    19     if (pipe(fd) < 0)
    20     {
    21         perror("pipe error");
    22     }
    23     
    24     if ((pid = fork()) < 0)
    25     {
    26         perror("fork error");
    27     }
    28     else if (pid > 0)          /* parent */
    29     {
    30         close(fd[1]);
    31         while ( 1 )
    32         {
    33             n = read(fd[0], line, MAXLINE);
    34             write(STDOUT_FILENO, line, n);
    35             write(STDOUT_FILENO, "
    
    
    ", 3);
    36         }
    37     }
    38     else                      /* child */
    39     {
    40         if ((pid = fork()) < 0)
    41         {
    42             perror("fork error");
    43         }
    44         else if (pid > 0)
    45         {
    46             close(fd[0]);
    47             
    48             while (1)
    49             {
    50                 memset(line, 'a',MAXLINE);
    51                 write(fd[1], line, MAXLINE);
    52             }
    53         }
    54         else
    55         {
    56             close(fd[0]);
    57             
    58             while ( 1 )
    59             {
    60                 memset(line, 'b',MAXLINE);
    61                 write(fd[1], line, MAXLINE);
    62             }
    63         }
    64     }
    65     
    66     exit(0);
    67 }
    View Code
  • 相关阅读:
    OpenGL中FrameBuffer使用
    每天进步一点点>结构化异常处理(SEH)
    js操作cookies
    [转]高性能网站优化与系统架构
    正则-匹配超链接地址及内容
    在c#.net中操作XML
    ActionScript 3 step by step (6) 元标记
    Facebook CEO:终极目标并非出售或上市
    ActionScript 3 step by step (3) 事件处理
    ActionScript 3 step by step (2) 使用Trace()跟踪输出
  • 原文地址:https://www.cnblogs.com/thammer/p/5093330.html
Copyright © 2020-2023  润新知