《UNIX环境高级编程》笔记 第十五章-进程间通信

这篇博客深入探讨了UNIX环境中的进程间通信(IPC)机制,包括匿名管道、命名管道(FIFO)、XSI IPC(消息队列、信号量和共享存储)的详细使用和注意事项。文章通过代码示例和概念解释,阐述了各种通信方式的创建、操作和使用场景,特别强调了不同通信方式的优缺点,以及如何在实际编程中合理选择和使用这些通信方式。
摘要由CSDN通过智能技术生成

1. 匿名管道

匿名管道是UNIX系统IPC(进程间通信)最古老的方式,所有UNIX系统都提供这种通信机制

匿名管道有以下局限性:

  • 历史上,它们是半双工的(数据只能在一个方向上流动)。现在某些操作系统提供全双工匿名管道,但是为了可移植性,应该按照半双工来进行编程
  • 管道只能在具有公共祖先的两个进程间使用。通常一个管道由一个进程创建,在进程fork之后,父子进程之间通过该管道进行通信。

命名管道(FIFO)没有第二种限制,UNIX域套接字没有这两种限制

1.1 pipe函数

通过pipe函数创建管道

int pipe(int pipefd[2]);

函数成功返回后,pipefd中保存两个文件描述符

  • pipefd[0]:读打开
  • pipefd[1]:写打开

即pipefd[1]的输出是pipefd[0]的输入

对于全双工实现的匿名管道,这两个文件描述符都是读/写打开的。

对于匿名管道,通过fstat函数应用于其读端(pipefd[0])时,获得的stat文件属性中的st_size字段是无意义的。

下图中左图显示管道的两端在一个进程中相互连接;右图则强调数据需要通过内核在管道中流动

在这里插入图片描述

1.2 匿名管道常用操作

通常,进程先调用pipe,接着调用fork,从而创建父进程与子进程间的IPC通道。

对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程关闭写端(fd[1])。

对于从子进程到父进程的管道,父进程关闭管道的写端(fd[1]),子进程关闭读端(fd[0])。

在这里插入图片描述

1.2.1 当管道的一端被关闭后,遵守下列规则:
  • 当read一个写端已被关闭的管道时,在所有数据都被读取后,read返回0,表示文件结束

    如果管道的写端还在,就并不会产生文件的结束(如果管道没数据,则read阻塞)

  • 如果write一个读端已被关闭的管道,产生信号SIGPIPE。该信号默认操作是终止进程,如果忽略该信号或捕获到该信号且捕获函数返回,则write返回-1,errno置EPIPE。

1.2.2 内核的管道缓冲区大小:

在写匿名管道(或FIFO)时,常量PIPE_BUF规定了内核的管道缓冲区大小。如果对管道调用write且写入字节数小于PIPE_BUF,那么此操作不会与其他进程对同一管道(或FIFO)的write操作交叉。但是如果同时有多个进程写该管道(或FIFO)且我们write的数据量大于PIPE_BUF,那么我们所写的数据可能会与其他进程所写的数据相互交叉。

    int p[2];
    pipe(p);
    cout << fpathconf(p[0],_PC_PIPE_BUF) << endl;
	//打印4096,说明PIPE_BUF管道缓冲区大小是4096字节。
1.2.3 示例

示例:父进程创建管道并fork子进程,指定父进程为管道写入端、子进程为读取端,父子进程间单向通信

main.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(){
   
    int pd[2];
    int r = pipe(pd);//创建管道
    printf("pipe: read: %d write: %d\n",pd[0],pd[1]);
    int pid = fork();//fork子进程
    if(pid){
   
        //父进程执行此部分
        close(pd[0]);   //设置父进程为管道的写入端,因此关闭读取端文件描述符pd[0]
        char buf[] = "this is some datas";
        sleep(3);   //父进程sleep三秒
        printf("parent Process begin write datas\n");
        write(pd[1],buf,strlen(buf));   //向管道写入数据
        printf("parent Process write datas done\n");
    }
    else{
   
        //子进程执行此部分
        close(pd[1]);   //设置子进程为管道的读取端,因此关闭写入端文件描述符pd[1]
        char buf[30];
        int num = read(pd[0],buf,sizeof(buf)-1);    //从管道读取数据
        buf[num] = '\0';	//字符串结尾
        printf("son Process get %d datas : %s\n",num,buf);
    }
}

运行结果

xtark@xtark-vmpc:~/桌面/linux_study/section3/pip test$ gcc pipe_test.c 
xtark@xtark-vmpc:~/桌面/linux_study/section3/pip test$ ./a.out 
pipe: read: 3 write: 4
parent Process begin write datas
parent Process write datas done
son Process get 18 datas : this is some datas

可见父进程成功将数据写入管道,子进程成功从管道读出数据。由于父进程在写入前sleep了三秒,可以证实管道读取端要读数据时,如果管道为空且存在写进程时会阻塞等待写入端写入数据后再读取。

1.2.4 补充:分页显示程序

常用的分页显示程序:more和less

如果想将输出结果按照一页一页的形式呈现出来,就可以使用管道实现

fork一个子进程,然后让子进程的标准输入成为管道读端,然后exec分页程序。父进程向管道写端写输出内容即可。

示例:将文件内容通过分页程序显示出来

int main(int argc, char *argv[])
{
   
    int fd[2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值