Linux进程通信:无名管道

进程通信目的:

(1)数据传输:进程间数据传输;

(2)通知事件:一个进程向另一个或一组进程发送消息,通知某个事件的发生(如子进程终止时需通知父进程);

(3)资源共享:多个进程共享资源,需要内核提供同步互斥机制;

(4)进程控制:某进程需要控制另一个进程的执行(如Debug进程),此时控制进程需要拦截另一个进程的所有陷入、异常、状态等。

进行通信分类及方式:


无名管道

特点:(1)半双工。数据同一时刻只能单向传输;

           (2)数据从管道一端写入,另一端读出;

           (3)写入管道的数据遵循先进先出;

           (4)管道非普通文件,不属于某个文件系统,只存在于内存;

           (5)无名管道只能在具有公共祖先的进程(父子进程、兄弟进程等)之间使用


(1)pipe函数:创建无名管道

#include<unistd.h>

int pipe(int pipefd[2]);
/*
功能:
    创建无名管道。
参数:
    pipefd:int型数组的首地址,存放了管道文件描述符pipefd[0]、pipefd[1]。
            pipefd[0]用于读管道,pipefd[1]用于写管道。
            一般的文件I/O函数都可用来操作管道(lseek除外)。
返回值:
    成功:0
    失败:-1
*/

pipe示例:

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

int test() {
    int fds[2];  // fds[0]用于读管道,fds[1]用于写管道
    int ret = -1;

    // 创建一个无名管道
    ret = pipe(fds);
    if (-1 == ret) {
        perror("pipe");
        return 1;
    }

    printf("读管道的文件描述符:%d, 写管道的文件描述符:%d\n", fds[0], fds[1]);

    // 关闭文件描述符
    close(fds[0]);
    close(fds[1]);
    return 0;
}

运行结果:


(2)父子进程使用无名管道通信原理:

a)需要在fork之前创建无名管道,然后子进程也有自己的读写管道描述符关联无名管道;

b)父进程给子进程发消息:父进程写管道、子进程读管道;需要关闭父进程的读端文件描述符(fds[0])、子进程的写端文件描述符(fds[1])。 反之类似。

c)管道默认为阻塞,读不到内容则阻塞等待有内容可读;可设置为非阻塞。


(3)管道读写特性:

case 1:

        a)若写端打开,管道中无数据,读端进程会阻塞;

        b)若写端打开,管道中有数据,读端进程将数据读出,下次若无数据可读则阻塞;

case 2:

        若写端关闭,读端进程读取全部内容后,返回0;

case 3:

        若读端打开,管道被写满,则写端进程阻塞;

case 4:

        若读端关闭,写端进程收到一个信号,然后退出。

查看管道大小:


(4)父子进程使用无名管道通信示例:

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

#define SIZE 64

int main(int argc, const char* argv[]) {

    int ret = -1;
    int fds[2];
    pid_t pid = -1;
    char buf[SIZE];

    // 1. 创建无名管道
    ret = pipe(fds);
    if (-1 == ret) {
        perror("pipe");
        return 1;
    }

    // 2. 创建子进程。需要在创建无名管道之后
    pid = fork();
    if (-1 == pid) {
        perror("fork");
        return 1;
    }

    // 子进程 读管道
    if (0 == pid) {
        close(fds[1]);  // 关闭写端

        ret = read(fds[0], buf, SIZE); // 读管道
        if (ret < 0) {
            perror("read");
            exit(-1);
        }

        printf("子进程读到的内容:%s\n", buf);

        close(fds[0]);  // 关闭读端
        exit(0); // 子进程退出
    }

    // 父进程 写管道
    close(fds[0]); // 关闭读端

    ret = write(fds[1], "ABCDEFG", 7);  // 写管道
    if (-1 == ret) {
        perror("write");
        return 1;
    }
    printf("父进程写了%d字节.\n", ret);
    close(fds[1]);  // 关闭写端

    return 0;
}

运行结果:


(5)fpathconf函数:查看管道缓冲区

#include<unistd.h>

long fpathconf(int fd, int name);
/*
功能:
    通过name查看管道缓冲区的不同属性
参数:
    fd:读端或写端文件描述符
    name:
        _PC_PIPE_BUF:查看管道缓冲区大小
        _PC_NAME_MAX:文件名字节数上限
返回值:
    成功:属性值
    失败:-1
*/

fpathconf示例:

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

int main(int argc, const char* argv[]) {

    int fds[2];
    int ret = -1;

    ret = pipe(fds);
    if (-1 == ret) {
        perror("pipe");
        return 1;
    }

    printf("读端缓冲区大小:%ld,\n写端缓冲区大小:%ld,\n读端文件名字节数上限:%ld,\n写端文件名字节数上限:%ld\n",
        fpathconf(fds[0], _PC_PIPE_BUF), fpathconf(fds[1], _PC_PIPE_BUF),
        fpathconf(fds[0], _PC_NAME_MAX), fpathconf(fds[1], _PC_NAME_MAX));

    return 0;
}

运行结果:


(6)管道读端缓冲区设置为非阻塞的方法:

// 获取读端缓冲区原先的状态标记flags 
int flags = fcntl(fd[0], F_GETFL); 

// 设置新状态标记flags加入非阻塞状态
flags |= O_NONBLOCK;

// 给读端缓冲区设置新状态标记
fcntl(fd[0], F_SETFL, flags);

读端设置为非阻塞,若无数据,读进程直接返回-1.

读端以非阻塞的方式读管道示例:

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

#define SIZE 64

int main(int argc, const char* argv[]) {

    int ret = -1;
    int fds[2];
    pid_t pid = -1;
    char buf[SIZE];

    // 1. 创建无名管道
    ret = pipe(fds);
    if (-1 == ret) {
        perror("pipe");
        return 1;
    }

    // 2. 创建子进程。需要在创建无名管道之后
    pid = fork();
    if (-1 == pid) {
        perror("fork");
        return 1;
    }

    // 子进程 读管道
    if (0 == pid) {
        close(fds[1]);  // 关闭写端

        /*设置读端非阻塞*/
        ret = fcntl(fds[0], F_GETFL);  // 获取读端缓冲区状态
        ret |= O_NONBLOCK; //将读端缓冲区加入非阻塞状态
        fcntl(fds[0], F_SETFL, ret); // 将新状态设置进入

        ret = read(fds[0], buf, SIZE); // 读管道
        if (ret < 0) {
            perror("read");
            exit(-1);
        }

        printf("子进程读到的内容:%s\n", buf);

        close(fds[0]);  // 关闭读端
        exit(0); // 子进程退出
    }

    // 父进程 写管道
    sleep(1);
    close(fds[0]); // 关闭读端

    ret = write(fds[1], "ABCDEFG", 7);  // 写管道
    if (-1 == ret) {
        perror("write");
        return 1;
    }
    printf("父进程写了%d字节.\n", ret);
    close(fds[1]);  // 关闭写端

    return 0;
}

运行结果:

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的马师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值