【study】linux 进程通信之管道

1.管道的本质:固定大小的内核缓冲区。
2.管道是半双工的,数据只能一个方向流动。
3.只有调用fork 父子进程才能管道通信。
4.管道的读写规则
一.如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE.

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
void handler(int num)
{
    int mypid;
    if(num == SIGCHLD)
    {
        while((mypid =waitpid(-1,NULL,WNOHANG)) > 0)
        {
            printf("exit pid id %d \n",mypid);
        }
    }

    if(num == SIGPIPE)
    {
        printf("sigpipe is happen \n");
    }

}
int main()
{
    int pipefd[2]={0};
    pipe(pipefd);
    signal(SIGCHLD,handler);
    signal(SIGPIPE,handler);
    pid_t pid = fork();
    if(pid <0)
    {
        perror("fork error \n");
    }
    if(pid == 0)
    {
        int i = 0;
        int ret = 0;
        int count = 0;
        int flags = 0;
        close(pipefd[0]);
        flags = fcntl(pipefd[1],F_GETFD);
        flags |= O_NONBLOCK;
        ret = fcntl(pipefd[1],F_SETFD,flags);
        if(ret == -1)
        {
            printf("fcntl error \n");
        }
        while(1)
        {
            ret =write(pipefd[1],"l",1);
            if(ret == -1)
            {
                printf("count is %d \n",count);
                break;
            }
            count++;
        }
        printf("cout    is   %d \n",count);
        close(pipefd[1]);
        exit(0);
    }else{
        close(pipefd[0]);
        sleep(1);
    }
    printf("father is break \n");
    return 0;
}

二.如果所有管道写端对应的文件描述符被关闭,则read返回0.
三.当管道不停的被写,写满的时候
 O_NONBLOCK disable: write调用阻塞
 O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

以测试管道容量为例:
(F_GETFL 开始写成 F_GETFLD 结果是write一直阻塞状态 无法退出哎)

void handler(int num)
{
    int mypid;
    if(num == SIGCHLD)
    {
        while((mypid =waitpid(-1,NULL,WNOHANG)) > 0)
        {
            printf("exit pid id %d \n",mypid);
        }
    }

    if(num == SIGPIPE)
    {
        printf("sigpipe is happen \n");
    }

}
int main()
{
    int pipefd[2]={0};
    int rett;
    rett = pipe(pipefd);
    if(rett == -1)
    {
        perror("error......\n");
    }
    signal(SIGCHLD,handler);
    signal(SIGPIPE,handler);
    pid_t pid = fork();
    if(pid <0)
    {
        perror("fork error \n");
    }
    if(pid == 0)
    {
        int i = 0;
        int ret = 0;
        int count = 0;
        close(pipefd[0]);
        int flags = fcntl(pipefd[1], F_GETFL);
        flags = flags | O_NONBLOCK;
        ret = fcntl(pipefd[1], F_SETFL, flags);
        if (ret == -1)
        {
            printf("fcntl err.\n");
            exit(0);
        }
        while(1)
        {
            ret =write(pipefd[1],"l",1);
            if(ret == -1)
            {
                perror("begin break \n");
                break;

            }
            count++;
        }
        printf("cout    is   %d \n",count);
        close(pipefd[1]);
        exit(0);
    }else if(pid >0){
        sleep(3);
        close(pipefd[0]);
        close(pipefd[1]);
    }
    printf("father is break \n");
    return 0;
}

5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#define num_k  68*1024
int main()
{

    char a[num_k];
    char b[num_k];
    int fd[2] = {0};
    memset(a,'A',sizeof(a));
    memset(b,'B',sizeof(b));
    int ret = pipe(fd);
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork error.....\n");
        return -1;
    }
    if(pid == 0)
    {
        close(fd[0]);
        ret = write(fd[1],a,sizeof(a));
        printf("son1 pid is %d,ret:%d \n",getpid(),ret);
        exit(0);
    }
    pid = fork();
    if(pid == 0)
    {
        close(fd[0]);
        ret = write(fd[1],b,sizeof(b));
        printf("son2 pid is %d,ret:%d \n",getpid(),ret);
        exit(0);
    }
    close(fd[1]);
    sleep(2);
    char buf_read[1024*4] = {0};
    while(1)
    {
        int read_ret = 0;
        ret = read(fd[0],buf_read,sizeof(buf_read));
        if(ret == 0)
        return -1;
        printf("pid id %d,read buf[4095]%c \n",getpid(),buf_read[4095]);
    }

执行结果如下:

pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
son1 pid is 50649,ret:69632 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]A 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 
son2 pid is 50650,ret:69632 
pid id 50648,read buf[4095]B 
pid id 50648,read buf[4095]B 

AB交叉了。write不再是一次性全部写入。管道容量可以测试:65536(我的)
6.最后 深入理解下管道

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
int main(void )
{
    int pipefd[2];
    pid_t pid;
    if (pipe(pipefd) == -1 )    
    {
        printf("pipe() err..\n");   
        return -1;
    }
    pid = fork();
    if (pid == -1)
    {
        printf("fork err..\n");
        return -1;
    }
    if (pid == 0)
    {
        close(pipefd[0]);
        //复制文件描述符pipefd[1],给标准输出,言外之意:execlp的ls命令输出到管道中
        dup2(pipefd[1], STDOUT_FILENO);
        close(pipefd[1]);

        execlp("ls", "ls", NULL);
        //如果替换新的进程印象失败,则会执行下面一句话    
        sprintf(stderr, "execute the cmd ls err..\n");
        exit(0);    


    } 
    else if (pid > 0 )
    {
        int len = 0; 
        char buf[100] = {0};
        close(pipefd[1]);
        //复制文件描述符pipefd[0],给标准输入,言外之意:execlp的wc命令从管道中读
        dup2(pipefd[0], STDIN_FILENO);
        close(pipefd[0]);
        //len = read(pipefd[0], buf, 100);
        execlp("wc", "wc", "-w", NULL);
        printf("len:%d, buf:%s \n", len , buf);

        //close(pipefd[0]);
    }

    wait(NULL);
    printf("parent ..quit\n");
    return 0;

}

用管道实现了 shell中管道ls | wc -l

猜你喜欢

转载自blog.csdn.net/dachunfree/article/details/77701027
今日推荐