进程间通信--管道

pipe管道

实现原理: 内核借助环形队列机制,使用内核缓冲区实现。

特点: 1. 伪文件;2. 管道中的数据只能一次读取;3. 数据在管道中,只能单向流动。

局限性:1. 自己写,不能自己读;2. 数据不可以反复读;3. 半双工通信;4. 血缘关系进程间可用。
管道的读写行为:

读管道:1. 管道有数据,read返回实际读到的字节数。
2. 管道无数据: 1)无写端,read返回0 (类似读到文件尾);2)有写端,read阻塞等待。
写管道:1. 无读端, 异常终止。 (SIGPIPE导致的)。
3. 有读端: 1) 管道已满, 阻塞等待;2) 管道未满, 返回写出的字节个数。

管道的优劣:
优点:简单,相比信号,套接字实现进程通信,简单很多。
缺点:1.只能单向通信,双向通信需建立两个管道;2.只能用于有血缘关系的进程间通信。该问题后来使用fifo命名管道解决。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char const *argv[])
{
    int fd[2] = { 0 };
    pipe(fd);
    pid_t pid = fork();
    if (pid == 0)
    {
        sleep(3);
        close(fd[0]);       //关闭读端
        write(fd[1], "hello", 5);
        close(fd[1]);       //关闭写端
        while (1)
        {
            sleep(1);
        }
    }
    else if(pid > 0)
    {
        close(fd[1]);       //关闭写端
        close(fd[0]);

        int status = 0;
        waitpid(pid, &status, 0);
        if(WIFSIGNALED(status)) 
        {
            printf("killed by %d\n", WTERMSIG(status));
        }
        char buf[1024] = { 0 };
        while (1)
        {
            int ret = read(fd[0], buf, sizeof(buf));
            if (ret == 0)
            {
                printf("read over!\n");
                break;
            }
            else if (ret > 0)
            {
                write(STDOUT_FILENO, buf, ret);
            }
        }
    }

    return 0;
}

利用管道实现ps -ef | grep bash命令:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    int fd[2] = { 0 };
    pipe(fd);

    pid_t p1, p2, ret1, ret2;
    p1 = fork();
    if (p1 < 0)
    {
        printf("fork child 1 error!\n");
        return -1;
    }
    else if (p1 == 0)
    {
        //child 1
        //关闭读端
        close(fd[0]);
        //标准输出重定向到写端
        dup2(fd[1], STDOUT_FILENO);
        //执行ps -ef命令
        execlp("ps", "ps", "-ef", NULL);
    }
    else
    {
        //parent
        p2 = fork();
        if (p2 < 0)
        {
            printf("fork child2 error!\n");
            return -1;
        }
        else if (p2 == 0)
        {
            //child 2
            //关闭写端
            close(fd[1]);
            //标准输入重定向写端
            dup2(fd[0], STDIN_FILENO);
            //执行grep bash命令
            execlp("grep", "grep", "bash", NULL);
        }
        else
        {
            //parent
            close(fd[0]);
            close(fd[1]);
            //回收子进程
            int status = 0;
            do
            {
                ret1 = waitpid(p1, &status, 0);
                ret2 = waitpid(p2, &status, 0);
            } while (ret1 != p1 || ret2 != p2);
            printf("p1 = %d, ret1 = %d, p2 = %d, ret2 = %d\n", p1, ret1, p2, ret2);

            printf("child all die!\n");
        }
    }

    return 0;
}

利用管道实现ls -l | wc -l命令:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main(int argc, char const *argv[])
{
    pid_t pid;
    int status = 0;
    int i = 0;

    //建立管道,在fork之前,父子进程可以共享文件描述符
    int fd[2];
    int ret = pipe(fd);
    if (ret == -1)
    {
        cout << "pipe error!" << endl;
        exit(1);
    }

    for (; i < 2; ++i)
    {
        pid = fork();
        if (pid < 0)
        {
            cout << "fork error" << endl;
            exit(1);
        }
        else if (pid == 0)
        {
            break;
        }
    }

    if (i == 0)
    {
        //子进程1写ls -l命令
        close(fd[0]);                   //关闭管道的读端
        dup2(fd[1], STDOUT_FILENO);     //重定向stdout
        execlp("ls", "ls", "-l", nullptr);
        cout << "execlp error!" << endl;
        exit(1);
    }
    else if (i == 1)
    {
        //子进程2读ls -l命令,写wc -l
        close(fd[1]);                   //关闭管道的写端
        dup2(fd[0], STDIN_FILENO);      //重定向stdin
        execlp("wc", "wc", "-l", nullptr);
        cout << "execlp error!" << endl;
        exit(1);
    }
    else if (i == 2)
    {
        //关闭管道读写端
        close(fd[0]);
        close(fd[1]);
        //父进程回收子进程
        wait(nullptr);
        wait(nullptr);
    }

    return 0;
}

一个读端多个写端:

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

using namespace std;

int main(int argc, char const *argv[])
{
    pid_t pid;
    int status = 0;
    int i = 0;

    //建立管道
    int fd[2];
    int ret = pipe(fd);
    if (ret == -1)
    {
        cout << "pipe error!" << endl;
        exit(1);
    }

    for (; i < 2; ++i)
    {
        pid = fork();
        if (pid < 0)
        {
            cout << "fork error" << endl;
            exit(1);
        }
        else if (pid == 0)
        {
            break;
        }
    }
    
    if (i == 0)
    {
        //子进程1写
        close(fd[0]);                   //关闭管道的读端
        write(fd[1], "1.hello\n", strlen("1.hello\n"));
        close(fd[1]);
    }
    else if (i == 1)
    {
        //子进程2写
        close(fd[0]);                   //关闭管道的读端
        write(fd[1], "2.HEELO", strlen("2.HEELO"));
        close(fd[1]);
    }
    else if (i == 2)
    {
        //关闭管道写端
        close(fd[1]);
        //父进程回收子进程
        wait(nullptr);
        wait(nullptr);
        char buf[1024];
        read(fd[0], buf, sizeof(buf));
        write(STDOUT_FILENO, buf, strlen(buf));
        close(fd[0]);
    }

    return 0;
}

fifo管道

实现无血缘关系的进程间通信。fifo操作起来像文件。

1.创建fifo

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
    int ret = mkfifo(argv[1], 0644);
    if (ret == -1)
    {
       perror("mkfifo error!");
        exit(1); 
    }
    
    return 0;
}

2.一个或多个进程写fifo

//file write

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("./a.out fifoname\n");
        return -1;
    }
    //fifo文件
    //打开fifo文件
    printf("begin write...\n");
    int fd = open(argv[1], O_WRONLY);
    printf("end write...\n");
    //写
    char buf[256] = { 0 };
    int num = 1;
    while (1)
    {
        sprintf(buf, "N%04d", num++);
        write(fd, buf, strlen(buf));
        sleep(1);
    }
    //关闭描述符
    close(fd);
    return 0;
}

3.一个进程读fifo

//fifo read

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

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("./a.out fifoname\n");
        return -1;
    }

    printf("begin read...\n");
    int fd = open(argv[1], O_RDONLY);
    printf("end read...\n");

    char buf[256] = { 0 };
    int num = 1;
    int ret = -1;
    while(1)
    {
        ret = read(fd, buf, sizeof(buf));
        if (ret > 0)
        {
            printf("read: %s\n", buf);
        }
        // sleep(1);
    }

    close(fd);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值