首页 > 系统相关 >【Linux探索学习】第二十六弹——进程通信:深入理解Linux中的进程通信

【Linux探索学习】第二十六弹——进程通信:深入理解Linux中的进程通信

时间:2025-01-18 19:29:44浏览次数:3  
标签:include 通信 msg 管道 Linux 进程 共享内存

Linux探索学习:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在Linux操作系统中,进程通信(IPC)是操作系统的一项核心功能,用于在不同进程之间交换数据或信号。这种能力在多任务操作系统中尤为重要,因为进程之间通常需要协作完成复杂的任务。本篇文章将详细介绍Linux中的进程通信机制,特别是管道通信,并结合代码示例和表格总结,帮助您全面理解这些技术。

目录

为什么需要进程通信?

Linux中的进程通信机制概览

1. 管道(Pipe)通信(匿名管道)

原理

特性

系统调用

示例代码:管道的基本用法

预期结果

​编辑

管道的局限性

双向通信

2. FIFO(命名管道)

创建命名管道

示例代码:命名管道

运行步骤

预期结果

特点

3. 消息队列

示例代码

运行结果

消息队列的优点和缺点

4. 共享内存

特性

示例代码

运行结果

优缺点

总结


为什么需要进程通信?

在Linux中,每个进程都有独立的地址空间,这种隔离性保障了系统的稳定性和安全性,但也使得进程间直接访问彼此的内存成为不可能。因此,为了在不同进程之间交换数据,操作系统提供了多种IPC机制。

常见的进程通信使用场景包括:

1. 数据传输:一个进程需要将它的数据发送给另一个进程
2. 资源共享:多个进程之间共享同样的资源。
3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另个进程的所有陷入和异常,并能够及时知道它的状态改变。


Linux中的进程通信机制概览

Linux 提供了多种进程通信方式,适用于不同场景。以下是常见的几种机制:

通信方式描述优点缺点
管道(Pipe)单向或双向数据流,父子进程间通信简单、效率高只能在亲缘进程之间通信,不适合大数据量传输
FIFO(命名管道)像管道一样,但可用于无亲缘关系的进程间通信灵活性更高性能较低
消息队列基于消息的异步通信方式可传递结构化消息管理复杂,性能受限
共享内存不同进程共享内存区域进行通信高效,适合大数据量传输同步机制复杂,需额外处理同步问题
信号量用于进程同步和资源管理简单、轻量级不适合复杂的通信场景
套接字(Socket)网络通信和本地进程通信的强大工具强大且灵活,支持多种协议实现较复杂,需学习成本

接下来,我们详细探讨这些通信方式,重点讲解管道和命名管道。(信号量和套接字的内容不作为重点)


1. 管道(Pipe)通信(匿名管道)

管道是Linux中最简单、最常用的进程通信方式之一。它提供了一个单向数据流,可以在父子进程之间传递数据。

原理

上面内容补充说明:

  1. 内存级文件:当我们进程打开一个文件并向里面写入时,是要先写入缓冲区的,然后系统再刷新进入磁盘中的,但是文件其实并不是一定要存在在磁盘中的,也可以直接存在内存中的,内存级文件与普通文件的区别就是不会刷新到显示屏中,其它基本一致
  2. 子进程在创建时会对父进程进行拷贝,files struct也会拷贝,但是拷贝方式类似于指针拷贝的浅拷贝,只会拷贝结构体,并不会把父进程打开的文件拷贝一份一样的,所以子进程的文件描述符与父进程一样,与父进程用的相同的文件(比如:当我们创建一个父子进程时,在不同的窗口打开它们,让这两个进程同时向显示屏上刷新内容时,都会刷新在父进程的显示屏上)
  3. 实现进程通信的前提就是让不同的进程看到同一份“资源”,所以我们就可以让父进程和子进程通过这样的内存级文件建立联系
  4. 管道的本质就是内存级文件
特性
  1. 单向性:标准管道是单向的,即数据只能沿一个方向流动。

  2. 亲缘关系:标准管道只能用于具有亲缘关系的进程间通信。

  3. 内核缓冲区:管道依赖于内核缓冲区,数据写入后,只有在被读取时才会释放缓冲区。

