• (匿名)管道的读写规则


    管道读写规则:
    1、当没有数据可读时

    O_NONBLOCK  disable(文件状态标志)未设置非阻塞的话 : read调用阻塞,即进程暂停执行,一直等到有数据来。
    O_NONBLOCK enable: 设置非阻塞模式的话,无数据可读时,read调用返回-1,errno值为EAGAIN。

    总之:阻塞模式的话,如果read无数据可读就阻塞;如果非阻塞模式的话,read无数据可读,则立即返回-1.

      int fcntl(int fd,int cmd) cmd=F_GETFLF_SETFL: 获取或者设置文件状态标志

     1 #include<unistd.h>
     2 #include<sys/types.h>
     3 #include<sys/stat.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<stdio.h>
     7 #include<errno.h>
     8 #include<string.h>
     9 
    10 #include<signal.h>
    11 #define ERR_EXIT(m)
    12     do
    13     {
    14         perror(m);
    15         exit(EXIT_FAILURE);
    16     }while(0)  //宏要求一条语句
    17 int main(int argc,char*argv[])
    18 {
    19     int pipefd[2];
    20     if(pipe(pipefd)==-1)
    21         ERR_EXIT("pipe error");
    22     pid_t pid;
    23     if((pid=fork())==-1)
    24         ERR_EXIT("fork error");
    25     else if(pid==0){//子进程发送数据给父进程
    26         sleep(3);//未设置文件状态标识时,文件对read阻塞,前三秒没有数据可读
    27         close(pipefd[0]);
    28         write(pipefd[1],"hello",5);
    29         close(pipefd[1]);
    30         exit(EXIT_SUCCESS);
    31     }
    32     close(pipefd[1]);
    33     char buf[10];
    34     int flags=fcntl(pipefd[0],F_GETFL);//获取文件状态标识
    35     fcntl(pipefd[0],F_SETFL,flags|O_NONBLOCK);//设置文件状态标识,非阻塞模式
    36     if(read(pipefd[0],buf,10)==-1) ERR_EXIT("read error");//默认文件pipefd[0]阻塞
    37     printf("buf=%s
    ",buf);
    38     close(pipefd[0]);
    39     return 0;
    40 }

    2如果所有管道写端对应的文件描述符被关闭,则read返回0。

     1 #include<unistd.h>
     2 #include<sys/types.h>
     3 #include<sys/stat.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<stdio.h>
     7 #include<errno.h>
     8 #include<string.h>
     9 
    10 #include<signal.h>
    11 #define ERR_EXIT(m)
    12     do
    13     {
    14         perror(m);
    15         exit(EXIT_FAILURE);
    16     }while(0)  //宏要求一条语句
    17 int main(int argc,char*argv[])
    18 {
    19     int pipefd[2];
    20     if(pipe(pipefd)==-1)
    21         ERR_EXIT("pipe error");
    22     pid_t pid;
    23     if((pid=fork())==-1)
    24         ERR_EXIT("fork error");
    25     else if(pid==0){
    26         close(pipefd[1]);//子进程写端关闭
    27         exit(EXIT_SUCCESS);
    28     }
    29     close(pipefd[1]);//父进程写端关闭
    30     sleep(1);
    31     char buf[10]={0};
    32     int ret=read(pipefd[0],buf,10);//读操作返回0
    33     printf("ret=%d
    ",ret);
    34     return 0;
    35 }

    3如果所有管道读端对应的文件描述符被关闭,则write操作符产生信号SIGPIPE.

     #include<unistd.h>
     #include<sys/types.h>
     #include<sys/stat.h>
     #include<fcntl.h>
     #include<stdlib.h>
     #include<stdio.h>
     #include<errno.h>
     #include<string.h>
     
      #include<signal.h>
     #define ERR_EXIT(m)
         do
         {
             perror(m);
             exit(EXIT_FAILURE);
         }while(0)  //宏要求一条语句
       void handler(int sig)
      {
        printf("recv a sig=%d ",sig);
      }
      int main(int argc,char*argv[]) {
       
    signal(SIGPIPE,handler); int pipefd[2]; if(pipe(pipefd)==-1) ERR_EXIT("pipe error"); pid_t pid; if((pid=fork())==-1) ERR_EXIT("fork error"); else if(pid==0){//子进程发 close(pipefd[0]); //关闭子进程读端 exit(EXIT_SUCCESS); } close(pipefd[0]); //关闭父进程读端 sleep(1); int ret= write(pipfd[1],"hello",5);
       if(ret == -1)
            printf("write error"); //捕捉信号的话会执行到这,否则默认处理函数之间结束进程 return 0; }

    4、当管道满的时候(不断往管道写入),write函数也有两种可能:

    如果文件状态标志(O_NONBLOCK disable)阻塞(未设置),write调用阻塞

    文件状态标志(O_NONBLOCK)非阻塞, write返回-1   errno=EAGAIN.

    同时这个例子还可以获取管道内核缓冲区的大小。

    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<errno.h>
    #include<string.h>
    
    #include<signal.h>
    #define ERR_EXIT(m)
        do
        {
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)  //宏要求一条语句
    int main(int argc,char*argv[])
    {
        int pipefd[2];
        if(pipe(pipefd)==-1)
            ERR_EXIT("pipe error");
        int ret;
        int count=0;
        int flags=(pipefd[1],F_GETFL);
        fcntl(pipefd[1],F_SETFL,flags|O_NONBLOCK);//改为非阻塞,写满的话再写就会出错。默认阻塞,写满write就会阻塞
        while(1)
        {
            ret=write(pipefd[1],"a",1);//一个一个写会阻塞,若一下子写的超过64k要写完才返回。
            if(ret==-1){
    printf("err=%s ",strerror(errno));
    break;
         } count
    ++; } printf("count=%d ",count);//65536字节.管道容量64k,管道容量不等于PIPE_BUF的大小,PIPE_BUF一般4k return 0; }

    5、当要写入的数据量不大于PIPE_BUF时,LINUX将保证写入的原子性,指多个进程向管道写入数据,各个进程数据连续,不被插入其他进程数据;当要写入的数据量大于PIPE_BUF时,LINUX将不再保证写入原子性。可能会被其他进程插入数据。PIPE_BUF一般为4KB。

    阻塞模式并且n<=PIPE_BUF:所有数据原子性写入。可以打印出来,在头文件<linux/limits.h>中。不一定等同于管道容量;

    非阻塞模式并且n<=PIPE_BUF:若空间不够,失败返回,不写入字符;

    阻塞模式并且n>PIPEBUF:不保证原子性,直到n个字节全部被写完才返回;

    非阻塞模式且n>PIPEBUF:失败同样不写入;若可以但不保证原子性。

     1 #include<unistd.h>
     2 #include<sys/types.h>
     3 #include<sys/stat.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<stdio.h>
     7 #include<errno.h>
     8 #include<string.h>
     9 
    10 #include<linux/limits.h>
    11 #define TEST_SIZE 68*1024
    12 #define ERR_EXIT(m)
    13     do
    14     {
    15         perror(m);
    16         exit(EXIT_FAILURE);
    17     }while(0)  //宏要求一条语句
    18 int main(int argc,char*argv[])
    19 {
    20     printf("pipe_buf=%d
    ",PIPE_BUF);
    21     int ret;
    22     char a[TEST_SIZE];//68KB
    23     char b[TEST_SIZE];
    24     memset(a,'A',sizeof(a));
    25     memset(b,'B',sizeof(b));
    26     int pipefd[2];
    27     if(pipe(pipefd)==-1)
    28         ERR_EXIT("pipe error");
    29     
    30     pid_t pid;
    31     //创建第一个子进程
    32     pid=fork();
    33     if(pid==0)
    34     {
    35         close(pipefd[0]);//关闭读端
    36         ret=write(pipefd[1],a,sizeof(a));//往管道写端写入68K数据。管道64k
    37         printf("apid=%d write %d bytes to pipe
    ",getpid(),ret);
    38         exit(0);
    39     }
    40     //再创建一个子进程,写入68k b
    41     pid=fork();
    42     if(pid==0)
    43     {
    44         close(pipefd[0]);
    45         ret=write(pipefd[1],b,sizeof(b));//写完68k才返回。管道大小64K.
    46         printf("bpid=%d write%d bytes to pipe
    ",getpid(),ret);
    47         exit(0);
    48     }
    49     //父进程
    50     close(pipefd[1]);
    51     sleep(1);//使子进程先到write()
    52     int fd=open("test.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);
    53     char buf[1024*4]={0};//每次接收4k数据
    54     int n=1;
    55     while(1)
    56     {
    57         ret=read(pipefd[0],buf,sizeof(buf));//每次接收4k数据
    58         if(ret==0)
    59             break;//子进程都发完数据了,管道写端都关闭了。跳出循环
    60         //没发完的话,每次收到4k,打印最后一个字符。会有穿插,68k远远超出PIPE_BUF
    61         printf("n=%02d pid=%d read %d bytes from pipe buf[4095=]%c
    ",n++,getpid(),ret,buf[4095]);//打印输出收到的数据
    62         write(fd,buf,ret);//每次4k写入文件。
    63     }
    64     return 0;
    65 }
  • 相关阅读:
    团队管理-每日站会,代码审查,结对编程
    Linux awk命令详解
    【Vegas原创】Excel中,日期和时间用&连接后格式不正确的解决方法
    SQLServer 数据库变成单个用户后无法访问问题的解决方法
    【Vegas原创】查询SQL Server更改记录的语句
    【Vegas原创】SQL Server 只安装客户端的方法
    IT? 挨踢
    64位Windows无法打开会声会影X5的解决方法
    小型IT部门建设之我见
    要熟练掌握的七个人生工具
  • 原文地址:https://www.cnblogs.com/wsw-seu/p/8385249.html
Copyright © 2020-2023  润新知