【Linux】进程通信----管道通信(下)

简介: 【Linux】进程通信----管道通信(下)

【Linux】进程通信----管道通信(上)    https://developer.aliyun.com/article/1565747



3.管道特征

1.管道的生命周期随进程,进程退出,管道释放

2.管道可以用来进行具有血缘关系的进程间通信(常用于父子通信)

3.管道是面向字节流的

4.半双工—单向通信(特殊)

5.互斥与同步机制——对共享资源进行保护的方案


💫 命名管道

概念:

我们前面已经知道:匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。那如果两个毫不相干的进程间通信交互呢?如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件


1.mkfifo

使用说明

NAME
       mkfifo - make FIFOs (named pipes)
 
SYNOPSIS
    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char *pathname, mode_t mode);
RETURN VALUE
       On success mkfifo() returns 0.  In the case of an error, -1 is returned (in which case, errno is set appropriately).


在当前路径下直接创建命名管道:

mkfifo named_pipe

往管道文件写东西:



2.创建管道文件

分为三个文件:comm.hpp:公共文件(同一份资源),server.cc:读取端,clinet.cc:写入端

comm.hpp文件(同一份资源)

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cerrno>
#include <cstring>
#include <cassert>
 
#define NAMED_PIPE "/tmp/mypipe.name"
 
bool createFifo(const std::string &path)
{
    umask(0);
    int n = mkfifo(path.c_str(),0666);
    if(n==0) return true;
    else
    {
        std::cout<<"errno:"<<errno<<"err string:"<<strerror(errno)<<std::endl;
        return false;
    }
}


server.cc:

#include "comm.hpp"
int main()
{
    bool ret = createFifo(NAMED_PIPE);
    assert(ret);
    (void)ret;
    return 0;
}

运行:



3.删除管道文件

使用说明

unlink   注意头文件,函数的参数以及返回值这三个主要部分:

NAME
       unlink - remove a directory entry
 
SYNOPSIS
       #include <unistd.h>
 
       int unlink(const char *path);
RETURN VALUE
       Upon successful completion, 0 shall be returned. Otherwise, -1 shall be returned and errno set to indicate the error. If -1 is returned, the named file shall not be changed.
 


在comm.hpp中封装好删除的函数:

void removeFifo(const std::string &path)
{
    int n = unlink(path.c_str());
    assert(n==0);
    (void)n;//防止n没使用而警告
}


在server.cc中进行调用:

#include "comm.hpp"
 
int main()
{
    bool ret = createFifo(NAMED_PIPE);
    assert(ret);
    (void)ret;
 
    removeFifo(NAMED_PIPE);
    return 0;
}


至此,创建和删除管道文件的操作我们实现完毕。下面进入通信环节

4.通信

说明:

其实在了解完了匿名管道之后,对于命名管道我们能够更好的理解:

client.cc(写端):

#include "comm.hpp"
 
int main()
{
    int wfd = open(NAMED_PIPE,O_WRONLY);
    if(wfd<0) exit(1);
    //write
    char buffer[1024];
    while(true)
    {
        std::cout<<"Please Say:";
        fgets(buffer,sizeof(buffer),stdin);
        //if(strlen(buffer)>0) buffer[strlen(buffer)-1] = 0;
        ssize_t n = write(wfd,buffer,strlen(buffer));
        assert(n==strlen(buffer));
        (void)n;
    }
    close(wfd);
    return 0;
}


server.cc(读端):

#include "comm.hpp"
 
int main()
{
    bool ret = createFifo(NAMED_PIPE);
    assert(ret);
    (void)ret;
 
    int  rfd = open(NAMED_PIPE,O_RDONLY);
    if(rfd<0) exit(1);
 
    //read
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(rfd,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s] = 0;
            std::cout<<"client->server" <<buffer<<std::endl;
        }
        else if(s==0)
        {
            std::cout<<"client quit,俺也一样"<<std::endl;
            break;
        }
        else
        {
            std::cout<<"err string:"<<strerror(errno)<<std::endl;
            break;
        }
    }
    close(rfd);
    removeFifo(NAMED_PIPE);
    return 0;
}


进行通信:



读端多出一行空行:写端输入之后多按了回车,修改为buffer[strlen(buffer)-1] = 0;

if(strlen(buffer) > 0) buffer[strlen(buffer) - 1] = 0;



🌙 管道通信的优化

管道通信的优点有以下几点:

  • 管道通信是简单易用的,只需要使用系统调用 pipe 或 mkfifo 就可以创建一个管道文件,然后使用文件操作函数来读写数据。
  • 管道通信是安全的,匿名管道只能用于具有亲缘关系的进程间通信,命名管道可以通过文件权限来控制访问。
  • 管道通信是面向字节流的,不需要事先约定数据的格式,也不需要考虑字节序的问题。