系统调用

Linux 提供以下系统调用与管道相关:

  • pipe(): 创建一个管道,返回两个文件描述符:fd[0](用于读)和 fd[1](用于写)。

示例代码:管道的基本用法

以下代码展示了父子进程通过管道进行通信:

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

int main() {
    int pipefd[2];
    pid_t pid;
    char buffer[128];

    // 创建管道
    if (pipe(pipefd) == -1) {
        perror("pipe failed");
        return 1;
    }

    pid = fork(); // 创建子进程
    if (pid < 0) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) { // 子进程
        close(pipefd[1]); // 关闭写端
        read(pipefd[0], buffer, sizeof(buffer)); // 读取数据
        printf("Child process received: %s\n", buffer);
        close(pipefd[0]); // 关闭读端
    } else { // 父进程
        close(pipefd[0]); // 关闭读端
        const char *msg = "Hello from parent!";
        write(pipefd[1], msg, strlen(msg) + 1); // 写入数据
        close(pipefd[1]); // 关闭写端
        wait(NULL); // 等待子进程结束
    }

    return 0;
}
预期结果
Child process received: Hello from parent!
管道的局限性
  1. 单向数据流:默认情况下,管道只支持单向通信。

  2. 仅限亲缘进程:标准管道仅适用于父子进程之间。

  3. 容量限制:管道的内核缓冲区有限,写入数据量过大会阻塞。(不同的操作系统内核下内存级文件的大小是不同的,默认规定的是4kb,但比如centos7.0下就是16kb)

双向通信

通过创建两个管道,可以实现双向通信(不常用)。例如:

int pipe1[2], pipe2[2];
pipe(pipe1);
pipe(pipe2);
// 使用 pipe1 进行父->子通信,使用 pipe2 进行子->父通信。

2. FIFO(命名管道)

FIFO(命名管道)克服了标准管道只能在亲缘进程间通信的限制。它是文件系统中的一种特殊文件,允许无亲缘关系的进程间通信。

创建命名管道

命名管道可以使用以下方法创建:

  1. 命令行工具:使用 mkfifo 命令。

  2. 系统调用:使用 mkfifo() 函数。具体方式可以通过man手册来查看

    man mkfifo
示例代码:命名管道

以下代码演示了两个独立进程通过命名管道通信:

写入进程:writer.c

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

int main() {
    const char *fifo = "/tmp/my_fifo";
    mkfifo(fifo, 0666); // 创建命名管道

    int fd = open(fifo, O_WRONLY); // 打开管道写端
    const char *msg = "Hello from writer!";
    write(fd, msg, strlen(msg) + 1); // 写入数据
    close(fd); // 关闭文件描述符

    return 0;
}

读取进程:reader.c

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

int main() {
    const char *fifo = "/tmp/my_fifo";
    char buffer[128];

    int fd = open(fifo, O_RDONLY); // 打开管道读端
    read(fd, buffer, sizeof(buffer)); // 读取数据
    printf("Reader process received: %s\n", buffer);
    close(fd); // 关闭文件描述符

    return 0;
}
运行步骤
  1. 启动 reader 进程:

    ./reader
  2. 启动 writer 进程:

    ./writer
预期结果
Reader process received: Hello from writer!
特点
  1. 支持无亲缘关系的进程通信。

  2. 可以通过文件路径访问。

  3. 数据读完即从管道中移除。


3. 消息队列

消息队列允许进程以消息的形式传递数据,并支持消息的优先级。

示例代码

发送端代码

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>

struct msg_buffer {
    long msg_type;
    char msg_text[100];
};

int main() {
    key_t key;
    int msgid;

    // 创建唯一的键值
    key = ftok("progfile", 65);

    // 创建消息队列
    msgid = msgget(key, 0666 | IPC_CREAT);

    struct msg_buffer message;
    message.msg_type = 1;

    strcpy(message.msg_text, "Hello, Message Queue!");

    // 发送消息
    msgsnd(msgid, &message, sizeof(message), 0);

    printf("Data sent: %s\n", message.msg_text);

    return 0;
}