管道通信的缺点有以下几点:


  • 管道通信是单向的,如果要实现双向通信,需要创建两个管道。
  • 管道通信是阻塞式的,如果读端没有数据可读或者写端没有空间可写,进程会被阻塞。
  • 管道通信是缓冲区有限的,如果写入数据过多而读出数据过少,会导致缓冲区满而无法继续写入。
  • 管道通信是不可靠的,如果读端或者写端被关闭,另一端可能会收到错误的信号或者返回值。


改进管道通信性能和效率的方法有以下几点:


  • 使用双向管道,可以实现两个进程之间的双向通信,而不需要创建两个单向管道。双向管道可以通过 socketpair 系统调用来创建,返回两个文件描述符,分别表示管道的两端。
  • 使用非阻塞模式,可以避免进程在读写管道时被阻塞,提高并发性能。非阻塞模式可以通过 fcntl 系统调用来设置文件描述符的 O_NONBLOCK 标志。
  • 使用自定义协议,可以根据通信的需求和场景,设计合适的数据格式和交互方式,提高数据传输的效率和可靠性。自定义协议可以包括数据包的长度、类型、校验码等信息。
  • 调整管道缓冲区的大小,可以根据数据量的大小和频率,选择合适的缓冲区大小,避免缓冲区溢出或者空闲浪费。管道缓冲区的大小可以通过 fcntl 系统调用来设置 F_SETPIPE_SZ 标志,并且可以通过 /proc/sys/fs/pipe-max-size 来修改最大容量。


🌟结束语 

      今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。


目录
相关文章
|
2天前
|
存储 Unix Linux
Linux I/O 重定向与管道
【8月更文挑战第17天】重定向在Linux中改变命令I/O流向,默认有&quot;&gt;&quot;覆盖输出至文件及&quot;&gt;&gt;&quot;追加输出至文件末尾,便于保存结果;使用&quot;&lt;&quot;从文件读取输入而非键盘,高效处理数据。文件描述符如0(stdin)、1(stdout)、2(stderr)标识I/O资源,支持读写操作。管道以&quot;|&quot;连接命令,使前一命令输出成为后一命令输入,如排序用户或找出CPU占用最高的进程,构建复杂数据处理流程。
16 9
|
4天前
|
Linux 调度
Linux源码阅读笔记05-进程优先级与调度策略-实战分析
Linux源码阅读笔记05-进程优先级与调度策略-实战分析
|
5天前
|
存储 Unix Linux
Linux I/O 重定向与管道
【8月更文挑战第14天】输出重定向可将命令结果存入文件,如`&gt;`覆盖写入或`&gt;&gt;`追加写入。输入重定向从文件读取数据,如`&lt;`代替键盘输入。这些操作利用文件描述符(如0:stdin, 1:stdout, 2:stderr)管理I/O。管道`|`连接命令,使前一命令输出作为后一命令输入,便于数据处理,如排序用户`sort -t: -k3 -n /etc/passwd | head -3`或查找CPU占用高的进程`ps aux --sort=-%cpu | head -6`。
17 4
|
4天前
|
Linux API C语言
Linux源码阅读笔记02-进程原理及系统调用
Linux源码阅读笔记02-进程原理及系统调用
|
7天前
|
Linux Shell 调度
【在Linux世界中追寻伟大的One Piece】Linux进程概念
【在Linux世界中追寻伟大的One Piece】Linux进程概念
15 1
|
5天前
|
Linux
Linux 查找进程所在目录
Linux 查找进程所在目录
15 0
|
7天前
|
缓存 Linux Shell
【在Linux世界中追寻伟大的One Piece】Linux进程控制
【在Linux世界中追寻伟大的One Piece】Linux进程控制
13 0
|
15天前
|
监控 Linux Shell
探索Linux操作系统下的进程管理
【8月更文挑战第4天】本文深入探讨了在Linux操作系统下进行进程管理的方法与技巧,通过实例分析展示了如何利用系统命令和脚本来监控、控制进程。文中不仅介绍了基础的进程查看、启动、终止操作,还详细解释了如何通过信号机制处理进程间的通信,以及如何编写自动化脚本以优化日常管理任务。文章旨在为系统管理员和开发人员提供实用的进程管理知识,帮助他们更高效地维护Linux系统。
|
1天前
|
安全 Linux 开发者
Linux笔记之ldd命令详解
`ldd`命令是Linux环境下一个非常实用的工具,用于显示一个程序运行时所需的共享库依赖。它帮助开发者和系统管理员快速诊断程序运行问题,特别是在处理"找不到库文件"或者"错误的库文件版本"等错误时。然而,出于安全的考虑,对于不信任的可执行文件,应该慎用 `ldd`命令,可以考虑使用其他工具如 `objdump`。总的来说,懂得如何妥善且安全地使用 `ldd`,对于维护一个稳定和高效的Linux系统来说,是非常重要的。
15 9