接收端代码

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>

struct msg_buffer {
    long msg_type;
    char msg_text[100];
};

int main() {
    key_t key;
    int msgid;

    // 创建唯一的键值
    key = ftok("progfile", 65);

    // 获取消息队列
    msgid = msgget(key, 0666 | IPC_CREAT);

    struct msg_buffer message;

    // 接收消息
    msgrcv(msgid, &message, sizeof(message), 1, 0);

    printf("Data received: %s\n", message.msg_text);

    // 删除消息队列
    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}
运行结果

运行发送端程序后,再运行接收端程序,结果如下:

发送端输出:

Data sent: Hello, Message Queue!

接收端输出:

Data received: Hello, Message Queue!
消息队列的优点和缺点
优点缺点
支持优先级队列,消息可以按优先级读取需要显式创建和销毁队列,操作较复杂
数据结构化,适合传递小型消息消息大小受系统限制,传递大数据性能较差
进程间解耦,无需直接建立父子关系存在队列上限,可能导致阻塞或失败

4. 共享内存

共享内存是Linux中效率最高的进程通信方式,因为数据直接存储在内存中,无需拷贝。它非常适合用于传递大规模数据。

特性
  1. 高效:内存共享避免了数据拷贝,通信效率高。

  2. 需同步机制:由于共享内存区域可以同时被多个进程访问,需要使用信号量或其他同步机制防止数据冲突。

  3. 适合大数据量传输:特别适合需要频繁通信的场景。

示例代码

以下展示了共享内存的基本用法。

创建和写入共享内存的进程:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main() {
    // 创建唯一键值
    key_t key = ftok("shmfile", 65);

    // 创建共享内存段
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);

    // 将共享内存附加到进程地址空间
    char *str = (char *) shmat(shmid, (void *)0, 0);

    // 写入数据
    strcpy(str, "Hello, Shared Memory!");

    printf("Data written to shared memory: %s\n", str);

    // 分离共享内存
    shmdt(str);

    return 0;
}

读取共享内存的进程:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main() {
    // 创建唯一键值
    key_t key = ftok("shmfile", 65);

    // 获取共享内存段
    int shmid = shmget(key, 1024, 0666 | IPC_CREAT);

    // 将共享内存附加到进程地址空间
    char *str = (char *) shmat(shmid, (void *)0, 0);

    // 读取数据
    printf("Data read from shared memory: %s\n", str);

    // 分离共享内存
    shmdt(str);

    // 销毁共享内存
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}
运行结果

写入进程输出:

Data written to shared memory: Hello, Shared Memory!

读取进程输出:

Data read from shared memory: Hello, Shared Memory!
优缺点
优点缺点
高效,适合大数据传输需显式同步,复杂度较高
数据共享无需频繁拷贝进程需要协同管理内存
持续性强,共享内存在进程间保持有效易出现数据一致性问题

总结

Linux中的进程通信机制为开发者提供了多种灵活的工具,应根据应用场景选择合适的方式:

  1. 管道与命名管道:简单场景下的首选,适用于中小型数据流。

  2. 消息队列:适合需要传递结构化消息的异步场景。

  3. 共享内存:性能需求高或传输大数据时的最佳选择,但需同步机制配合。

通信机制数据传输方向数据持久性是否需同步优化场景
管道(Pipe)单向瞬时不需要父子进程通信,简单小型数据流
FIFO单向瞬时不需要无亲缘关系的进程间通信
消息队列单/多向瞬时不需要传递结构化消息,任务解耦
共享内存双向持久需要频繁通信或大数据传输

以上就是进程通信的主要内容,其中我们重点先要理解管道通信的原理,学习进程通信对我们后期学习网络部分内容有着很重要的铺垫作用。

本篇笔记(不完整,后期会更新):


感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

标签:include,通信,msg,管道,Linux,进程,共享内存
From: https://blog.csdn.net/2301_80220607/article/details/145124014

相关文章

  • 快速部署WSL(Windows Subsystem for Linux)
    概述WindowsSubsystemforLinux(WSL)是微软为Windows10及更高版本推出的一项功能,允许用户在Windows上运行Linux二进制可执行文件。WSL提供了一个完全兼容的Linux内核接口,使用户能够在不使用虚拟机或双启动的情况下运行Linux环境。本文将详细介绍如何快速部署WSL,包括安装、配置和......
  • Linux常用命令总结
    Linux常用命令指南文章目录Linux常用命令指南1.文件与目录操作命令(1)ls-列出目录内容(2)cd-改变当前工作目录(3)pwd-显示当前目录(4)mkdir-创建新目录(5)rmdir-删除空目录(6)rm-删除文件或目录(7)mv-移动或重命名文件(8)cp-复制文件或目录2......
  • linux实现macos的timeMachine系统备份
    在上一篇文章中,我们详细介绍了Btrfs文件系统的基本使用方法和核心原理。本文将重点讲解如何利用Btrfs的特性来实现系统备份功能。实现原理其实很简单:Linux内核支持直接从Btrfs的子卷(subvolume)启动系统。基于这个特性,我们可以通过计划任务定期为系统根目录创建快照,再配合btrfs-lin......
  • Nexpose 7.3.0 for Linux & Windows - 漏洞扫描
    Nexpose7.3.0forLinux&Windows-漏洞扫描Rapid7on-premVulnerabilityManagement,releasedJan15,2025请访问原文链接:https://sysin.org/blog/nexpose-7/查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgNexposeVulnerabilityScanner您的本地漏洞扫描......
  • 操作系统进程-概述、fork/vfork、exit/_exit、getpid/getppid、wait/waitpid与exec族
    进程基本概念什么是进程:进程是程序的一次执行过程,进程是程序执行和资源管理的最小单元。在Linux环境下,每个正在运行的程序都被称为进程并行与并发:并行(微观上,一个很短时间范围内):在很短的时间段内(时间点上),两个事情同时发生并发(宏观上,一个时间段内):在一段时间内,多个事情同......
  • 操作系统进程-进程间通信的概述、匿名管道pipe和有名管道mkfifo函数的介绍及应用
    进程间通信(IPC)概述进程间通信(InterProcessCommunication)是指在两个或多个不同的进程间传递或者交换信息。进程是一个独立的资源管理单元,不同的进程之间资源是独立的,不能在一个进程中直接访问另一个进程的资源,但是进程间不是孤立的,也需要一些信息的交互和状态传递,所以就......
  • 软件包管理器 ---【linux基础开发工具】
    文章目录一、软件包管理器1、什么是软件包2、Linux软件生态3、......
  • 首发 最新AWVS/Acunetix Premium V24.8高级版漏洞扫描器(最新版)Windows/Linux下载
    前言AcunetixPremium是一种Web应用程序安全解决方案,用于管理多个网站、Web应用程序和API的安全。集成功能允许您自动化DevOps和问题管理基础架构。AcunetixPremium:全面的Web应用程序安全解决方案Web应用程序对于企业和组织与客户、合作伙伴和员工的联系至关重要。......
  • Linux 常用命令——文件目录篇(保姆级说明)
    文件及目录类列出当前目录中的文件和子目录(ls)ls[-参数][name...]#列出所有根目录ls/#列出所有txt文件ls*.txt参数:-a显示所有文件及目录(.开头的隐藏文件也会列出)-d只列出目录(不递归列出目录内的文件)。-l以长格式显示文件和目录信息,包括权限、所有......
  • Linux性能调优:技术宅的魔法秘籍
    各位观众朋友们,大家好!今天,咱们来聊聊一个听起来就特别技术范儿,但实际上和我们每个人的生活都息息相关的话题——Linux系统性能调优。别急,我知道你们可能已经在心里默念:“这不就是那些技术宅才关心的事儿嘛,跟我有啥关系?”别走开,我保证,这事儿比你想象的有意思多了,而且说不定还......